The kernel is just a function like any other, but which runs forever.
kernel( ) {
initialize( ); // includes starting the first user task
FOREVER {
request = getNextRequest( );
handle( request );
}
}
Where is the OS?
All the interesting stuff inside done by the kernel is hidden inside
getNextRequest.
int getNextRequest( ) {
active = schedule( ... );
return activate( active );
What's inside activate( active )?
The hard part to get right is `transfer of control'
| M[4:0] | Mode | Registers accessible |
| 10000 | User | r0-r15 |
| 10001 | FIQ (Fast interrupt processing) | r0-r7, r15r8_fiq-r14_fiq
|
| 10010 | IRQ (Interrupt processing) | r0-r12, r15r13_irq,r14_irq
|
| 10011 | Supervisor | r0-r12, r15r13_svc,r14_svc
|
| 10111 | Abort | r0-r12, r15r13_abt,r14_abt
|
| 11011 | Undefined | r0-r12, r15r13_und,r14_und
|
| 11111 | System | r0-r15
|
| Bit | Mnemonic | Meaning |
| 31 | N | Negative |
| 30 | Z | Zero |
| 29 | C | Carry |
| 28 | V | Overflow |
| 8-27 | DNM | Does not matter in v4 |
| 7 | I | Interrupts disabled |
| 6 | F | Fast interrupts disabled |
| 5 | T | Thumb execution |
| 4 | M4 | Five processor mode bits |
| 3 | M3 | |
| 2 | M2 | |
| 1 | M1 | |
| 0 | M0 |
| Exception
Type |
Modes
Called from |
Mode at
Completion |
Instruction
Address |
| Reset | hardware | supervisor | 0x00 |
| Undefined instruction | any | undefined | 0x04 |
| Software interrupt | any | supervisor | 0x08 |
| Prefetch abort | any | abort | 0x0c |
| Data abort | any | abort | 0x10 |
| Ordinary interrupt | any | IRQ | 0x18 |
| Fast interrupt | any | FIQ | 0x1c |
0x00000000. Usually it is
ldr pc, [pc, #0x18]; 0xe590f018 is the binary encoding
which you will normally find in addresses 0x00 to
0x1c. Just executing an instruction, rather than having
an address that is specially processed saves transistors, which is
good.
The indirect jump allows the CPU to jump anywhere in the address
space.
0x20 to 0x3c.; In calling code
; store values of r0-r3
; load arguments into r0-r3
bl <entry point> ; this treats the pc and lr specially
; lr <- pc, pc <- <entry point>
; r0 has the return value
; r1-r3 have useless junk
; In called code
entry point:
mov ip, sp
stmdb sp!, {fp, ip, lr} ; and usually others,
; determined by the registers the function uses
...
ldmia sp, {fp, sp, pc} ; and whatever others
; exact inverse of stmdb
Note the role of the index pointer (ip), link register (lr) and stack pointer (sp).
The final instruction could be
ldmia sp, {fp, sp, lr}
mov pc, lr
The sequence
bl junk . . . junk: mov pc, lr
is a NOP.
The software interrupt instruction ( SWI{cond} <immed_24> ). What happens when it is executed?
The CPU ignores the 24-bit immediate value, which can be used by the programmer as an argument identifying the system call, for example.
; In calling code
; Store r0-r3
; Put arguments into r0-r3
; 0x08 holds the kernel entry point
swi n ; n identifies which system call you are calling
; retrieve return value from r0
; r1-r3 have even more useless junk
; In kernel
kernel entry:
; Change to system mode
; Save user state on user stack
; Return to supervisor mode
ldr r4, [lr, #-4] ; gets the request type
; At this point you can get the arguments
; Where are they? Why couldn't you retrieve them earlier?
; Retrieve kernel state from kernel stack
; Do kernel work
The sequence
swi n . . . kernel entry: movs pc, lr
is a NOP.
This NOP depends on a bunch of things being correctly set up, especially the low memory.
Responding to SWI treats the scratch registers in a special way.
In the third part of the kernel you will implement hardware interrupts.
It seems desirable to have as much code as possible common to hardware and software interrupts.
kernel entry?Hint. How does gcc pass arguments into a function?
Try reading this.
The order matters
kernel entry:
sp_usr in a scratch register, say r2ldr r3, [lr, #-4]
active is indexed off the kernel's frame pointeractive is a pointer to the TD of the requestergetNextRequest( active ) and get to work
There is more than one way to do almost everything in this list, and I have chosen this way of describing what is to be done because it's simplest to describe, not because it's necessarily best!.
After a while it's time to leave the kernel
activeGetNextRequest( active )Inside GetNextRequest
movs pc, lr
The instruction after this one is normally the kernel entry.
Return to: