My Operating system Development Experience and understanding -Part 05
Hell developers…!
in this article we are going to understand about Interrupts and inputs in OS development. Before that if you don’t read my previous OS development Articles please go and check those from below links
- My Operating system Development Experience and understanding. Part 01 -Setting Up environments
- My Operating system Development Experience and understanding -Part 02- C instead of assembly
- My Operating system Development Experience and understanding -Part 03-implement Drivers
- My Operating system Development Experience and understanding -Part 04-Segmentation in OS development
let get into todays Topics
Interrupts and Input
It would be good if the OS could also receive input now that it can make output. (In order to read information from the keyboard, the operating system must be able to handle interrupts.) When a hardware device, such as a keyboard, serial port, or timer, tells the CPU that its status has changed, an interrupt occurs. Interrupts can also be sent by the CPU due to program errors, such as when a program refers to memory it doesn’t have access to or divides a value by zero. Finally, software interrupts, which are interrupts caused by the int assembly code instruction and are frequently used for system calls.
Interrupts
Interrupts are signals sent by a device to the CPU, such as a keyboard or a hard drive, telling it to stop what it’s doing and start doing something different. A keyboard controller, for example, can transmit an interrupt when a character key is pressed. The OS can then display the character on screen right away, even if the CPU was previously engaged in something completely unrelated, and then return to its previous task. When a specific interrupt occurs, the CPU consults a table given by the operating system to find an entry for that specific interrupt.
Types of Interrupts
There are generally three classes of interrupts on most platforms
- Exceptions: These are created by the CPU internally and used to notify the operating kernel of an occurrence or circumstance that requires its attention. Exception circumstances on x86 CPUs include Double Fault, Page Fault, General Protection Fault, and so on.
- Interrupt Request (IRQ) or Hardware Interrupt :This type of interrupt is generated externally by the chipset, and it is signaled by latching onto the #INTR pin or equivalent signal of the CPU in question. There are two types of IRQs in common use today.
IRQ Lines, or Pin-based IRQs
Message Signaled Interrupts
- Software Interrupt: This is an interrupt is a signal sent by software executing on a CPU to the kernel, indicating that it requires attention. Interrupts of this sort are commonly utilized for System Calls. The “INT” instruction is used to initiate a software interrupt on x86 CPUs. Because the x86 CPU can use any of the 256 interrupt vectors available for software interrupts, kernels usually pick one. On x86-based platforms, for example, many modern Unices use vector 0x80.
- Interrupts Handlers
The Interrupt Descriptor Table is used to handle interrupts (IDT). For each interrupt, the IDT specifies a handler. The interrupts are numbered (0–255), and the handler for interrupt “i”is defined at the table’s “ith”position. Interrupt handlers are divided into three categories:
Task handler
Interrupt handler
Trap handler
The task handlers make use of Intel-specific x86 features. The only difference between an interrupt handler and a trap handler is that an interrupt handler suppresses interruptions, so you can’t get an interrupt while handling one. In this development process, we’ll learn how to use trap handlers and manually disable interrupts as necessary.
-Creating an Entry in the IDT
An entry in the IDT for an interrupt handler consists of 64 bits. The highest 32 bits are shown in the figure below:
The lowest 32 bits are presented in the following figure:
A description for each name can be found in the table below:
The offset is a code pointer (preferably an assembly code label). For example, the following two bytes might be used to generate an entry for a handler whose code begins at 0xDEADBEEF and operates in privilege level 0 (therefore using the same code segment selector as the kernel):
0xDEAD8E00
0x0008BEEF
If the IDT is represented as an unsigned integer idt[512] then to register the above example as an handler for interrupt 0 (divide-by-zero), the following code would be used:
idt[0] = 0xDEAD8E00
idt[1] = 0x0008BEEF
As written in the chapter “Getting to C”, we recommend that you instead of using bytes (or unsigned integers) use packed structures to make the code more readable.
Handling an Interrupt
When an interrupt occurs, the CPU will push some interrupt information onto the stack, then check up and go to the proper interrupt handler in the IDT. At the moment of the interrupt, the stack will look like this:
[esp + 12] eflags
[esp + 8] cs
[esp + 4] eip
[esp] error code?
Because not all interrupts generate an error code, the error code is preceded by a question mark. 8, 10, 11, 12, 13, 14, and 17 are the specific CPU interrupts that cause an error code to be added to the stack. The interrupt handler can use the error code to find out more about what went wrong. It’s also worth noting that the interrupt number isn’t moved to the top of the stack. We can only tell what interrupt has happened by looking at the code that is running — if the handler for interrupt 17 is running, then interrupt 17 has happened.
When the interrupt handler is finished, it returns with the iret instruction. The stack should be the same as it was at the moment of the interrupt, according to the command iret . As a result, any values that the interrupt handler has pushed onto the stack must be popped. Before returning, iret pops the value from the stack to restore eflags, and then jumps to cs:eip as defined by the values on the stack.
Because all registers used by interrupt handlers must be preserved by pushing them onto the stack, the interrupt handler must be written in assembly code. This is because the interrupted code is unaware of the interruption and hence expects its registers to remain unchanged. It will be tedious to write all of the interrupt handler functionality in assembly code. It’s a good idea to write an assembly handler that saves the registers, runs a C function, restores the registers, and then executes iret.
The status of the registers, the state of the stack, and the number of the interrupt should all be passed to the C handler as arguments.
following code say how I created C handler
in interrupt.h
Creating a Generic Interrupt Handler
It’s a little tough to design a generic interrupt handler because the CPU doesn’t push the interrupt number into the stack. This section will demonstrate how to do it with macros. It’s easier to use NASM’s macro functionality instead of writing one version for each interrupt. Because not all interruptions generate an error code, the value 0 will be used as the “error code” for interrupts that do not generate one. this can be done is shown in the code below:
create a file call Interrupt_handlers.s
The common_interrupt_handler does the following:
Push the registers on the stack.
Call the C function interrupt_handler.
Pop the registers from the stack.
Add 8 to esp (because of the error code and the interrupt number pushed earlier).
Execute iret to return to the interrupted code.
Since the macros declare global labels the addresses of the interrupt handlers can be accessed from C or assembly code when creating the IDT.
Interrupt.c (accessing interrupt)
Loading the IDT
The IDT is loaded with the lidt assembly code instruction which takes the address of the first element in the table. It is easiest to wrap this instruction and use it from C:
Programmable Interrupt Controller (PIC)
On most systems, there are two PICs, each with eight separate inputs and one output signal that informs the CPU that an IRQ has occurred. Because the slave PIC’s output signal is coupled to the master PIC’s third input (input #2), when the slave PIC wants to inform the CPU of an interrupt, it actually informs the master PIC, who then informs the CPU. This is referred to as “cascade.” IRQ 2 cannot occur because the master PIC’s third input is configured for this rather than as a standard IRQ.
In the beginning there was only one PIC and eight interrupts. As more hardware were added, 8 interrupts were too few. The solution chosen was to chain on another PIC on the first PIC
Every interrupt from the PIC has to be acknowledged — that is, sending a message to the PIC confirming that the interrupt has been handled. If this isn’t done the PIC won’t generate any more interrupts.
Acknowledging a PIC interrupt is done by sending the byte 0x20 to the PIC that raised the interrupt. Implementing a pic_acknowledge function can thus be done as follows:
Reading Input from the Keyboard
When a key is pressed, the keyboard controller sends an interrupt signal to a device called the Programmable Interrupt Controller, or PIC. IRQ #1 is the keyboard interrupt because of the keyboard and PIC connections, therefore when a key is hit, IRQ 1 is transmitted to the PIC. The PIC’s job will be to determine whether the CPU should be notified of an IRQ immediately or not, as well as to convert the IRQ number into an interrupt vector (a number between 0 and 255) for the CPU’s table.
The OS is supposed to handle the interrupt by talking to the keyboard via in and out instructions (or inportb/outportb, inportw/outportw, and inportd/outportd in C), asking what key was pressed, doing something about it (such as displaying the key on the screen and notifying the current application that a key has been pressed), and then returning to whatever code was executed. Any additional IRQs from the keyboard will be blocked if the key is not read from the buffer.
The keyboard does not generate ASCII characters, it generates scan codes. A scan code represents a button — both presses and releases. The scan code representing the just pressed button can be read from the keyboard’s data I/O port which has address 0x60
The next step is to write a function that translates a scan code to the corresponding ASCII character
all the thing implement by following C code
Keyboard.c file:
today we successfully created PIC and keyboard interrupts in next OS development article we will cover user_modes. and now thanks for Reading this article for your development process See you all next time
thanks for reading
Abdullah M.R.M
Resources
http://wiki.osdev.org
https://github.com/tuhdo/os01
https://0xax.gitbooks.io/linux-insides/content/
https://intermezzos.github.io/
http://os.phil-opp.com/
http://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf