2. Device Initialization and Process 0 Activation 45
2.5 Binding the Interrupt Service Routine
The user process and the kernel often use interruption and handle many exceptions, such as overflow, boundary checking, page fault exception, and so on. The interruption mecha- nism is also widely used in system call. These interruptions and exceptions need a special service program to execute. The function trap_init can hook the service program of the interruption and exception to the interruption descriptor table (idt), which also rebuilds the interruption service to support the operation of the kernel and the process in the host.
After setting mem_map[]
ROM BIOS and VGA
Kernel
Disable interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
mem_map[] each unit of memory page state management structure control the number of a page use
1M 6M 16M
1M 6M 16M
1M 6M 16M
Set the usage number to 100 Set to 100
Set main memory area page as “not use” state Set to 0
Before setting mem_map[]
Figure 2.5 The initialization of the memory management structure mem_map.
The process of binding and the space occupied by the interruption service routine are shown in Figure 2.6.
The execution code is as follows:
//Code path:init/main.c:
void main(void) {
……
trap_init();
……
}
//Code path:kernel/traps.c:
void trap_init(void) {
int i;
set_trap_gate(0,÷_error);//Divide by zero error
set_trap_gate(1,&debug); //step-by-step debugging set_trap_gate(2,&nmi); //Non-maskable interrupt set_system_gate(3,&int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow); //overflow
set_system_gate(5,&bounds); //bounds check error set_trap_gate(6,&invalid_op); //Invalid operation set_trap_gate(7,&device_not_available); //Invalid device set_trap_gate(8,&double_fault); //double fault
set_trap_gate(9,&coprocessor_segment_overrun);//coprocessor segment
//overrun
set_trap_gate(10,&invalid_TSS); //Invalid TSS
set_trap_gate(11,&segment_not_present); //Segment does not present set_trap_gate(12,&stack_segment); //stack exception
set_trap_gate(13,&general_protection); //general protection exception set_trap_gate(14,&page_fault); //page fault
set_trap_gate(15,&reserved); //reserved
set_trap_gate(16,&coprocessor_error); //coprocessor error
for (i = 17;i<48;i++) //they are all binding, the interrupt service routine //name is initialized to reserved
set_trap_gate(i,&reserved);
set_trap_gate(45,&irq13); //coprocessor
outb_p(inb_p(0x21)&0xfb,0x21); //Allow IRQ2 interrupt request outb(inb_p(0xA1)&0xdf,0xA1); //Allow IRQ2 interrupt request set_trap_gate(39,¶llel_interrupt); //parallel port }
//Code path:include\asm\system.h:
……
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__(“movw%%dx,%%ax\n\t” \ //The low word of edx is assigned to the low //word of eax
“movw%0,%%dx\n\t” \ //% 0 corresponds to “i” at the first line //after the second colon
“movl%%eax,%1\n\t” \ //% 1 corresponds to “o” at the second line //after the second colon
“movl%%edx,%2” \ //% 0 corresponds to “o” at the third line //after the second colon
: \ //After this colon are outputs, After the next colon are //inputs
: “i” ((short) (0x8000+(dpl<<13)+(type<<8))), \ //Immediate
“o” (*((char *) (gate_addr))), \ //Address of the former 4 bytes
//of interrupt descriptor
“o” (*(4+(char *) (gate_addr))), \//Address of the last 4 bytes
//of interrupt descriptor
The purpose of the code is to make up the idt descriptors described in Section 1.3.5.
The idt is copied for ease of reading, as shown in the following:
The result of executing the above code is as follows (Figure 2.7):
Comparing:
Disable interrupt ROM BIOS
and VGA
Kernel
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
IDT 48 interruption service program
0 47 255
Hook interrupt service program with IDT
Kernel code area Kernel data area
IDT
0 47 255
After hook up Before hook up
Figure 2.6 Hooking the interrupt service program.
set_trap_gate(0,÷_error) set_trap_gate(n,addr)
_set_gate(&idt[n],15,0,addr) _set_gate(gate_addr,type,dpl,addr)
“d” ((char *) (addr)),”a” (0x00080000))//”d” corresponds to edx,
//”a” corresponds to eax
……
#define set_trap_gate(n,addr) \ _set_gate(&idt[n],15,0,addr)
As we can see, n is 0; gate_addr is &idt[0], which is the address of the first content of idt; type is 15; dpl (descriptor privilege level) is 0; and addr is the entrance address of the interrupt service program “divide_error(void) (Figure 2.8).”
The instruction “movw%0,%%dx\n\t” means that the low word of edx is assigned to the low word of eax. Then edx is (char *) (addr), as well as ÷_error. The value of eax is 0x0080000, which is mentioned in head.s. Here, 8 is 1000 in binary, of which every bit is mean ingful. Thus, the value of eax is 0x0080000 + (the low word of(char *)(addr)). 0x0008 is the segment selector, with the same meaning as the “jmpi 0,8” described in Chapter 1.
The instruction “movw%0,%%dx\n\t” means that (short) (0x8000 + (dpl<<13) + (type<<8)) is assigned to dx. Do not forget that, here, edx is (char *) (addr), as well as
÷_error.
CPU
IDTR
IDT
Interrupt service program
gate_addr idt[0]
gate_addr idt[1]
gate_addr idt[2]
gate_addr idt[3]
gate_addr idt[4]
addr addr addr addr addr
÷_error
&debug
&nmi
divide_error(void) debug(void) nmi(void) int3(void) overflow(void)
&int3
&overflow ...
Ignore GDT
Figure 2.7 An overview of the interrupt handle.
set_trap_gate(0, ÷_error)
set_trap_gate(n, addr)
_set_gate(&idt[n], 15, 0, addr)
_set_gate(gate_addr, type, dpl, addr) Figure 2.8 Function parameters.
Because data of this part is spliced by bit, we must calculate precisely as follows.
0x8000 is 1000 0000 0000 0000 in binary.
dpl is 00, and dpl<<13 is 000 0000 0000 0000.
type is 15, and type<<8 is 1111 0000 0000.
Adding them up amounts to 1000 1111 0000 0000, which is the value of dx. Calculation result of edx is the high word of (char *) (addr), as well as the high word of ÷_error +1000 1111 0000 0000.
The instruction “movl%%eax,%1\n\t” means that the value of eax is assigned to
*((char *) (gate_addr)), also the first 4 bytes of idt[0]. Similarly, “movl%%edx,%2” means that the value of edx is assigned to *(4+(char *) (gate_addr)), also the last 4 bytes of idt[0].
All 8 bytes together is integral idt[0]. The result is as follows (Figure 2.9):
The first entry of interrupt descriptor “diving by zero” in the IDT table is initialized completely. Initialization of other interrupt service routine and IDT is quite similar.
0x8000:
dpl<<13:
type<<8:
1000 0000 0000 0000 000 0000 0000 0000 1111 0000 0000 1000 1111 0000 0000
8 7
15 0
7
5
3
1
6
4
2
0
edx (char*)(addr), the high word of ÷_error
1 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0
Selector 0x0008
(char*)(addr), the low word of ÷_error
eax
15 8 7 0
6 4 2 0 7
5 3 1
(char*)(addr), the high word of ÷_error
(char*)(addr), the low word of ÷_error Selector 0x0008
1 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0
''movl %%eax,%1'' ''o''(*((char*)(gate_addr))) ''movl %%edx,%2'' ''o''(*(4+(char*)(gate_addr))) ''d''((char*)(addr))
''movw %0,%%dx\n\t'' ''i''((short)(0x8000+(dpl<<3)+(type<<8)))
''a''(0x00080000) ''movw %%dx,%%ax\n\t''
Figure 2.9 How the parameters add into the IDT.
Both set_system_gate(n,addr) and set_trap_gate(n,addr) use the same _set_
gate(gate_addr,type,dpl,addr), and the difference is that the dpl of set_trap_gate is 0 while the dpl of set_system_gate is 3. “dpl is 0” means that it can only be handled by the kernel.
However, “dpl is 3” means that system can be called from privilege level 3 (also the user privilege level).
Learn more about privilege level in Volume 3.pdf of the Intel IA-32 Architectures Software Developer’s Manual.
Next, int 0x11–0x2F of idt are all initialized, and the pointer to interrupt service pro- gram in idt is set with the status “reserved.”
Set the idt of the co-processor.
Enable the interrupt request from IRQ2 and IRQ3 of the 8259A interrupt controller.
Set the idt of the parallel port (access to printer).
The 32-bit interrupt service system is established to meet the interrupt signal mechanism “passive response,” which is described as follows. On one hand, the hard- ware sends signals to 8259A, and 8259A preliminarily handles signals and transfers interrupt signals to the CPU. On the other hand, if the CPU does not receive signals, it executes the program. Otherwise, the program being executed is interrupted and a specific interrupt service program is located by the idt, which will be executed immedi- ately. After interruption is completed, the CPU will return to the program point where interrupt happens to continue. If interrupt signal is again received, the CPU will repeat the process.
The original design is not like this. Originally, the CPU polls all hardware from time to time to check whether its task has been completed or not. If not, the CPU continues. This method takes time for handling user process and reduces the over- all efficiency of the system. We can see that it is not efficient to handle signals by way of “active polling.” It is an improvement that the “passive response” mode takes the place of the “active polling” mode in handling the I/O problem between host and peripherals.