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.