4. Minix 3 Notes

4.1 Disabling the Default Interrupt Handler

When Minix 3 boots, its terminal driver configures the KBC and installs its own keyboard interrupt handler. This way, users are able to login to Minix and use the different virtual terminals. On one hand this is great: you do not need to configure the KBC. On the other hand this raises an issue: how can your code read the scancodes? Indeed, when the user presses/releases a key, the KBC will generate an interrupt, and the KBC interrupt handler of the terminal driver will execute and read the scancode, and possibly echo a character in the terminal.

A solution to this issue, is for your driver to subscribe the KBC interrupts, as described in Section 5.3 of Lab 2. However, now it should specify not only the IRQ_REENABLE policy but also the IRQ_EXCLUSIVE policy. As a result, the standard Minix 3 KBC interrupt handler will not be notified of the occurrence of KBC interrupts, thus preventing it from interfering with your code.

Note that the keyboard's IRQ number is 1.

IMP.: To ensure that you can use Minix 3 virtual terminals once your program is done, your code must cancel its subscription of the KBC interrupt before exiting, by calling the sys_irqrmpolicy() kernel call.

4.2 Measuring Time

Both the KBC and the keyboard may take some time to respond to a command. For example, IBM's specification of the i8042 requires the keyboard to respond to a command in 20 ms. Thus your code should not expect to get a response immediately after issuing a command. A simple approach is for your code to wait indefinitely, or until the KBC reports a time-out. A more fault-tolerant approach is for your code to give enough-time for the KBC or the keyboard to respond, retry a few times on time-out, and finally give up. Given that the time intervals to consider are in the order of tens of ms, the sleep() function is not useful, because it is able to measures time intervals whose duration is a multiple of a second. Instead, you can use the function tickdelay() of Minix 3's libsys as follows:

#include <minix/sysutil.h> #define DELAY_US 20000 tickdelay(micros_to_ticks(DELAY_US);

This function is similar to sleep() in that it blocks the process that executes it for the time interval specified in its argument. After that time interval, the process resumes execution by executing the instruction that follows tickdelay().

4.3 Assembly Programming in Minix

Minix 3 can use the GNU build system. Therefore, you can write your assembly code using the GNU assembler (GAS) assembly language (AT&T's syntax). You should also be able to use the so-called Intel's syntax, which is supported since version 2.10. Indeed, the version of GAS in the Minix version we are using is version 2.17. For that you must use the .intel_syntax directive. If you wish you can give it a try, but be warned that this syntax appears to be poorly documented (so you may find it useful this link to the GAS section of the Linux Assembly HOWTO).

For an example of a simple assembly function in AT&T's syntax, check pg. 18 of the class notes on Mixed C and Assembly Programming.

4.4 Executing I/O Directly (in Assembly)

Minix 3 device drivers, as your code, although privileged execute at user level. In Minix 3, and in almost all OS's, by default, user level programs are not allowed to execute I/O sensitive operations, such as IN, OUT, STI or CLI. Therefore, if your interrupt handler issues any of these instructions, the kernel will kill it as a result of a general protection exception.

However, the IA-32 ISA provides a mechanism that allows some processes to execute those operations even when running at user-level. This is based on the use of the IOPL (I/O Privilege Level) field of the Extended Flags (EF) register. This field specifies the minimum current privilege level of a process when it executes those operations. If the current privilege level is lower (meaning that it has more privileges) than that specified in that field of the EF register, then the process will be allowed to perform I/O sensitive operations.

Minix 3 offers a kernel call sys_enable_iop() that allows the IOPL of a process to be set to a value such that it is able to execute I/O sensitive operations, even though it executes at user level. The following code segment illustrates how this can be done.

sef_startup(); /* Enable IO-sensitive operations for ourselves */ sys_enable_iop(SELF);