Binding the Interrupt Service Routine

Một phần của tài liệu The art of linux kernel design (Trang 68 - 73)

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,&divide_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,&parallel_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,&divide_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 &divide_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

&divide_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

&divide_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, &divide_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 &divide_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 &divide_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 &divide_error

eax

15 8 7 0

6 4 2 0 7

5 3 1

(char*)(addr), the high word of &divide_error

(char*)(addr), the low word of &divide_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.

Một phần của tài liệu The art of linux kernel design (Trang 68 - 73)

Tải bản đầy đủ (PDF)

(524 trang)