introduction to interrupts how we can intervene in the cpu’s interrupt-handling mechanism (in...

28
Introduction to Interrupts How we can intervene in the CPU’s interrupt- handling mechanism (in real-mode)

Post on 20-Dec-2015

224 views

Category:

Documents


0 download

TRANSCRIPT

Introduction to Interrupts

How we can intervene in the CPU’s interrupt-handling

mechanism (in real-mode)

Recall ‘fetch-execute’ cycle

• The processor repeatedly performs the following sequence of actions:– Fetch the next instruction (at address CS:IP)– Advance the instruction-pointer register (IP)– Decode the just-fetched instruction– Execute the just-decoded operation

• But devices may interrupt this cycle (by raising a voltage on the INTR input-pin)

External Interrupt Requests

CPU

SMI#

NMI

INTRIF-bit

EFLAGS

As soon as the CPU has completed execution one instruction(and before it starts to execute the next one), it will sample the voltages on these input-pins. If it detects a signal on the INTRline, and if the IF-bit is set to 1 in the FLAGS register, then theCPU will defer executing the next instruction, and instead will transfer control to an Interrupt Service Routine (ISR).

Response to INTR

• These are the actions taken by the CPU if it detects an INTR signal (while IF=1):– Push the FLAGS register onto the stack– Clear IF and TF bits in the FLAGS register– Push the CS and IP registers onto the stack– Query the PIC to obtain the IRQ ID-number– Lookup the corresponding Interrupt-Vector– Load CS and IP with the vector’s values

Interrupt Vector Table (IVT)

0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x070x080x100x180x200x280x300x380x400x480x500x580x600x680x70

0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F0x11 0x12 0x13 0x14 0x15 0x16 0x170x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F0x21 0x22 0x23 0x24 0x25 0x26 0x270x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F0x31 0x32 0x33 0x34 0x35 0x36 0x370x39 0x3A 0x3B 0x3C 0x3D 0x3E 0x3F0x41 0x42 0x430x49

Here is the User Timer-Tick Interrupt-Vector(it’s the one we will temporarily modify to point at our own “custom” Interrupt Service Routine

0x78

An Interrupt Vector’s layout

OFFSET SEGMENT

Byte 0 Byte 1 Byte 2 Byte 3

Each vector is a doubleword (4 bytes)

Least significant word goes into register IP

Most significant word goes into register CS

Conditions on entering an ISR

• Further device-interrupts are disabled

• On the stack are saved IP, CS and FLAGS

• All the other CPU registers are unmodified

• The suspended program can be resumed, at the point where it was interrupted, using the CPU’s special ‘iret’ instruction

• ‘iret’ pops the three topmost words off the stack and back into IP, CS, and FLAGS

Preserving interrupted context

• It is vital for an interrupted program to be able to resume with exactly the same CPU state it had before being suspended

• The CPU saves only a ‘minimal’ amount of the program-context (FLAGS, CS, and IP)

• It’s a responsibility of the ISR programmer to save and restore any other registers it may be necessary to modify within the ISR

The IBM-PC ROM-BIOS

• When the IBM-PC was introduced (1981), the full source-code for its ROM-BIOS was published (as a Technical Reference)

• That code was written entirely in assembly language (for the 8086 processor)

• It was approximately 5,400 lines in length

• It included Interrupt Service Routines for each of the standard peripheral devices

An ISR example

• One of the standard peripheral devices is the Programmable Interval-Timer (PIT)

• During the Power-On Self-Test, the timer is programmed to generate an interrupt at regular intervals (approximately 18.2 times per second)

• These ‘timer-tick’ interrupts are used for timekeeping purposes by the ROM-BIOS

Four timer-tick actions

• By studying the IBM source-code, we see that four actions are performed each time the PIT generates a ‘timer-tick’ interrupt:– The double-word counter (located at 40:6C)

gets incremented; if it reaches its maximum, it’s reset to zero and a ‘rollover’ flag is set.

– The motor_wait counter (byte at 40:40) gets decremented; when it reaches 0, the floppy disk-drive motors, if on, are turned off

Timer ISR (continued)

• A software interrupt (INT-0x1C) executes, to allow applications an easy method for implementing customized timing activities

• A EOI-command (End-Of-Interrupt) is sent to the system’s Programmable Interrupt Controller (PIC) informing it that it’s now ok to activate requests for interrupts from the peripheral devices

An In-Class Exercise

• We want to experiment with ‘hooking’ the user timer-tick interrupt (INT-0x1C)

• By default the IVT contains an interrupt vector for INT-0x1C pointing to a ‘dummy’ interrupt service routine (just an ‘iret’)

• We can overwrite this IVT entry with our own entry, a vector that points to a custom service-routine that performs some action we have designed (e.g., show time-of-day)

The bootsector ‘squeeze’

• As we know, there isn’t much room for us to include much custom code in a diskette boot-sector (only 512 bytes total)

• But we CAN put code into a bootsector that will load lots more code, from other diskette-sectors, into the physical ram

• This is how an operating system boots up

• We can call the BIOS to read disk-sectors

INT-0x13 services

• Six service-routines for floppy diskettes:– #0: reset the floppy diskette controller– #1: return status of diskette controller– #2: read sector(s) from floppy diskette– #3: write sector(s) to floppy diskette– #4: verify sector(s) on floppy diskette– #5: format a track on a floppy diskette

• More details on Ralf Brown’s Interrupt List

1440KB Diskette organization

Sectors (18 per track)Tracks (80 per head)

Heads (2 per disk)

Sector-size(512 bytes)

Terminology and Numbering

• The Disk-Controller locates its data-blocks using a so-called CHR coordinate-system– Cylinders: 0..79– Heads: 0..1– Records: 1..18

• The floppy drives are numbered 0, 1, 2, …

How to read from diskette

• INT-0x13, function 2 (Read sectors):• ES:BX = destination of the data-transfer• DL = disk-drive number (0, 1, … )• CH = cylinder-number: 0, 1, … , 79• DH = head-number: 0, 1• CL = record-number: 1, 2, … , 18• AL = record-count: 1, 2, … , 18• AH = function-number: 0x02

Demo ‘trackldr.s’

• This is a bootsector program that loads the remaining seventeen diskette sectors from track that contains the boot-sector

• These sectors are placed consecutively in memory, starting from address 0x10000

• This loader checks for a special signature (0xABCD) before making a far-call to the designated entry-point (at 1000:0002):

callf #0x0002, #0x1000

The ‘usertick.s’ demo

• The ‘trackldr.s’ loader is assembled and installed as the diskette’s ‘boot’ sector:– $ as86 trackldr.s –b trackldr.b– $ dd if=trackldr.b of=/dev/fd0

• This program is assembled and installed as the second sector on the floppy disk– $ as86 usertick.s –b usertick.b– $ dd if=usertick.b of=/dev/fd0 seek=1

What ‘usertick.s’ does

• Copies interrupt-vector 0x1C to save-area

• Installs new entry as interrupt-vector 0x1C

• Waits for the user to press any key

• While waiting, the current time-of-day gets displayed on the screen’s top-right corner

• When any key is hit, the original interrupt-vector 0x1C is restored to the IVT, and control returns to the ‘trackldr.s’ program

How time-of-day is found

• The ROM-BIOS DATA-AREA maintains a count of the number of timer-ticks since the day began (at midnight)

• Divide ticks by 18.2 (ticks-per-second) to get the number of seconds since midnight

• Divide seconds by 60 to get minutes today• Divide minutes by 60 to get hours today• Divide hours by 12 to get halfdays today• Remainder (0 or 1) will tell us AM or PM

Drawing the string to VRAM

• string-format: “ hh:mm:ss xm “ (13 chars)

• Screen’s memory-segment: 0xB800

• Cell’s offset (after top-right corner) = 80 * 2

• String’s starting offset: (80-13) * 2 = 134

Videoscreen

80 columns

25rows

Modifying our ISR

• We can modify the actions in our interrupt service routine so as to display a different item of timing information

• Instead of displaying the time-of-day, we could show how much time has elapsed since our computer was last restarted

• To do this, we would need to access the Pentium’s TimeStamp Counter register

Time Stamp Counter

• The Time Stamp Counter (TSC) is a 64-bit register located, inside the CPU, and reset to zero when the power is first turned on

• The TSC is automatically incremented with each CPU clock-cycle

• Our CPUs operate at 2.4 GHz

• We can compute the time since Power-On if we divide the TSC value by 2.4 billion

The RDTSC instruction

• A special CPU instruction is used to read the Time Stamp Counter’s current value

• The name of that instruction is ‘rdtsc’

• Our ‘as86’ assembler doesn’t recognize this mnemonic opcode (it’s too new)

• But we can still use it, by including its machine-code in our instruction-stream:

.BYTE 0x0F, 0x31 ; RDTSC

How RDTSC works

• When the CPU executes ‘rdtsc’, the value from register TSC (64-bits) is copied into the EDX:EAX register-pair

• So if we want to divide by 2,400,000,000, we can then use this code-fragment:

mov ecx, #2400000000 ; setup divisor

div ecx ; do division

; quotient is now in EAX, regainder in EDX

Exercise

• Revise the ‘usertick.s’ program so that it will display how long it’s been since your computer was started, like this:

hh:mm:ss since power-on