Exceptions and Interrupts How does Linux handle servicerequests from the cpu and from the peripheral devices? The ‘fetch-execute’ cycle Normal programming assumes this ‘cycle’: • 1) Fetch the next instruction from ram • 2) Interpret the instruction just fetched • 3) Execute this instruction as decoded • 4) Advance the cpu instruction-pointer • 5) Go back to step 1 But ‘departures’ may occur • Circumstances may arise under which it would be inappropriate for the cpu to proceed with this fetch-execute cycle • Examples: • An ‘external device’ may ask for service • An interpreted instruction may be ‘illegal’ • An instruction ‘trap’ may have been set Faults • If the cpu detects that an instruction it has just decoded would be illegal to execute, it cannot proceed with the fetch-execute cycle • This type of situation is known as a ‘fault’ • It is detected BEFORE incrementing the IP • The cpu will react by: 1) saving some info on its stack, then 2) switching control to a special fault-handling routine Fault-Handling • • • • • • • The causes of ‘faults’ may often be fixed A few examples: 1) Writing to a ‘read-only’ segment 2) Reading from a ‘not present’ segment 3) Executing an out-of-bounds instruction 4) Executing a ‘privileged’ instruction If the problem can be remedied, the cpu can resume executing from where it left off Traps • The cpu may have been programmed to automatically switch control to a ‘debugger’ Program after it has executed an instruction • This type of situation is known as a ‘trap’ • It is activated AFTER incrementing the IP • It is accomplished by setting the TF flag • Just as with faults, the cpu will react: save return-info, and jump to trap-handler Faults versus Traps Both ‘faults’ and ‘traps’ occur at points within a computer program which are ‘predictible’ (i.e., triggered by pre-planned instructions), so they are ‘in sync’ with the program (and thus are called ‘synchronous’ interruptions in the normal fetch-execute cycle) The cpu responds in a similar way to faults and to traps – yet what gets saved differs! Faults vs Traps (continued) • With a ‘fault’: the saved address is for the instruction that was the cause of the fault – so that instruction will get re-fetched after the cause of the problem has been fixed • With a ‘trap’: the saved address is for the instruction following the one which triggered the trap Synchronous vs Asynchronous • Devices which are ‘external’ to the cpu may under go certain changes-of-state that the system needs to take notice of • These changes occur independently of what the cpu is doing, and so cannot be predicted from reading a program’s code • They are ‘asynchronous’ to the program, and are known as ‘interrupts’ Interrupt Handling • As with faults and traps, the cpu responds to interrupt-requests by saving some info on the stack and then jumping to a special ‘interrupt-handler’ routine designed to take appropriate action for the particular device which caused the interrupt to occur The ‘Interrupt Controller’ • Special hardware is responsible for telling the cpu when an external device wants to ‘interrupt’ the current program • This hardware is the ‘Interrupt Controller’ • It needs to let the cpu know which among several devices is the one needing attention • It also needs to prioritize multiple requests Cascaded 8259 PICs CPU INTR INTA Master PIC IRQ0 IRQ1 IRQ2 IRQ3 IRQ4 IRQ5 IRQ6 IRQ7 Slave PIC PC Design (for single-processor systems) IRQ8 IRQ9 IRQ10 IRQ11 IRQ12 IRQ13 IRQ14 IRQ15 Two Crucial Data Tables • The Global Descriptor Table (GDT) defines memory segments, and enforces their access-privileges (by using ‘segment descriptors’) • The Interrupt Descriptor Table (IDT) defines entry-points for the code-routines which handle ‘interrupts’ and ‘exceptions’ How does CPU find GDT/IDT? • Two dedicated registers: GDTR and IDTR • Both have identical 48-bit format: Segment Base Address 47 Segment Limit 16 15 0 Kernel must setup these registers during system startup (set-and-forget) Privileged instructions: LGDT and LIDT used to set these register-values Unprivileged instructions: SGDT and SIDT used for reading register-values Segment-Descriptor Format 63 56 39 Limit [19..16] Base[31…24] Access attributes Base[ 15 … 0 ] 31 32 Base[23…16] Limit[ 15 ... 0 ] 16 15 Quadword (64-bits) 0 Gate-Descriptor Format 63 32 Entrypoint Offset[ 31…16 ] Code-segment Selector Gate type code (Reserved) Entrypoint Offset[ 15…0 ] 0 31 Quadword (64-bits) Intel-Reserved ID-Numbers • Of the 256 possible interrupt ID-numbers, Intel reserves the first 32 for ‘exceptions’ • Operating systems such as Linux are free to use the remaing 224 available interrupt ID-numbers for their own purposes (e.g., for service-requests from external devices, or for other purposes such as system-calls Some Intel-defined exceptions • • • • • • • • • • • • • • • • 0: divide-overflow fault 1: debug traps and faults 2: non-maskable interrupts 3: debug breakpoint trap 4: INTO detected overflow 5: BOUND range exceeded 6: Undefined Opcode 7: Coprocessor Not Available 8: Double-Fault 9: (reserved) 10: Invalid Task-State Segment 11: Segment-Not-Present fault 12: Stack fault 13: General Protection Exception 14: Page-Fault Exception 15: (reserved) Error-Codes • A few of the Intel-defined exceptions also push a diagnostic ‘error-code’ onto the cpu stack (in addition to pushing the EFLAGS, CS and EIP register-values); the ‘handler’ must discard that error-code before it can execute the ‘iret’ instruction to resume the program that got interrupted (Exceptions 8 and 10 through 14 generate error-codes) Using our ‘physmem’ driver We can look at the kernel’s GDT/IDT tables 1) We can find them (using ‘sgdt’ and ‘sidt’) 2) We can ‘read’ them by using ‘physmem’ Demo program on course website: showidt.cpp It prints out the 256 IDT Gate-Descriptors In-Class Exercise • Write a similar program (showgdt.cpp) • Two ‘big’ issues: size of GDT isn’t known in advance (whereas the IDT is of a known size) labeling for GDT entries is by offset (whereas the IDT entries use index) • There could be some ‘other’ issues as well Some Questions • How many “non-null” descriptors in GDT? (There’s always at least one that’s “null”) • What kinds of descriptors does Linux use? Sizes? Access-rights? Code/Data? TSS?