Intel 80x86 CPU Hardware interrupt process in protected mode
University learning operating system principle, feel process and file seems to be introduced up to two pieces of content, but slowly found that to understand the operating system work mechanism, if according to the Order of knowledge learning, the interruption should be compared before the right, only understand the mechanism of interruption, it is possible to truly understand the process, file system, Equipment and other concepts.
Interrupts actually provide a basic guarantee for the implementation of many concepts in the OS, such as process scheduling, device access, user-state and kernel-state switching, various exception handling, and so on, which require interrupted participation, and even secure access to critical resources requires interrupted support. This is enough to show that interrupts can be used as an entry point to understand how the operating system works and how it is specific, not just as a separate chapter in the relevant book.
Because the mechanism and implementation of interrupts are closely related to the hardware, here is only a summary of the Intel 80x86 series CPU interrupts at the hardware level of the process.
1. Conceptual differences between interrupts and exceptions
Intel's Official document [1] understands interrupts and anomalies as two different mechanisms that interrupt the execution of the current program. This is the common denominator of interrupts and anomalies. The difference is:
Interrupts (interrupt) are asynchronous events that are typically triggered by an I/O device, for example;
An exception (exception) is an event that is synchronous, typically such as when a processor executes an instruction and finds an error.
Interrupts can be divided into masking interrupts and non-shielded interrupts, anomalies are divided into fault, traps and abnormal abort 3 kinds, their specific differences in many books and official documents are explained more clearly here no longer repeat.
There are two points to note about their differences:
1) The usual shielding interrupts are not exceptions, that is, the exception will not be affected by the CPU if bit is cleared (off interrupt, instruction: CLI), such as a fault, even if the interrupt will trigger the CPU processing.
2) usually said int 80h This system calls the use of the interrupt mode is actually the hardware is understood as exception handling, and therefore will not be masked out, which is also very good understanding, int 80h This interrupt mode is the active trigger in the program, for the CPU is a synchronous event, so it belongs to the category of exceptions.
2. Interrupt (exception) handling process
It is important to be clear that the CPU's specific processing mechanism for interrupts and exceptions is essentially identical, namely:
When the CPU receives an interrupt or an abnormal signal, it pauses the execution of the current program or task and, through a certain mechanism, jumps to the relevant handler responsible for processing the signal, and then jumps back to the program or task that was interrupted just after the processing of the signal is completed. This is only described in the protection mode of the processing process, make clear the protection mode of processing (more complex), real-mode processing mechanism is easy to understand.
The specific processing process is as follows:
0) Pre-preparation for interrupt response:
To be able to handle a variety of interrupt signals, the system needs to know which interrupt service program is responsible for each signal and how these interrupt service programs work. The system can correctly respond to various interrupt signals and anomalies only if it knows the two things well beforehand.
[A] The system has unified all the interrupt signal numbering (altogether 256:0~255), this is known as the interrupt vector, the specific interrupt vector which indicates which interrupts have some provisions, but also in a given range of self-set.
The correspondence between interrupt vectors and interrupt service programs is primarily the responsibility of IDT (interrupt vector table). The operating system sets up interrupt descriptors for various interrupt vectors in IDT (there are three types of interrupt gate descriptors: Task Gate, interrupt Gate, and Trap Gate), which are reserved for CPU queries. The location of IDT itself is saved by IDTR, and of course the address is populated by the OS.
The following diagram shows the basic structure of IDT and how IDTR indicates the location and length of IDT:
[B] The Interrupt service program is specifically responsible for processing interrupts (exceptions) code is implemented by the software, that is, the operating system, this part of the code belongs to the operating system kernel code. That is, from the CPU detects the interrupt signal to load the interrupt service program and resumes from the interrupt service program to execute the suspended program, the process is basically determined by the hardware, and the specific interrupt vector and service program corresponding relationship settings and interrupt Service program content is determined by the operating system.
1) CPU Check for interrupt/abnormal signal
After executing each instruction of the current program, the CPU verifies that the interrupt controller (such as 8259A) is sent interrupt request during the execution of the instruction, and if so, the interrupt vector corresponding to the interrupt request is read from the bus when the corresponding clock pulse arrives [2].
For soft interrupts such as exceptions and system calls, the interrupt vectors are given directly, so unlike the hardware interrupt requests that are sent through the IRQ (interrupt request) line, they are no longer dedicated to their corresponding interrupt vectors.
2) segment selector for interrupt programs that handle this vector according to the interrupt vector to the IDT table
The CPU finds the interrupt descriptor corresponding to the vector according to the resulting interrupt vector to the IDT table, and interrupts the segment selector that character holds the interrupt service program.
3) Find the appropriate segment descriptor in the GDT according to the obtained segment selector
The CPU uses the segment selector of the interrupt service program that IDT has found to obtain the corresponding segment descriptor from the GDT, which describes the character to save the segment base and attribute information of the interrupt service program, at which point the CPU gets the start address of the Interrupt service program.
Here, the CPU will be based on the current CS register in the CPL and GDT segment descriptor of DPL, to ensure that the interrupt service program is higher than the current program, if the interrupt is a programming exception (such as: int 80h system call), then also check the CPL and IDT table in the interrupt descriptor DPL, To ensure that the current program has permission to use the Interrupt service program, which avoids user application access to special trap doors and break gates [3].
The following figure shows the positioning method from the interrupt vector to the starting position of the corresponding interrupt service program in the GDT:
4) The CPU sets the address of the stack to be used by the running interrupt service according to the judgment of the privilege level.
Depending on the DPL information of the CPL and Interrupt Service Program segment descriptor, the CPU verifies whether a privileged conversion has occurred, such as if the current program is running in the user state, and the interrupt program is running in the kernel state, which means that a privilege-level conversion has occurred. The CPU then obtains the kernel stack address of the program from the current program's TSS information (which exists in the TR register at the first address in memory), which includes the values of SS and ESP, and immediately switches the stack currently used by the system into a new stack. This stack is the stack to be used by the interrupt service program that is about to run. The SS,ESP used by the current program is then saved to the new stack.
6) Protect the site of the current program
The CPU begins to use the stack to protect the field of a suspended program: Press the Eflags,cs,eip,errorcode (if it is an exception with an error code) that is used by the current program, in turn.
The schematic of the stack changes given in the official document [1] is as follows:
7) Jump to the first instruction of the interrupt service program to start execution
The CPU takes advantage of the interrupt Service program's segment descriptor to load the address of its first instruction into CS and EIP registers and begins execution of the interrupt service program. This means that the previous program was suspended and the interrupt service program officially began to work.
8) The interrupt service process is completed, and the program that executed the previous interrupt is resumed.
At the end of each interrupt service program, there must be an interrupt to complete the instructions to return to the previous program, which is Iret (or iretd). When the program executes this return instruction, the field information of the previously saved suspended program is ejected from the stack, that is, Eflags,cs,eip restarts execution. If there is a privilege-level conversion, the SS and ESP are also ejected, which also means that the stack is switched back to the stack that was originally used.
Here's a place to be aware: if this is handled with an error code (ERRORCODE) exception, the CPU in the previous program to restore the scene, and will not eject ErrorCode, that is, the CPU seems to have forgotten to press a errorCode into the stack, It is therefore required that the relevant Interrupt service program should actively eject ErrorCode before calling Iret returns.
Bibliography:
[1] Intel IA-32 architectures software developers Manual Volume 1 Basic Architecture
[2] "Microcomputer interface technology and application", Huazhong University of Science Press, Liu Leshan Editor
[3] Deep understanding of the Linux kernel, third edition, China Power Press
In the previous case, the real mode uses int 15h to get the memory information and then display it in protected mode. In the protection mode, the interrupt mechanism has changed greatly, the original interrupt vector table is replaced by IDT (Interrupt descriptor table, interrupt descriptor tables), and the BIOS interrupt in real mode can not be used in protected mode. IDT can match each interrupt vector with a descriptor. The descriptors in IDT can be interrupt gate descriptors, Trap gate descriptors, and task gate descriptors. Although IDT is very different in form from a vector table in real mode, it is also a vector table.
The action mechanism of the interrupt gate and the trap gate is almost the same, except that the call command is used when invoking the gate, and here we use the INT directive.
Each interrupt (exception) corresponds to an interrupt vector number, and the vector number corresponds to the corresponding interrupt handler through IDT. Protection mode interrupts and exceptions are shown in the following table:
Vector number |
Mnemonic |
Describe |
Type |
Error code |
Source |
0 |
#DE |
Division Error |
Fault |
No |
Div and idiv Instructions |
1 |
#DB |
debugging exceptions |
Fault/trap |
No |
Access to any code and data |
2 |
— |
Unshielded interrupts |
Interrupt |
No |
unshielded External interrupts |
3 |
#BP |
debugging breakpoints |
Trap |
No |
directive INT 3 |
4 |
#OF |
Overflow |
Trap |
No |
Instruction into |
5 |
#BR |
Crossed |
Fault |
No |
Directive bound |
6 |
#UD |
Invalid (undefined) opcode |
Fault |
No |
instruction UD2 or invalid instruction |
7 |
#NM |
Device not available (no math coprocessor) |
Fault |
No |
Floating-point or wait/fwait directives |
8 |
#DF |
Double error |
Abort |
Have (0) |
All can produce anomalies or nmi or intr The instructions |
9 |
|
Coprocessor segment out of bounds (reserved) |
Fault |
No |
Floating point Instruction (386 no longer handles this exception |
10 |
#TS |
Invalid TSS |
Fault |
Yes |
Task switching or when visiting TSS |
11 |
#NP |
Segment does not exist |
Fault |
Yes |
When you load a segment register or access a system segment |
12 |
#SS |
Stack segment Error |
Fault |
Yes |
Stack operation or when loading SS |
13 |
#GP |
General Protection Errors |
Fault |
Yes |
Memory or other protection checks |
14 |
#PF |
Page error |
Fault |
Yes |
Memory Access |
15 |
— |
Intel reserved, not used |
|
|
|
16 |
#MF |
X87FPU floating point error (mathematical error) |
Fault |
No |
X87FPU floating point instruction or wait/fwait instruction |
17 |
#AC |
Alignment Test |
Fault |
Have (0) |
In-Memory data access (486 start support) |
18 |
#MC |
Machine Check |
Abort |
No |
The error code (if any) and the source depend on the Specific mode (Pentium CPU starts to support) |
19 |
#XF |
SIMD Floating-point exceptions |
Fault |
No |
SSE and SSE2 floating point instructions (Pentium III Start support) |
20~31 |
— |
Inter reserved, not used |
|
|
|
32~255 |
— |
User-defined interrupts |
Interrupt |
|
external interrupt or int n instruction |
The three types of exceptions are explained as follows:
Fault (Error)--an exception that can be corrected, and the program can continue to execute after the correction. When a fault occurs, the processor will save the state before the instruction that generated the fault. The return address of the exception handler will be the command that generates the fault, not the one that follows.
Trap-An exception that is reported immediately after the execution of a trap's instruction, and it also allows the program or task to continue to execute without losing its continuity. The return address of the exception handler will be the one following the command that generated the trap.
About (terminate)--does not always report the exact location of the exception, it does not allow the program or task to continue executing, but is used to report a critical error.
Interrupts occur for two reasons:
External interrupts (that is, hardware-generated)-The correspondence between hardware interrupts and vector numbers needs to be established. Can be divided into two types of shielded interrupts and non-shielded interrupts. An unshielded interrupt is received by the CPU's NMI pin, and a masked interrupt is received by the CPU's INTR pin. The relationship between the shielded interrupt and the CPU is through the programmable Interrupt Controller 8259A (which can be considered as an agent for all peripheral devices in the interrupt mechanism, which can not only select the request that should be processed in the device with the interrupt at the same time as the priority It can also be set up to block or open the corresponding interrupt by setting its register.
Internal interrupt (generated by int n)-similar to the use of the call Gate, n is the vector number.
Set 8259A:
There are two cascaded 8259A connected to the CPU, each 8259A has 8 interrupt signal line, two cascade male can hook up 15 different peripherals. The 8259A is set up so that interrupt requests from these peripherals correspond to interrupt vectors. When the BIOS initializes it, IRQ0~IRQ7 is set to the corresponding vector number 08h~0fh, but the vector numbers are already occupied in protected mode, so it needs to be reset. The 8259A is a programmable interrupt controller that can be set up by writing a specific ICW (initialization Command Word) to the appropriate port. The main 8259A corresponding port address is 20h and 21h, the corresponding port address from 8259A is a0h and a1h. Initialization process (cannot reverse order): Write icw1-> toward Port 20h or a0h to port 21h or a1h write icw2-> toward Port 21h or a1h write icw3-> toward Port 21h or a1h write ICW4 (Note: 2, 3, 4 write ports are the same).
There is also a thing called OCW (Operation Control Word), a total of OCW1, OCW2, OCW3 a total of three, used to mask or open an external interrupt (to 21h or a1h write OCW1, in fact OCW1 is written to the interrupt mask register IMR, When an interrupt arrives, IMR will determine if the interrupt should be discarded, or send EOI to 8259A to notify it that the interrupt processing is complete.
For EOA, the end of each interrupt processing requires sending a EOI to 8259A in order to continue accepting interrupts. The Send EOI is implemented by writing OCW2 to a 20h or a0h port.
The difference between a masked interrupt and an NMI is whether it is affected by the if bit, while the 8259A Interrupt screen register (IMR) also affects whether the interrupt is being responded to. Therefore, the external masked interrupt is affected by the IF bit (required 1) and the IMR bit (0 required).
In practice, the generation of interrupts is mostly a privilege-level transformation, and the rules are exactly the same as calling a call to a door. If the interrupt or exception occurs without a privilege level transformation, then eflags, CS, EIP, will be pressed into the stack sequentially, if there is an error code, then the error code is finally compressed stack. If there is a privilege-level transformation, SS and ESP are pressed into the inner stack, then eflags, CS, EIP, error codes (if any). Anyway, the stack will change when an interrupt or an exception occurs.
The instruction iretd must be used when returning from an interrupt or exception, except that it changes the value of the eflags at the same time. It is important to note that only if the CPL is 0 o'clock, the IOPL field in EFlags will change, and if the CPL is less than or equal to IOPL, the if will change. Also note that the error code is not automatically ejected from the stack when the iretd executes, so it is removed from the stack before executing it.
Also, there is a small difference between the break gate and the trap door. Interrupts caused by the interrupt gate vector will reset if, because other interrupts can be prevented from interfering with the processing of the current interrupt, and subsequent iret instructions will reply to the if's original value from the stack, while the interrupts generated through the trap gate will not change if.
The following content is I/O in protected mode with very little content ~
I/O sensitive directives: in, ins, out, outs, CLI, STI, they can only be executed if the CPL is less than or equal to IOPL, and if the low-privileged instruction attempts to access these I/O sensitive instructions will cause a general protection error (#GP).
The instructions that can change the IOPL are only Popf and iretd, but only the programs that run in RING0 can change them, and the low privileged levels cannot be changed, but they do not produce an exception (which remains the same). The directive Popf can also be used to change if (like executing the CLI and STI). In this case, however, the Popf also becomes an I/o sensitive instruction, only if the CPL is less than or equal to IOPL, the Popf can change if, otherwise it remains the same (no exception is generated).
The I/O bitmap base is an offset from the address of the TSS, pointing to the I/O license bitmap. Each of its characters indicates whether a byte port address is available (0 is available and 1 is unavailable). Since each task can have a separate TSS, each task can have its own I/O license bitmap. The I/O license bitmap must end with 0FFh. If the I/O bitmap base is greater than or equal to the TSS segment bounds, there is no I/O license bitmap, and if the CPL is less than or equal to IOPL, all I/O directives will cause an exception. The use of the I/O license bitmap makes it possible for different tasks to have different I/O access rights even under the same privilege level.