1 00:00:00,930 --> 00:00:05,370 In this lecture, we will see how to jump from ring0 to ring3. 2 00:00:06,330 --> 00:00:13,470 As we have learned in the previous lectures, the CPL is stored in the lower 2 bits of cs and ss registers. 3 00:00:13,470 --> 00:00:16,630 We are running in ring 0 so far. 4 00:00:16,650 --> 00:00:19,740 If we check the lower 2 bits of cs register 5 00:00:19,740 --> 00:00:20,600 , 6 00:00:20,730 --> 00:00:24,780 we will see the value is 0 meaning that we are running in ring0. 7 00:00:25,890 --> 00:00:32,549 What we need to do is prepare the code segment descriptor for ring3 and load the descriptor to cs register. 8 00:00:32,549 --> 00:00:39,630 Once the descriptor is successfully loaded into cs register, we will run in ring3. 9 00:00:40,410 --> 00:00:45,330 In this process, the data segment descriptor for ss register is needed. 10 00:00:46,220 --> 00:00:50,750 This is the only case where we load the data segment descriptor ourselves. 11 00:00:52,470 --> 00:00:54,720 OK, let's add two more descriptors. 12 00:01:01,550 --> 00:01:07,680 the first one is code segment descriptor for ring3. The value is pretty much the same, 13 00:01:07,850 --> 00:01:09,260 so we just copy and paste. 14 00:01:13,900 --> 00:01:19,540 The difference is that the value here is F 15 00:01:21,540 --> 00:01:28,860 instead of 9. When we look at it in binary format, we can see the DPL is changed from 0 to 3 16 00:01:28,860 --> 00:01:29,480 . 17 00:01:29,910 --> 00:01:33,710 So the privilege level of the descriptor we added here is 3. 18 00:01:35,400 --> 00:01:38,180 The data segment descriptor is simple as well. 19 00:01:39,620 --> 00:01:46,340 The DPL field of the descriptor is also 3. The value here is F 20 00:01:52,160 --> 00:01:57,660 And this is the value we need to assign to the data segment descriptor. 21 00:01:58,280 --> 00:02:00,170 Let's look at the attributes. 22 00:02:01,510 --> 00:02:08,520 and present bit is set to 1. 10 means this descriptor is data segment descriptor 23 00:02:08,520 --> 00:02:09,220 . 24 00:02:10,500 --> 00:02:19,560 The w bit is 1 indicating that the data segment is writable. Ok, with the descriptors prepared, 25 00:02:19,560 --> 00:02:24,740 we use interrupt return to jump from ring0 to ring3. So let's do it. 26 00:02:29,810 --> 00:02:34,880 To return to the ring3, we have to prepare 5 8-byte data on the stack. 27 00:02:36,720 --> 00:02:43,290 As you see, the top of the stack is RIP value which specifies where we will return. 28 00:02:43,290 --> 00:02:49,830 The next data is the code segment selector we will load into cs register after we return. 29 00:02:49,830 --> 00:02:54,810 Then the value of rflags which contains the status of the CPU. 30 00:02:54,840 --> 00:03:01,680 When we return, the value will be loaded in rflags register. The stack pointer is stored in the next location 31 00:03:01,680 --> 00:03:06,420 which will be loaded in register RSP. The last one is stack segment selector. 32 00:03:07,170 --> 00:03:12,120 Because stack is last-in first-out structure, we push the data in reverse order. 33 00:03:14,070 --> 00:03:22,350 In this example, the stack segment selector is 18 in hexadecimal so the descriptor we reference is the fourth one. 34 00:03:22,830 --> 00:03:27,270 and the RPL is 3. Since we want to get to ring3, 35 00:03:27,270 --> 00:03:32,940 the DPL of the descriptor and RPL of ss selector are set to 3. 36 00:03:34,900 --> 00:03:35,950 OK, so 37 00:03:38,580 --> 00:03:39,300 we push 38 00:03:40,380 --> 00:03:42,540 18 ors 3 39 00:03:44,020 --> 00:03:46,310 You can simply add them together and push the result 40 00:03:46,330 --> 00:03:46,870 . 41 00:03:48,210 --> 00:03:49,140 Then we push 42 00:03:50,740 --> 00:03:57,220 7c00. The stack pointer is set to 7c00 in this example. 43 00:04:00,030 --> 00:04:05,370 Then the value of rflags. We only set the bit 1 to 1 which is required. 44 00:04:06,810 --> 00:04:07,890 So we push 2. 45 00:04:09,900 --> 00:04:17,200 Other flags such as carry flag overflag are set to 0. 46 00:04:17,730 --> 00:04:24,330 The interrupt flag is not set because when we are running in ring3 and an interrupt is fired, 47 00:04:24,330 --> 00:04:29,220 task state segment TSS is needed to call the handler, we haven’t implemented the TSS 48 00:04:29,220 --> 00:04:31,140 up to this point, 49 00:04:31,140 --> 00:04:33,750 so we don’t enable interrupt when we return. 50 00:04:35,040 --> 00:04:42,060 The code segment selector is 10 which references the third descriptor and the RPL is 3. 51 00:04:42,630 --> 00:04:43,590 So we push 52 00:04:44,920 --> 00:04:47,080 10 ors 3 53 00:04:48,750 --> 00:04:51,540 The return address is user entry. 54 00:04:52,680 --> 00:04:55,140 So we push user entry. 55 00:04:58,900 --> 00:05:01,480 Ok now we can write interrupt return. 56 00:05:03,990 --> 00:05:10,500 When the return executes, the rsp is set to 7c00 and we will jump to user entry. 57 00:05:11,010 --> 00:05:18,000 Cs and ss registers will load the descriptor just as we set here. 58 00:05:18,020 --> 00:05:19,170 We haven’t defined the label user entry. 59 00:05:19,380 --> 00:05:20,040 Let's do it. 60 00:05:24,590 --> 00:05:28,130 For the simplicity, we only check the lower 2 bits of cs register 61 00:05:28,130 --> 00:05:31,970 which indicates the privilege level we are currently at. 62 00:05:32,630 --> 00:05:36,110 So we move ax cs 63 00:05:37,700 --> 00:05:41,090 and al 11 in binary. 64 00:05:43,930 --> 00:05:51,580 so we use this instruction to preserves the lower 2 bits of al register and clear other bits. 65 00:05:52,240 --> 00:05:53,360 If the value in al now is 3, 66 00:05:53,560 --> 00:05:57,670 it means we are running in ring3. 67 00:05:59,300 --> 00:06:05,450 We compare al and 3 if not euqal we jump to UEnd. 68 00:06:07,810 --> 00:06:11,530 Note that we cannot execute halt instruction in ring3. 69 00:06:13,550 --> 00:06:14,810 So in the uend, 70 00:06:18,130 --> 00:06:20,140 we just jump to the current location 71 00:06:22,230 --> 00:06:26,940 to end our program. If we are in ring3, we print the character U 72 00:06:31,340 --> 00:06:33,590 indicating that we are running in user mode. 73 00:06:42,940 --> 00:06:45,280 OK, we save the file and test it out. 74 00:06:47,830 --> 00:06:56,620 And here, the number should be hexadecimal number and also we comment out sti instruction because we don't 75 00:06:56,620 --> 00:06:58,090 want to enable interrupt. 76 00:07:00,980 --> 00:07:03,140 We build the project in the terminal 77 00:07:06,740 --> 00:07:08,450 and test it in the bochs. 78 00:07:11,930 --> 00:07:13,930 OK, you can see u is printed. 79 00:07:14,910 --> 00:07:17,180 So we successfully jump to ring3.