1 00:00:00,600 --> 00:00:08,130 In the lecture, we will talk about interrupts and see how to set up interrupt descriptor table as well as process interrupts. 2 00:00:08,130 --> 00:00:13,390 Interrupt handling is especially important part of the operating system. 3 00:00:13,920 --> 00:00:19,470 For example, If we don’t implement interrupt handling for keyboard, then we cannot use keyboard 4 00:00:19,470 --> 00:00:22,380 for interacting with computer. 5 00:00:22,400 --> 00:00:27,390 Process scheduling which is an essential part of our operating system relies on timer interrupt. 6 00:00:28,490 --> 00:00:35,410 Before we delve into the details, let’s talk a little bit about interrupt and exception. When we look at interrupts, 7 00:00:35,450 --> 00:00:42,530 we actually refer to hardware interrupts such as timer, keyboard interrupts etc. Whereas exception occurs 8 00:00:42,590 --> 00:00:46,870 as a result of execution errors or internal processor errors. 9 00:00:47,570 --> 00:00:51,410 The process of handling interrupts and exceptions are very similar. 10 00:00:52,980 --> 00:00:58,860 As we have learned the general process of interrupt handling, in this section we will go into details 11 00:00:58,860 --> 00:01:05,160 about how to set up idt and write interrupt service routines to handle specific interrupts. 12 00:01:07,760 --> 00:01:14,710 When an interrupt is fired, the correspond idt entry is selected, the entry includes the information about 13 00:01:14,780 --> 00:01:21,500 which code segment the interrupt handler is at and its attributes, the offset and other info, etc 14 00:01:21,700 --> 00:01:22,330 . 15 00:01:23,190 --> 00:01:27,590 So the essential data about the interrupt handler is stored in idt entry. 16 00:01:28,310 --> 00:01:29,180 Let's take a look. 17 00:01:30,480 --> 00:01:37,530 The idt entry takes up 16 bytes. The 64-bit offset of the interrupt handler is divided into three parts 18 00:01:37,530 --> 00:01:38,270 . 19 00:01:38,880 --> 00:01:42,750 As you see, the lower 16 bits is stored in the first two bytes. 20 00:01:43,970 --> 00:01:46,280 then the second part and last part. 21 00:01:48,110 --> 00:01:54,890 The bits 16 to 31 holds the selector of the code segment descriptor which the interrupt handler is at 22 00:01:54,890 --> 00:01:55,400 . 23 00:01:57,060 --> 00:02:03,870 IST stands for interrupt stack table. If we assign a value to this field, when the interrupt is fired, 24 00:02:04,440 --> 00:02:08,789 the stack pointer this field references is loaded into rsp register. 25 00:02:09,509 --> 00:02:12,090 We don't use the IST in this course. 26 00:02:12,330 --> 00:02:16,320 So we simply set it to zero meaning that this field is not used. 27 00:02:17,350 --> 00:02:24,340 As for the attributes, there is actually only one field we can modify which is DPL field. 28 00:02:24,340 --> 00:02:31,150 The present bit, as we know, should be 1. And the value 01110 indicates that this entry 29 00:02:31,150 --> 00:02:32,680 is interrupt descriptor table. 30 00:02:33,520 --> 00:02:41,350 The dpl field specifies that which cpl can access this descriptor. For example, if we set DPL to 0, 31 00:02:41,380 --> 00:02:47,080 then we cannot access the descriptor when we run in ring3 because the cpl is 3. 32 00:02:47,680 --> 00:02:53,760 This field is useful when the descriptor is for the software interrupt. We will talk about that later in the course 33 00:02:53,780 --> 00:02:54,490 . 34 00:02:54,970 --> 00:03:01,840 As for the hardware interrupt, we simply set the DPL to 0. Because the processor ignores 35 00:03:01,840 --> 00:03:05,050 the dpl of the descriptor of hardware interrupt and exceptions. 36 00:03:08,050 --> 00:03:13,020 There are 256 different exceptions and interrupts vectors we can use. 37 00:03:13,540 --> 00:03:19,000 As you can see, the numbers here are called interrupt vectors which identifies the exceptions and interrupts 38 00:03:19,000 --> 00:03:19,870 . 39 00:03:20,350 --> 00:03:27,010 For example, the vector of divide by zero exception is 0. 1 represents the debug exception and so on. 40 00:03:27,610 --> 00:03:32,820 Note that the vectors from 0 to 31 are predefine by the processor. 41 00:03:33,220 --> 00:03:39,580 We cannot redefine them. The vectors from 32 to 255 are user defined. 42 00:03:40,120 --> 00:03:44,800 The vectors for hardware interrupts and software interrupts are within this range. 43 00:03:46,370 --> 00:03:50,600 In this lecture, we will write a handler to deal with divide by zero exception. 44 00:03:51,800 --> 00:03:53,090 So let's get started. 45 00:03:53,960 --> 00:04:02,150 OK, in the kernel, we implement the handler for the divide by zero exception which is vector 0. 46 00:04:02,600 --> 00:04:08,780 So first off, we define the idt which can have 256 entries in total. 47 00:04:13,730 --> 00:04:16,600 So we define the label idt 48 00:04:18,899 --> 00:04:25,990 and here we use directive repeat and then the specific times we want to repeat, 49 00:04:26,070 --> 00:04:27,690 256 in this example. 50 00:04:31,480 --> 00:04:35,650 Here we simply set all the entries with the same value which is 0. 51 00:04:44,940 --> 00:04:48,420 As we did with gdt, we also define a constant 52 00:04:51,710 --> 00:04:52,130 idt length 53 00:04:57,810 --> 00:04:59,580 as well as idt pointer. 54 00:05:03,990 --> 00:05:07,910 The structure of idt pointer is the same as gdt pointer. 55 00:05:08,410 --> 00:05:11,660 The first two bytes stores the length of idt 56 00:05:18,080 --> 00:05:21,770 and address of idt is stored in the next 8 bytes. 57 00:05:26,000 --> 00:05:32,570 So for the moment, we set all the fields of the entries to zero and change the values of specific entries 58 00:05:32,570 --> 00:05:34,280 in the code when they need to. 59 00:05:35,620 --> 00:05:42,700 Because the attribute and selector value in each entry are the same in this example, we set these two fields here. 60 00:05:43,630 --> 00:05:46,840 So the first field is attribute field. 61 00:05:48,000 --> 00:05:53,540 As we have seen the structure of the entry in the slides, the attribute field is in the 6th byte. 62 00:05:54,420 --> 00:05:56,670 The value we copy to the field is 63 00:05:59,650 --> 00:06:00,550 8e 64 00:06:01,560 --> 00:06:05,490 which is 10001110 in binary. 65 00:06:07,270 --> 00:06:14,140 You can see present bit is 1 and 01110 means that this is the interrupt gate descriptor. 66 00:06:14,350 --> 00:06:17,990 DPL is simply set to 0. 67 00:06:19,300 --> 00:06:22,480 Another field is in 3rd byte. 68 00:06:24,570 --> 00:06:26,490 In this example, we set it to 69 00:06:28,040 --> 00:06:33,020 8 which is the same code segment descriptor that we currently use. 70 00:06:34,160 --> 00:06:40,100 One thing worth mentioning is that when using interrupt gate, we can only jump to the code segment 71 00:06:40,100 --> 00:06:44,520 that is in the same privilege or higher privilege than we are currently at. 72 00:06:45,260 --> 00:06:52,220 For example, if the DPL of the code segment the selector 8 reference is 3, 73 00:06:52,220 --> 00:06:58,950 then we cannot use this interrupt gate descriptor to jump to the handler because we are currently running in ring0, 74 00:06:59,030 --> 00:07:01,820 since ring0 has higher privilege than ring3. 75 00:07:02,940 --> 00:07:08,820 So you see using interrupt gate descriptor, we can only jump from lower privilege to higher privilege segment. 76 00:07:08,820 --> 00:07:15,320 the dpl of the code segment descriptor the selector 8 reference is 0, 77 00:07:15,570 --> 00:07:21,720 we can be sure that jump will succeed because ring0 is the most privileged level in our system. 78 00:07:22,920 --> 00:07:24,750 Alright, back to the start of kernel. 79 00:07:27,130 --> 00:07:32,670 In this example, we only want to write the handler for divide by zero exception which is the first exception 80 00:07:32,700 --> 00:07:33,250 . 81 00:07:34,280 --> 00:07:38,660 we copy the offset of the handler to the first idt entry. 82 00:07:42,350 --> 00:07:45,530 we move rdi idt 83 00:07:47,530 --> 00:07:48,880 And the move rax 84 00:07:49,890 --> 00:07:51,930 handler0 85 00:07:53,830 --> 00:07:59,910 Ok now rdi holds the address of idt and rax stores the offset of handler0. 86 00:08:01,180 --> 00:08:06,690 As we have seen it before, the offset is divided into three parts in the idt entry. 87 00:08:07,610 --> 00:08:11,650 The lower 16 bits is in the first two bytes. 88 00:08:16,090 --> 00:08:18,040 we move rdi ax 89 00:08:19,500 --> 00:08:26,070 which will copy the lower 16 bits of the offset to the location that rdi points to, that is 90 00:08:26,070 --> 00:08:27,260 the first two bytes of idt entry. 91 00:08:28,410 --> 00:08:32,419 Then we shift the offset in rax rightward by 16 bits. 92 00:08:36,940 --> 00:08:42,549 And now the bit 16 to 31 are in the register ax. 93 00:08:43,990 --> 00:08:44,590 we move 94 00:08:46,320 --> 00:08:48,780 rdi + 6 95 00:08:50,350 --> 00:08:50,750 ax. 96 00:08:52,740 --> 00:08:59,850 What this instruction does is we copy the bit 16 to 31 of the offset to the second part 97 00:08:59,850 --> 00:09:02,730 which is at the 7th byte in the entry. 98 00:09:04,080 --> 00:09:09,440 We continue doing it. Shift the offset 16 bits again. 99 00:09:11,490 --> 00:09:15,450 And bit 32 to 63 are in register eax now. 100 00:09:16,540 --> 00:09:17,410 So we move. 101 00:09:18,220 --> 00:09:19,900 rdi 102 00:09:20,880 --> 00:09:21,330 + 8 103 00:09:23,130 --> 00:09:28,500 and eax which will copy the value in eax to the third part of the offset. 104 00:09:29,480 --> 00:09:30,550 OK, we are done. 105 00:09:31,970 --> 00:09:34,740 Let’s load idt pointer using instruction load idt 106 00:09:34,760 --> 00:09:35,740 , 107 00:09:39,430 --> 00:09:41,740 and the idt pointer 108 00:09:43,730 --> 00:09:45,630 With the idt set up and loaded, 109 00:09:45,680 --> 00:09:50,710 what we are going to do next is we are going to write the handler. In this example, 110 00:09:50,720 --> 00:09:54,830 the handler is called Handler0, so we defines the label 111 00:09:56,800 --> 00:09:58,000 handler0. 112 00:10:00,660 --> 00:10:07,080 And it does nothing except print character D on the screen so that we know that divide by zero exception 113 00:10:07,080 --> 00:10:09,480 is actually handled by this service routine. 114 00:10:10,200 --> 00:10:12,690 So we copy the code of printing characters here. 115 00:10:16,130 --> 00:10:22,530 and the character we want to print is D. To make the character slightly different, we set the attribute 116 00:10:22,580 --> 00:10:27,510 of the character to the value c, which means the character is in red. 117 00:10:28,550 --> 00:10:34,790 The return instruction is different than the normal return instruction ret in procedures. 118 00:10:35,210 --> 00:10:37,330 Here we use interrupt return 119 00:10:40,930 --> 00:10:45,940 which will pop more data than the regular return and can return to the different privilege level 120 00:10:45,940 --> 00:10:46,370 . 121 00:10:46,910 --> 00:10:51,560 We will use this instruction to jump to ring3 manually later in this section. 122 00:10:52,330 --> 00:10:54,480 More details will be discussed there. 123 00:10:54,970 --> 00:10:55,740 For the moment, 124 00:10:55,750 --> 00:11:01,840 For now we just write it here to finish the handler. And also we stop the system by jumping to end. 125 00:11:07,090 --> 00:11:12,730 The reason is that we have an exception in the privilege level 0 where the system kernel runs. 126 00:11:12,920 --> 00:11:15,670 If our kernel has an error, we should stop it, 127 00:11:16,000 --> 00:11:19,560 print the error message to let us know and fix the error. 128 00:11:21,020 --> 00:11:24,260 OK, now, creating a divide by zero error is simple. 129 00:11:25,530 --> 00:11:28,410 All we need to do is using div instruction. 130 00:11:30,550 --> 00:11:34,900 So first off, we zero a register, for example. 131 00:11:38,110 --> 00:11:39,420 rbx and then div rbx 132 00:11:40,950 --> 00:11:46,760 If all things are set correctly, we will see character D in red is printed on screen. 133 00:11:46,780 --> 00:11:47,490 we save the file 134 00:11:49,070 --> 00:11:50,810 and run the script in the terminal. 135 00:11:54,700 --> 00:11:55,540 run the bochs. 136 00:11:58,210 --> 00:12:00,790 The red character D is printed on screen.