CS452 - Real-Time Programming - Spring 2016

Lecture 5 - Context Switches

Public Service Annoucements

  1. Due date for kernel 1, Friday, 27 May
  2. Partners: if you don't have one, please let me know.

Building a Microkernel: Context Switches

What Does a Microkernel Provide?


What is a task?

  1. A set of instructions
  2. The current state, which is changed by executing instructions. The state of a task includes which includes

Two tasks can use the same set of instructions, but

The kernel keeps track of every task's state

The TD normally contains

  1. The task's stack pointer, which points to a private stack, in the memory of the task, containing

    all ready to be reloaded whenever the task next runs.

  2. Possibly the return value for when the task is next activated
  3. The task's parent
  4. The task's state
  5. Links to queues on which the task is located

Possible states of the task

  1. Active: running or about to run
  2. Ready: can run if scheduled
  3. Blocked: waiting for something to happen

Kernel Structure

The kernel is just a function like any other, but which runs forever.

    main( ) {
      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.

    TD *active;
    int getNextRequest( ) {
      active = schedule( ... );
      return activate( active );

What's inside activate( active )?

  1. transfer of control to the active task
  2. execution to completion of the active task
  3. transfer of control back to the kernel

The hard part to get right is `transfer of control'

ARM 920T core

Specific documentation from ARM, which covers


  1. 16 32-bit registers Note that there are partially separate register sets in some modes. They are essential for writing a kernel.
  2. There are seven processor modes.
    M[4:0] Mode Registers accessible Used for
    10000 User r0-r15


    OS servers, application code
    10001 FIQ (Fast interrupt processing) r0-r7, r15

    cpsr, spsr_fiq

    Responding to a privileged interrupt. Not used in our kernel.
    10010 IRQ (Interrupt processing) r0-r12, r15
    r13_irq, r14_irq

    cpsr, spsr_irq

    Responding to an ordinary interrupt
    10011 Supervisor r0-r12, r15
    r13_svc, r14_svc

    cpsr, sprs_svc

    Kernel runs in this mode
    10111 Abort r0-r12, r15
    r13_abt, r14_abt

    cpsr, spsr_abt

    Responding to failure of either data or instruction fetch.
    11011 Undefined r0-r12, r15
    r13_und, r14_und

    cpsr, spsr_und

    Responding to an attempt to execute an undefind instruction.
    11111 System r0-r15


    Execute privileged instructions while seeing all user mode registers.

  3. Program status register, which you will find in two places: the current program status register (CPSR) and the saved program status register (SPSR).

    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. Set, but do not change.
    5 T Thumb execution. Set but do not change.
    4 M4 Five processor mode bits
    3 M3
    2 M2
    1 M1
    0 M0
    Notice that the SPSR is mode specific. Failing to take into account the mode-specificity of SPSR has over the years been the cause of very subtle (= hard to find) bugs in the kernel.

  4. Exceptions



    Called from

    Mode at




    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

    1. You are concerned right now with Reset and Software Interrupt.
      • Undefined instruction, Prefetch abort & Data abort may occur on execution errors. They are the equivalent of seg fault.
    2. The first instruction executed by the CPU after reset is the one at location 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. You see #0x18 where you might expect to see #0x20# because the evaluation of the offset from pc occurs in the third stage of the pipeline so that the pc has been incremented twice.

    3. RedBoot puts entry points of RedBoot into all the addresses 0x20 to 0x3c.
    4. This makes it possible to jump to any location in the 32 bit address space.
    5. Note endianness of RedBoot output when examining these locations.
  5. Three data types

General Comments

  1. each instruction is exactly one word
  2. load and store RISC architecture
  3. rich set of addressing modes, based on barrel shifting
  4. allows you to keep any partial computation it makes

Context Switch

What gcc does

Register use

Function call

  1. Save r0-r3 into local variables.
  2. Put arguments into r0-r3.
  3. bl #entry; lr gets pc, pc gets #entry
  4. mov ip, sp; allows multiple store
  5. push {fp, ip, lr} onto stack; possibly other registers
  6. application code gets arguments from the register, does its thing, then puts the return value into r0.
  7. pop {fp, sp, pc} from stack; possibly others. This returns from the function.
  8. put r0 (return value) into local variable


Function Call (gcc calling conventions)

; 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
   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
   mov   pc, lr

is also a NOP.

Software Interrupt SWI

The software interrupt instruction ( SWI{cond} <immed_24> ). What happens when it is executed?

  1. lr_svc gets address of the following instruction. This is where the kernel will return to.
  2. SPSR_svc gets CPSR. This saves the mode, condition codes, etc.
  3. CPSR[0:4] gets 0b10011. Supervisor mode.
  4. CPSR[5] gets 0. ARM (not Thumb) state.
  5. CPSR[6] gets 1. Privileged interrupts disabled.
  6. CPSR[7] gets 1. Normal interrupts disabled.
  7. PC gets 0x08

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.


  1. What is above kernel entry?
  2. If you put swi in a wrapper or stub what happens before and after it?
  3. If the request had arguments, how would you get them into the kernel?
    Hint. How does gcc pass arguments into a function?
  4. It might be important that there are three link registers. Which three link registers? Why?
  5. In practice it isn't important. Why not? It will be very important later.


  1. Try this first on paper drawing the stack, registers, etc after each instruction
  2. Try coding in baby steps, which is usually a good idea in assembly language.

Try reading this.

Return to: