• No results found

TR register and Task State Segment

In document Low Level Programming (Page 113-118)

Interrupts and System Calls

6.1 Input and Output

6.1.1 TR register and Task State Segment

There are some artifacts from the protected mode that are still somehow used in long mode. A segmentation is an example, now mostly used to implement protection rings. Another is a pair of a tr register and Task State Segment control structure.

The tr register holds the segment selector to the TSS descriptor. The latter resides in the GDT (Global Descriptor Table) and has a format similar to segment descriptors.

Likewise for segment registers, there is a shadow register, which is updated from GDT when tr is updated via ltr (load task register) instruction.

The TSS is a memory region used to hold information about a task in the presence of a hardware task-switching mechanism. Since no popular OS has used it in protected mode, this mechanism was removed from long mode. However, TSS in long mode is still used, albeit with a completely different structure and purpose.

These days there is only one TSS used by an operating system, with the structure described in Figure 6-1.

The first 16 bits store an offset to an Input/Output Port Permission Map, which we already discussed in section 6.1. The TSS then holds eight pointers to special interrupt stack tables (ISTs) and stack pointers for different rings. Each time a privilege level changes, the stack is automatically changed accordingly. Usually, the new rsp value will be taken from the TSS field corresponding to the new protection ring. The meaning of ISTs is explained in section 6.2.

Figure 6-1. Task State Segment in long mode

Figure 6-3. Interrupt descriptor Figure 6-2. idtr register

6.2 Interrupts

Interrupts allow us to change the program control flow at an arbitrary moment in time. While the program is executing, external events (device requires CPU attention) or internal events (division by zero, insufficient privilege level to execute an instruction, a non-canonical address) may provoke an interrupt, which results in some other code being executed. This code is called an interrupt handler and is a part of an operating system or driver software.

In [15], Intel separates external asynchronous interrupts from internal synchronous exceptions, but both are handled alike.

Each interrupt is labeled with a fixed number, which serves as its identifier. For us it is not important exactly how the processor acquires the interrupt number from the interrupt controller.

When the n-th interrupt occurs, the CPU checks the Interrupt Descriptor Table (IDT), which resides in memory. Analogously to GDT, its address and size are stored in idtr. Figure 6-2 describes the idtr.

Each entry in IDT takes 16 bytes, and the n-th entry corresponds to the n-th interrupt. The entry incorporates some utility information as well as an address of the interrupt handler. Figure 6-3 describes the interrupt descriptor format.

DPL  Descriptor Privilege Level

Current privilege level should be less or equal to DPL in order to call this handler using int instruction. Otherwise the check does not occur.

Type 1110 (interrupt gate, IF is automatically cleared in the handler) or 1111 (trap gate, IF is not cleared).

The first 30 interrupts are reserved. It means that you can provide interrupt handlers for them, but the CPU will use them for its internal events such as invalid instruction encoding. Other interrupts can be used by the system programmer.

When the IF flag is set, the interrupts are handled; otherwise they are ignored.

Question 96 What are non-maskable interrupts? What is their connection with the interrupt with code 2 and

IF

flag?

The application code is executed with low privileges (in ring3). Direct device control is only possible on higher privilege levels. When a device requires attention by sending an interrupt to the CPU, the handler should be executed in a higher privilege ring, thus requiring altering the segment selector.

What about the stack? The stack should also be switched. Here we have several options based on how we set up the IST field of interrupt descriptor.

• If the IST is 0, the standard mechanism is used. When an interrupt occurs, ss is loaded with 0, and the new rsp is loaded from TSS. The RPL field of ss then is set to an appropriate privilege level. Then old ss and rsp are saved in this new stack.

• If an IST is set, one of seven ISTs defined in TSS is used. The reason ISTs are created is that some serious faults (non-maskable interrupts, double fault, etc.) might profit from being executed on a known good stack. So, a system programmer might create several stacks even for ring0 and use some of them to handle specific interrupts.

There is a special int instruction, which accepts the interrupt number. It invokes an interrupt handler manually with respect to its descriptor contents. It ignores the IF flag: whether it is set or cleared, the handler will be invoked. To control execution of privileged code using int instruction, a DPL field exists.

Before an interrupt handler starts its execution, some registers are automatically saved into stack. These are ss, rsp, rflags, cs, and rip. See a stack diagram in Figure 6-4. Note how segment selectors are padded to 64 bit with zeros.

Figure 6-4. Stack when an interrupt handler starts

Sometimes an interrupt handler needs additional information about the event. An interrupt error code is then pushed into stack. This code contains various information specific for this type of interrupt.

Many interrupts are described using special mnemonics in Intel documentation. For example, the 13-th interrupt is referred to as #GP (general protection).1 You will find the short description of the some interesting interrupts in the Table 6-1.

Table 6-1. Some Important Interrupts

VECTOR MNEMONIC DESCRIPTION

0 #DE Divide error

2 Non-maskable external interrupt

3 #BP Breakpoint

6 #UD Invalid instruction opcode

8 #DF A fault while handling interrupt

13 #GP General protection

14 #PF Page fault

Not all binary code corresponds to correctly encoded machine instructions. When rip is not addressing a valid instruction, the CPU generates the #UD interrupt.

The #GP interrupt is very common. It is generated when you try to dereference a forbidden address (which does not correspond to any allocated page), when trying to perform an action, requiring a higher privilege level, and so on.

The #PF interrupt is generated when addressing a page which has its present flag cleared in the corresponding page table entry. This interrupt is used to implement the swapping mechanism and file mapping in general. The interrupt handler can load missing pages from disk.

The debuggers rely heavily on the #BP interrupt. When the TF is set in rflags, the interrupt with this code is generated after each instruction is executed, allowing a step-by-step program execution.

Evidently, this interrupt is handled by an OS. It is thus an OS’s responsibility to provide an interface for user applications that allows programmers to write their own debuggers.

To sum up, when an n-th interrupt occurs, the following actions are performed from a programmer’s point of view:

1. The IDT address is taken from idtr.

2. The interrupt descriptor is located starting from 128 × n-th byte of IDT.

3. The segment selector and the handler address are loaded from the IDT entry into cs and rip, possibly changing privilege level. The old ss, rsp, rflags, cs, and rip are stored into stack as shown in Figure 6-4.

4. For some interrupts, an error code is pushed on top of handler’s stack. It provides additional information about interrupt cause.

5. If the descriptor’s type field defines it as an Interrupt Gate, the interrupt flag IF is cleared. The Trap Gate, however, does not clear it automatically, allowing nested interrupt handling.

1See section 6.3.1 of the third volume of [15]

If the interrupt flag is not cleared immediately after the interrupt handler start, we cannot have any kind of guarantees that we will execute even its first instruction without another interrupt appearing asynchronously and requiring our attention.

In document Low Level Programming (Page 113-118)