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 ); } }
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'
Specific documentation from ARM, which covers
Features
partially separate register sets in different modes
some registers are special, but not very special
M[4:0] | Mode | Registers accessible | Used for |
10000 | User | r0-r15
|
OS servers, application code |
10001 | FIQ (Fast interrupt processing) | r0-r7, r15 r8_fiq-r14_fiq
|
Responding to a privileged interrupt |
10010 | IRQ (Interrupt processing) | r0-r12, r15 r13_irq, r14_irq
|
Responding to an ordinary interrupt |
10011 | Supervisor | r0-r12, r15 r13_svc, r14_svc
|
Kernel runs in this mode |
10111 | Abort | r0-r12, r15 r13_abt, r14_abt
|
Responding to failure of either data or instruction fetch. |
11011 | Undefined | r0-r12, r15 r13_und, r14_und
|
Responding to an attempt to execute an undefind instruction. |
11111 | System | r0-r15
|
Execute privileged instructions while seeing all user mode registers. |
The modes that interest you now are
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 |
seg
fault
.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.
0x20
to 0x3c.
Register use
Function call
bl #entry lr gets pc; pc gets #entry
; In calling code ; store values of r0-r3 ; load arguments into r0-r3 bl #entry - pc ; this treats the pc and lr specially: lr <- pc, pc <- <entry> ; On return: r0 has the return value, r1-r3 have useless junk ; In called code entry: mov ip, sp stmdb sp!, {fp, ip, lr} ; and sometimes others, ; determined by the registers the function uses ... ldmia sp, {fp, sp, pc} ; and whatever others ; exact inverse of stmdb
This sequence of four instructions is an example of the NOP design pattern, named by Bobby Xiao.
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> - pc . . . junk: mov pc, lr
is also 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 another argument, usually identifying the system call.
; In calling code ; Store r0-r3 ; Put arguments into r0-r3 ; 0x28 holds the kernel entry point swi n ; n identifies the system call ; retrieve return value from r0 ; r1-r3 have 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? ; Retrieve kernel state from kernel stack ; Do kernel work
The sequence
swi n . . . kernel entry: movs pc, lr
is a NOP
. The 's' in movs puts the SPSW into pc.
Questions:
Hint. How does gcc
pass arguments into a function?
Suggestions:
Try reading this.
Return to: