1.   Introduction

        Hardware Interrupts

        Software Interrupts

        Processor Exception

2.   Real-Mode

        Software Interrupts

1.   Code Exception handler in C

2.   Stack Layout

        Hardware Interrupts

1.   Interrupt Controller Chips

2.   Exception support

3.   Protected-Mode

        Software Interrupts

        Hardware Interrupts

 


 

Introduction

There are many of the functionality embedded inside a personal computer is implement by Hardware devices other than the processor. Since each device operates at its own pace, a method is needed for synchronizing the operation of the processor with these devices. One solution is for the processor to sit in a tight loop, asking each device about its current state (a method known as polling). When data is available in one of the devices, the processor can then read and process the incoming bytes. While this method works, it has many disadvantages. First, it is very wasteful in terms of processing power, since the processor is constantly busy reading the status of the attached devices instead of executing some useful code. And second, when the rate of data transfer is extremely high, the processor might lose data bytes arriving from the hardware devices.

The interrupt controller serves as an intermediate between the hardware devices and the processor. Its responsibility is to alert the processor when one of the hardware devices needs its immediate attention. In this case, the processor stops its current activity and jumps to execute a function (interrupt handler) which was previously associated with the calling. For example iadd count, 3.

While hardware interrupts have a significant importance, software interrupts also play a major role in the normal operation of the PC. These interrupts are synchronous rather than asynchronous, since they are generated by the processor itself when it encounters an INT instruction inside the code stream. Software interrupts are usually used by the operating system to publish its internal functions, thereby allowing applications to take advantage of these services.

Exceptions belong to a special type of software interrupts. They are generated by the processor itself whenever some unexpected critical event occurs. For instance, a page fault exception (interrupt 14) is triggered when the processor attempts to access a page, which is marked as not-present. The exception handler can then reload the page from disk (virtual memory) and restart the instruction which generated the exception.

Three types of exceptions can be generated by the processor: faults, traps and aborts. When a fault exception occurs, the CS and (E)IP registers which are pushed on the stack, point to the address of the instruction, which generated the exception. This gives the exception handler a chance to fix the condition which caused the exception to occur, before restarting the faulting instruction. Traps are similar to interrupts in the sense that they make the processor push the address of the next instruction to the stack, while aborts neglect to specify the location of the faulting instruction, since they are usually used to indicate severe errors (such as hardware errors or illegal system tables) which are not recoverable.

Real-Mode
Software Interrupts

The BIOS (Basic Input Output System) uses a similar software communication mechanism. The INT 10h, 13h and 16h are all interfaces to internal BIOS functions, which control the screen, the disk controller and the keyboard. Instead of accessing hardware devices directly, DOS uses the BIOS services to control the operation of the system. Modern operating systems such as Windows NT drop their reliance upon the BIOS in favor of faster mechanisms (device drivers) for accessing the PC's hardware.

When the processor bumps into an INT instruction, it pushes the address of the next instruction (CS and IP registers) and the contents of its flags register to the stack and then jumps to execute the interrupt handler. This ensures that when the handler code ends, the processor returns automatically to the original code stream (the code which was executing before the interrupt occured).

Here are the precise steps taken by the processor when it encounters an INT instruction:

Push Flags, CS and IP to the stack (in this order).

Multiply interrupt number by 4 and use the resulting number as an offset into the interrupt table (located at the beginning of the physical address space).

Get far address (CS and IP) of the interrupt handler from the table entry.

Disable interrupts (note that if you wish hardware interrupts to be serviced even when the processor executes inside an interrupt handler, you must issue a STI instruction at the beginning of the handler code).

Jump to execute the handler.

When the handler code ends (an IRET instruction is executed), pop CS, IP and the flags from the stack so that control returns to the currently active application.

Many applications (especially TSRs - Terminate and Stay Resident programs) hook software interrupts to supply additional services on top of those offered by DOS and the BIOS. One good example is Netware 3.1, which hooks INT 21h in order to add support for network file operations (such as reading a file located on a remote computer). Other pieces of code (mostly device drivers) hook hardware interrupts to monitor various devices and ensure their proper operation.

To hook an interrupt, a program needs only to replace a specific interrupt table entry with the address of its own interrupt handler. Whenever this interrupt occurs, the processor automatically invokes the handler. A good programming practice is to place a call to the previous handler inside the interrupt handling function. This ensures that all previously installed hooks will get a chance to handle the incoming interrupt.

Note that when loading an application that uses software interrupts to call operating system services, there is no need to patch the application code with the actual memory addresses of the OS services. Therefore, the operating system can freely relocate its services in memory by simply updating the corresponding interrupt table entries with the new addresses.

The Interrupt Descriptor Table (IDT) is an array of 8 byte interrupt descriptors in memory devoted to specifying (at most) 256 interrupt service routines. The first 32 entries are reserved for processor exceptions, and any 16 of the remaining entries can be used for hardware interrupts. The rest are available for software interrupts. The address of the IDT is stored in a processor register called the IDTR. We can access this register with the following C

functions:

void lidt(void *base, unsigned int limit) {

unsigned int i[2];

i[0] = limit << 16;

i[1] = (unsigned int) base;

asm ("lidt (%0)": :"p" (((char *) i)+2));

}

 

void *sidt(void) {

unsigned int ptr[2];

asm ("sidt (%0)": :"p" (((char *) ptr)+2));

return (void *) ptr[1];

}

Stack Layout: The stack layout on entry to an C exception handler is different from a normal C function call, as follows:

Location

Value on Exception

Value on Call

%esp+20

EFlags

4th parameter

%esp+16

CS

3rd parameter

%esp+12

EIP

2nd parameter

%esp+8

error code

1st parameter

%esp+4

return value

return value

%esp

old frame pointer

old frame pointer

 

Hardware Interrupts

Hardware interrupts are not very different in behavior. But still, when considering the path traveled by a hardware interrupt from the instant it leaves the hardware device until it reaches the processor, you must take into account the actions taken by the interrupt controller.

When the processor executes an INT instruction, it retrieves the interrupt code from the opcode itself. This code is later used by the processor to index the IVT (interrupt vector table) and find the address of the interrupt handler. On the other hand, when a hardware interrupt is detected, the interrupt controller sends the interrupt code to the processor via the data bus. When the code is finally acquired by the processor (either from the INT opcode or from the interrupt controller) the steps taken are equivalent to those discussed earlier.

Table 1 presents the interrupt vectors occupied by the interrupt controller chips under different operating systems:

OS

Vectors occupied by the master 8259A

Vectors occupied by the slave 8259A

DOS

8h - Fh

70h - 77h

Windows 95 / 98

50h - 57h

58h 5Fh

Windows NT

30h - 37h

38h 3Fh

Table 1 - Interrupt vectors occupied by the 8259A chips

Exceptions

When executing in real-mode, the following exceptions are supported:

Exception

Vector

Condition

Division by Zero

0

Attempting to execute a DIV or an IDIV instruction with a divisor which equals zero.

Debug / Single Step

1

Used in conjunction with the debug registers to indicate a breakpoint hit. The processor also issues this interrupt after executing every instruction when the TRAP flag is set.

Breakpoint

3

The INT 3 instruction generates this exception. Since the opcode of this instruction is only one byte long, it is often used by debuggers to set a breakpoint in application code. The debugger needs only to replace the first byte of the instruction with the INT 3 opcode (11001100b) and wait for the breakpoint exception to occur. After the exception handler resumes execution and before returning to the debugged application, the debugger sets the INT 3 opcode to the previously overwritten byte and issues an IRET (interrupt return) instruction.

Overflow

4

When performing arithmetic instructions with signed operands, the processor set the OF flag to indicate an overflow. The INTO instruction tests this flag and if it is set - generates an exception.

Bounds Check

5

The BOUND instruction is used to verify that an array index does not exceed a certain limit. If the limit is exceeded, an exception is generated.

Invalid Opcode

6

Occurs when the processor executes one of the reserved opcodes or uses the LOCK prefix improperly. Can also indicate an invalid operand following an opcode.

Device Not Available

7

Attempting to execute a floating-point instruction when there is no coprocessor installed. The EM bit of the CR0 register can be cleared to disable this exception.

Double Fault

8

An interrupt occurs which has no corresponding entry in the IVT or a second exception is generated while the processor is executing a previously activated exception handler.

Stack Exception

12

Stack operation exceeds offset FFFFh or a selector pointing to a non-present segment is loaded into SS.

CS, DS, ES, FS, GS Segment Overrun

13

Word memory access at offset FFFFh or an attempt to execute past the end of the code segment.

Floating-Point Error

16

An error with the numeric coprocessor (Divide-by-Zero, Underflow, Overflow...).

Table 2 - Real-Mode exceptions

Other exceptions exist only in Virtual 8086 Mode:

Exception

Vector

Condition

Invalid Task State Segment

10

Indicates that one of the checks made during a task switch failed.

Segment Not Present

11

Loading one of the segment registers with a selector to a segment marked as not-present.

Page Fault

14

Accessing a supervisor page from user privileged code or attempting to access a page which is marked as not-present.

Alignment Check

17

Memory access to an unaligned memory location (can only occur while executing ring 3 code).

Table 3 - Virtual-Mode exceptions

Protected-Mode

Handling interrupts in protected-mode requires setting up a table in memory known as the IDT (Interrupt Descriptor Table). The IDT is somewhat different from the interrupt vector table that exists in real-mode. Instead of being "stuck" at physical address 0, the protected-mode IDT can float around in the linear address space with absolute freedom (although it is possible to change the address of the IVT while in real-mode, it is incompatible with the implementation of the 8086 processor). The linear address of the IDT is determined by a value set into the IDTR register using the LIDT instruction. Each entry in the IDT is 8 bytes long (as opposed to the 4 bytes entries of the IVT) and can contain one of three gate descriptors:

A task gate - Causes a task switch to occur.

An interrupt gate - Control is transferred to the interrupt handler with interrupts disabled.

A trap gate - Control is transferred to the interrupt handler (the interrupt flag remains unchanged).

The following figure presents the internal structure of a trap gate entry:


Figure 1 - Structure of a trap gate entry base on format of an i386 gate descriptor.

P: present bit. Needs to be set for working interrupts.

DPL: should by 0 for exceptions and hardware interrupts, 3 for software interrupts callable by application.

Offset in Target Segment: address of interrupt handling routine.

Target Segment Selector: code segment to run interrupt handler in.

Trap and interrupt gates have much in common since they are both used for control transfers between different privilege levels. The selector and offset fields specified in these gate descriptors are used to locate and execute the interrupt handler code. A control transfer is only permitted to a code residing in the same or a higher privilege level but never to a lower privileged one.

Software Interrupts

Software interrupts are still widely used by protected-mode operating systems in order to publish their internal services to the external world. However, most of these interfaces are now wrapped by functions and cannot be accessed directly (although it's possible, it's not recommended). For instance, Windows NT uses the undocumented interrupt 2Eh to allow control transfers between its user mode and kernel mode components. Whenever a non-privileged Windows application issues a call to an API function, the system routes the call until it reaches an INT 2Eh instruction. The IDT gate corresponding to this interrupt points to a handler inside ntoskrnl, which performs the required privileged operations on behalf of the application. Windows 95 and 98 use a similar technique to hop between user and kernel mode, but rely upon INT 30h instead of using INT 2Eh.

Hardware Interrupts

Hardware interrupts in protected-mode behave in the same manner as described in the real-mode section. The only major difference is that when executing in protected-mode, the processor consults its IDT rather than searching the real-mode IVT for the address of the interrupt handler.

Exceptions

Most of the information that was presented in the previous sections, applies to protected-mode as well. However, exception 13 (General Protection Fault) has a wider meaning in protected-mode and can indicate additional conditions than those defined for real-mode.