CS452 - Real-Time Programming - Fall 2008
Lecture 6 - Kernel, Tasks
Questions & Comments
- Bill Cowan's easy method versus inline assembly.
Tasks
Kernel maintains a task descriptor (TD) for each created task. That is, to
create a task the kernel must allocate a TD and initialize it. The TD normall
contains
- The task's stack pointer, which points to a private stack containing
- PC
- other registers
- local variables
all ready to be reloaded whenever the task next runs.
- Possibly the return value for when the task is next activated
- The task's parent
- The task's state
Possible states of the task
- Active: running or about to run
- Ready: can run if scheduled
- Blocked: waiting for something to happen
Diagram
Hardware Facts for Eos
Memory Map
- 0x000000 to 0x09FFFF: low memory (size is 640K)
- 0x0A0000 to 0x0FFFFF: VGA, etc (size is 360K)
- 0x100000 to 0x1XXXXF: your kernel (size is determined by you)
- (Ox1XXXXF + 0x1) to 0x1FFFFF: free memory for tasks (size is the
remainder of 2 Mbyte)
Devices
Accessed by special I/O instructions
Architecture of X86
General Purpose Registers
- E[A-D]X: general purpose registers
- ESP: the stack pointer
- EBP: the base pointer
- ESI, EDI: used for string copying
All these registers have special names for the low 16 bits. (drop the E),
and for the low 8 bits (add an L), and for the second 8 bits (add an H)
Special Purpose Registers
- IDTR: interrupt descriptor table
- GDTR: global descriptor table
- the GDTR is 48 bits, divided [--16--|---32---]
- 16 bit part is limit, the size of the table
- 32 bit part is base, the start of the table
- write using
lgdt [gdtr],where idtr is a pointer a
struct containing an interrupt descriptor
- read using
sidt
- CS, DS, ES, FS, GS, SS: segment registers
- EIP: program counter
Addressing Memory
Physical address is
- start of segment + offset
Helps to provide
- position independent (relocatable) code
- memory protection
- program owns segment(s)
- segment has low and high limits
- program can't access outside the limits
Six segment registers
- CS
- DS
- ES
- FS
- GS
- SS
Each segment register is an index into a Global Descriptor Table (GDT),
which contains
- low limit
- high limit
- etc.
Index is actually contents of register >> 3.
Setting up a Task
Every task requires at least two GDTs
- accessed through CS and DS
- This includes the kernel
The compiler assumes that DS = SS
- That is, the stack is in the data segment.
Set DS = ES = FS = GS = SS for each task
There is no GDT in place when the kernel boots
How is this done
- The address of the table is kept in the special register GDTR
- lgdt sets GDTR; sgdt reads GDTR
Yet another register EFLAGS contains processor state such as
- condition codes
- what is enabled
- Getting started you want to have interrupts disabled!
Setting up a task
- CS points to code in ELF executable
- DS points to memory you allocate for the task.
Context Switch in the 386
int <vector>
- pushes EFLAGS, CS, EIP onto the active task's stack
- finds entry
<vector> in the interrupt descriptor
table (IDT)
- jumps to the address in the
IDT[<vector>]
iretl
- pops EFLAGS, CS, EIP from the active task's stack
- puts them in the registers
Task to kernel
int <vector> in task
pushal saves all the active task's registers on the active
task's stack
- switch to kernel stack
- you got CS, EIP from IDT
- ESP from your magic place
- DS from the kernel variables
- restore remainder of kernel state from stack
Kernel to task
- save kernel state on kernel stack
- switch to active task's stack
- restore general purpose registers:
popal
iretl gets EFLAGS, CS, EIP from the task stack.
Getting the Kernel Started
Each task is a C function
So is the kernel
Each gets compiled and linked into an ELF executable
- ELF = Executable and Linking Format
Then 452-post.py
(/u/cs452/public/tools/bin/452-post.py) binds the ELFs together
to be downloaded.
Downloading
Before starting to execute the kernel needs to have
- A stack, on which it can place data, with the stack pointer set.
- Its first instruction in the PC
The boot loader does this. How?
- The bound executable obeys the multiboot specification
- The kernel is called by
int main( unsigned long magic,
multiboot_info_t *mbiaddr )
- The stack starts as
mbiaddr, magic
multiboot_info_t is a struct with the following
interesting fields
typedef struct multiboot_info {
...
unsigned long mods_count;
unsigned long mods_addr;
...
} multiboot_info_t
The second is a pointer to module_t, the first module
record.
- module_t is a struct with the following interesting fields
typedef struct module {
unsigned long mod_start;
unsigned long mod_end;
unsigned long string;
...
} module_t
You might find this
URL has some interesting information (and links) if you are really
interested in how executables are structured, or in the multiboot boot
process.
Return to: