Your first milestone is fairly straightfoward, and should only take you a few hours to complete. However, you should use this as an opportunity to get to used working with your partner, and probably work out exactly how you can work together so you don't end up duplicating work, or worse still not completing essential parts of the project.
By now you should have got yourself in a group and you should have a group account setup for you. We recommend that you use Mercurial (or another version control system of your choice) to maintain your source code, (this isn't a requirement, just a suggestion), and that a repository be setup in your group account with the correct permissions and sticky bits set so that you can both access it.
If you are using Mercurial, we recommend you set up the meld (or equivalent) merging tool as the default merge program to avoid painful merges.
The aim of this milestone is to design and implement device driver to read the time. You should add a file for the timer implementation and modify the main system call loop to handle timer interrupts.
For many purposes, such as benchmarking, a high clock resolution is desired. The NSLU2/IXP420 platform features a timer cell which contains several high-frequency counters which are used as timers. Using the timer you implement in this milestone, you will be measuring the performance of the file system that you implement in milestone 6.
Your driver needs to export the interface specified in libs/sel4/libclock/include/clock.h
. There are the
following functions:
The IPC message sent by the timer contains in MR0 a return code (CLOCK_R_OK, CLOCK_R_UINT, CLOCK_R_CNCL) and in MR1 and MR2 the present value of the real-time clock.
The interface is just an internal function call interface. You do not
need to export this interface to the users. User programs will indirectly
access the clock driver through the time
and
sleep
syscalls (implemented in a later milestone).
NOTE: After registering an interrupt, you must call
seL4_IRQHandler_Ack
.
This is a block diagram of the IXP420 network controller on which the slug is based. The IXP420 is a single chip computer which has an XScale processor and lots of dedicated hardware for I/O. The platform hardware reference manual can be found in [1].
Although the Slug is a very powerful platform with lots of functionality we will only be doing a driver for the 'timers' cell on the "Advanced Peripheral Bus"(APB). If you are interested in the full arcana of device drivers for the other cells particularly the networking stacks then explore the directories libs/sel4/libixp_osal and libs/sel4/libixp400_xscale_sw, but be warned it is really, really hard to read and understand code.
Your main job is to learn how to program the IXP420's timer cell (OSTS) to generate timer interrupts and how to write a seL4 driver to handle these interrupts.
The OSTS contains four functions, 2 general purpose 32 bit timers, a 32
bit time counter and an emergency watchdog timer. For our purposes, we
are only interested in the OSTS as our source of timer interrupts. You
will be using one of the general purpose timers and the timestamp
register. The interrupt vectors for the timers are connected to the 5, 11
& 14 interrupt lines, see libs/sel4/libclock/include/nslu2.h
.
There is an abundance of different devices and writing device drivers is usually seen as a very difficult task. This is true in a sense. However, programming a device is really just a matter of learning about its registers, what values to read and write to those registers and when to do it.
The minimal subset of the OSTS's functionality that you must understand and use is listed below (the numbers in parentheses are the offsets of the register addresses, in bytes, from the OSTS's base address). Refer to Chapter 14 of the developer's manual for more complete descriptions.
Again, this is only the minimal understanding that you need. For deeper understanding, you are encouraged to learn about the other registers and can even play with those as well.
NOTE: This section is deliberately kept short (e.g., we do not dictate which timer to use or in what mode to use it in). The idea is that you work things out for yourself and make your own design and implementation decisions. There are only two conditions that must be satisfied:
For this project you have been supplied with an extensive set of skeleton code to help you along the way. This code is intended as an implementation guide, not as a 'black-box' library.
It is important that you fully understand all provided code that you use. You are encouraged to modify and improve the supplied code, though for your own sanity avoid libs/ixp_* as they are Intel's incredibly complex networking libaries. For the purposes of assessment, we treat any supplied code that you call as your code and as such you may be asked to describe how it works.
Now might be a good time to get familiar with the resources, e.g. the framework documentation
The seL4/ARM kernel exports specific interrupts to a user level
interrupt handler via asynchronous notification.
You will need to use the om_server interface in order to map in the timer device and manage
interrupts. This interface provides high level memory and capability management on top of the
seL4 interface, and you must use it over the seL4 functions. Do not try to use seL4_IRQ_Control_Get
.
It is recommended that you read the om_server documentation (section 4.1.4.34), or look at the source in
libs/sel4/libom_server
. Additionally, you should read Chapter 5 of the sel4 documentation to gain an understanding of TCBs, and Section 7.1 to understand how interrupts are delivered.
In seL4/ARM, device registers are memory mapped. That is, hardware registers can be accessed via normal load/store operations to special addresses. To access device registers, you must first map the memory with the appropriate attributes, see Section 4.1.4.11-15 in the om_server reference manual.
You may need to resolve some or all of these issues:
You should be able to show some test code that uses all the functions specified in the driver interface. Specifically set up and demonstrate:
time_stamp
every time this interrupt is received. This should not use register_timer
, as it will later be used to call a function from the NFS library to keep your file system working correctly.register_timer
. Make
sure the delay used is long enough such that the system call loop is entered before these wake up.
As register_timer
does not IPC any threads yet, we should see print outs of the timestamp as
each delay expires. This part is only as a demo, and can be removed after the milestone demonstration. You will later use register_timer
to implement a sleep
system call for your user-level processes.