CS 452/652 Winter 2026 - Lecture 6
Context-Switch and System Call
Jan 22, 2026 prev next
AEM - AArch64 Exception Model
Context Switch
- execution state
- high-level language: line, variables (automatic, static, global, heap)
- machine: program counter, stack pointer, other registers, stack content
- data on stack (heap?)
- context = registers + stack
- registers: fast operands for processor operations
- stack: dynamic storage for automatic variables
- stack switch
- save registers (to somewhere) without changing them
- stack switch: save/restore SP register
- restore registers
- mode switch (privilege level) - processor exception
- some state automatically saved
- usually implies stack switch for safety & security
- system call & interrupt: stack switch & mode switch
Kernel Design
- no stack (use SP_EL0, unsafe)
- temp stack - kernel as event handler
- single stack - simple, default, kernel stack in SP_EL1
- per-task kernel stack - preemptive time-slicing kernel
- often with return to task after system call
- extra benefit: lazy context-switch (even on interrupt)
- system call: caller-saved done by compiler
- interrupt: save only caller-saved
- continue on per-task kernel stack: callee-saving done by compiler
- stack switch (in kernel): save only callee-saved
Exception Vector
- kernel-controlled jump destinations for exceptions (see AEM Table 4)
- vectors: VBAR_ELn exceptions to EL n
- 4 groups (from where): Current EL SP0/x, Lower EL 64/32
- 4 vectors (32 instructions): Synchronous, IRQ, FIQ, Error
→ total of 2KB = 4 * 4 * 128 bytes
- recommendation: set up dummy handlers for everything!
System Call
- synchronous exception - this is how a task asks the kernel for something
- dedicated instruction:
svc N
- execution continues (PC) at hard-coded handler (exception vector)
- processor in EL1 using SP_EL1 (assuming SPSel=1)
- ELR_EL1 holds return address (next PC after svc)
- ESR_EL1 holds exception code and N from SVC
- SPSR_EL1 holds processor state from before SVC
- SP is banked SP_EL1 (run with SPSel set to 1)
- C/C++ function call: compiler has saved x0-x18 (as needed)
- what needs to happen next?
- save general-purpose registers
- save ELR_EL1, SP_EL0, SPSR_EL1
- SPSR not strictly needed for system call (ABI rules)
- access to SP_EL0 direct or via SPSel = 0 (temporarily)
- we might not return to same task
- choices for storing user context:
- on user stack, or
- in task descriptor
- restore kernel state
- symmetry: kernel vs. user entry/exit
- kernel entry/exit: syscall vs. irq
- access system call arguments?
- resume user task:
- save kernel context (function call, ABI)
- restore ELR_EL1, SP_EL0, SPSR_EL1
- return from exception:
eret
- restores PC from ELR, pstate from SPSR
- returns to EL0 (then using SP_EL0)
System Call Parameters
- example:
int Create(int priority, void (*function)())
- how does the kernel receive the two parameters?
- how does it return the int?
- simple answer: can use the same arg/return conventions established by the ABI
- user task puts params into x0 and x1 before svc
- exception handler saves application context
- kernel can inspect (saved) x0 and x1 to find parameters
- kernel overwrites (saved) x0 with return value before
eret
- after
eret, user task looks in x0 to find result
- system call stubs: create a user-level function corresponding to each system call
Task State
- execution state
- management state: Ready, Active, Blocked, etc.
- management information: parent task
- normally used for group scheduling, termination
- task descriptor: in-kernel data structure holding task state
- queues for scheduling and blocking
- task-shared memory: code, readonly data
- task-private memory on stack
- ok to use static global for singleton tasks with subroutines
- ok to use static global for boot-time write-once configuration constants
- do not share other memory between tasks!
Task Creation
Kernel Initialization
- initially in boot.S:
- sets up EL1 stack pointer, masks interrupts, jumps to kmain
- what else needs to happen?
- need to initialize exception vector
- need to initialize any kernel data structures
- allocate slab of task descriptors and stacks and manage via free list
- need to create an initial task, and context switch to it
- initial task will need to run some user-level function, at some priority
- hard code function pointer into kernel
- set up new task context to be the "saved" context for user task
- return from exception (restore user context, then
eret)
Additional Information
An earlier context-switch document by Bill Cowan is available
here.
This is certainly not the only way to write a context switch and I do not
necessarily recommend (or not recommend) this particular approach, but I
figure every bit of information can help.