You have met the idea of user mode, kernel mode, and the syscall doorway in the parent note . Here we do the opposite of hand-waving: we list every kind of situation a privilege system can face, then work each one to the end. If you can trace all of these, nothing about mode switching can surprise you in an exam.
Before working examples, we lay out the full space of cases. Every real event on a CPU is one of the cells below. Think of the two axes as "who is running?" (current mode) and "what did they just attempt?" (the event).
#
Current mode
Event attempted
Legal?
What hardware does
A
User
ordinary unprivileged instruction (add, load)
✅
runs it, mode unchanged
B
User
privileged instruction (cli, hlt, write page-table base)
❌
trap → kernel fault handler
C
User
syscall / ecall (the legal doorway)
✅
atomic promotion → kernel entry vector
D
User
external interrupt fires (timer, disk done)
✅
atomic promotion → interrupt vector
E
User
memory access outside its mapped region
❌
page fault → kernel
F
Kernel
any instruction, including privileged
✅
runs it, mode unchanged
G
Kernel
sret / iret (return to user)
✅
atomic demotion → user, restore user PC
H
Kernel
writes the mode bit directly
✅ (kernel only)
mode changes; a user attempting this = cell B
I
degenerate : mode bit = kernel but PC came from user's choice
—
this is the exploit we must prevent
vectoring makes it impossible
J
limiting : user loop never yields (while(1){})
—
timer (cell D) forces control back
pre-emption
K
word problem : read() from a file
mix of A, C, F, G
✅
full round trip
L
exam twist : nested — interrupt during a syscall
mix of D inside F
✅
privilege already kernel; PC saved twice
The examples below hit every letter. The matrix cell each covers is stated in its title.
Definition The two words we lean on
A trap (also "exception"): the hardware itself stops your instruction and jumps to the OS because you did something illegal or asked for help. See Interrupts and exceptions .
The mode bit : a field inside the Processor status register (PSW) that literally answers "am I user or kernel right now?". On x86 it appears as CPL (Current Privilege Level), a 2-bit number: 0 = kernel, 3 = user.
Worked example User program runs
add r1, r2, r3
A user process (CPL = 3) executes a plain addition.
Forecast: Does anything trap? Does the mode change? (Guess before reading.)
Decode the opcode. Why this step? Enforcement always happens at decode, before the effect, so an illegal op never takes effect.
Ask: is this opcode privileged? add touches only the program's own registers → not privileged. Why does that matter? The mode check is only consulted for privileged ops; unprivileged ones skip it.
Execute. Registers update, mode stays CPL = 3.
Verify: No trap raised, mode unchanged. Sanity check: if every instruction trapped, user programs could never run at all — so the common case must be free. ✅
Worked example User program executes
cli (disable interrupts) on x86
Current CPL = 3. cli requires CPL = 0.
Forecast: Will the interrupts actually be disabled? Where does the CPU go next?
Decode cli. Why? Same as always — check before effect.
Look up the required level. cli needs level 0. Why store a required level per opcode? So one uniform comparison rule covers every privileged instruction.
Compare: required 0, current 3. Since 3 > 0, the running code is less trusted than required → violation. Why "greater number = less trust"? Rings count inward; see Protection rings (x86) / Exception Levels (ARM) . Ring 0 = King.
Raise #GP (General Protection Fault). Control jumps to the kernel's fault vector; the OS usually kills the process. Why not silently ignore? Ignoring lets an attacker probe what's allowed; a trap keeps the OS in charge.
Verify: Interrupts were never disabled — the effect never fired because decode blocked it. The comparison 3 ≤ 0 is false, confirming the trap. ✅
Worked example User process invokes
write() via syscall
The C library placed the syscall number in a register, then executed syscall.
Forecast: After syscall, what is the mode, and who chose the next PC — the user or the OS?
syscall executes in user mode. Why is syscall itself allowed in user mode? It is the one instruction whose whole purpose is a controlled escalation — see System calls and the OS interface .
Hardware atomically sets mode → kernel. Why atomic? If the mode-change and the jump were separable, an attacker could interleave and run their own code as kernel (that's forbidden cell I).
PC ← fixed OS entry vector , not any user-supplied address. Why a vector? So the user chooses that they enter the kernel, never where . This closes the cell-I hole.
Kernel validates arguments , does the privileged work, then sret (cell G) drops mode → user.
Verify: After step 3, mode = kernel and PC = OS-chosen vector. Both guarantees hold simultaneously → no escalation exploit. ✅
while(1){} forever — how does the OS ever get the CPU back?
The process makes no syscalls. It just spins.
Forecast: Is the machine frozen forever? If not, what saves it?
Before dispatch, the kernel armed a hardware timer. Why can the kernel do this but the user can't? Programming the timer is privileged (cell B would block the user). See Pre-emptive multitasking and the scheduler .
The loop spins in user mode (all its instructions are cell A — legal, no yield).
The timer fires → external interrupt (cell D). Hardware atomically: mode → kernel, PC → timer handler vector.
The scheduler runs , may switch to another process.
Verify: The user could stop this only by disabling the timer or masking interrupts — both privileged (cell B). So it cannot . Therefore the loop is pre-empted after one timer quantum, not forever. Limiting behaviour confirmed: even infinite user code cannot monopolise the CPU. ✅
Worked example User code writes to an address the OS never mapped for it
*(int*)0xFFFF0000 = 42; where that address belongs to the kernel or another process.
Forecast: Does the write land in kernel memory? What stops it?
The address is translated by the page table . Why involve the page table? User addresses are virtual; the mapping decides what physical page (if any) they reach.
The page-table entry for this page is marked "not accessible from user mode" (or is absent). Why? The kernel deliberately never mapped its own pages as user-writable.
Hardware raises a page fault → kernel handler. The OS sees the access was illegal and kills the process.
Verify: The value 42 never reached kernel memory — the mapping check happened before the store completed. Even in kernel mode this protects against a user-mode access bit; the mode field and the page permission cooperate. ✅
Worked example Malicious user tries
mov CPL, 0 (pretend-instruction: "just set my mode to kernel")
The attacker's idea: skip the doorway, write the mode bit directly.
Forecast: Does the CPU become kernel? Why is this the crux of the whole design?
Decode the write-to-mode-bit opcode. Why check? Writing the mode bit is itself a privileged operation (cell H is legal only in kernel).
Compare: required level 0, current 3 → violation → trap (cell B).
So the only way the mode bit becomes kernel from user mode is via syscall/interrupt (cells C, D), which also forces the PC to an OS vector (cell I is unreachable).
Verify: Escalation requires writing the mode bit; writing it requires already being kernel. This is a fixed point — you cannot bootstrap privilege. The exploit cell I ("kernel mode but PC chosen by user") is provably unreachable. ✅
read(fd, buf, n) from C — trace every mode transition
Forecast: How many times does the mode change from start to finish? (Guess a number.)
Library packs syscall number + fd, buf, n into registers (user mode, cell A). Why registers? Fast, ABI-defined, no memory needed.
syscall → mode change #1: user → kernel (cell C). PC → OS entry vector.
Kernel validates that buf and buf+n lie inside this process's memory. Why validate? Otherwise a user could trick the kernel into writing to the kernel's own memory.
Kernel performs the privileged disk read (cell F, legal because mode = kernel), copies bytes into buf.
sret/iret → mode change #2: kernel → user (cell G). PC → instruction after syscall.
Verify: Exactly 2 mode changes (one up, one down). Both are hardware-atomic; the user never chose the kernel PC. If your forecast said 2, ✅.
Worked example Timer interrupt fires while the kernel is servicing a syscall
We are already in kernel mode (mid-read()) when the timer fires.
Forecast: Does the mode change? What extra state must be saved this time?
We are in kernel mode (mode bit = 0). Why does that matter for the next step? The interrupt still vectors, but there is no user→kernel promotion to do — we're already kernel.
The interrupt vectors to the timer handler; the CPU saves the current (kernel) PC so the syscall can resume. Why save again? We now have a nested return: first back to the interrupted kernel code, later back to the user.
Handler runs, returns; the syscall resumes; eventually sret drops to user (cell G).
Verify: Mode goes kernel → kernel (no promotion), and there are two saved return points (one for the interrupt, one for the original syscall). The single user→kernel and kernel→user pair from Example 7 is unchanged; the interrupt is nested strictly inside it. ✅
Recall Quick self-test (click to reveal)
Which matrix cell? A user program's hlt instruction ::: Cell B — privileged instruction in user mode → trap.
Which matrix cell? The scheduler regaining a spinning CPU ::: Cell D (timer interrupt), enabling the limiting case J.
How many mode changes in one complete read()? ::: Exactly 2 — up on syscall, down on sret.
Why is cell I (kernel mode, user-chosen PC) impossible? ::: Because promotion only happens via vectored syscall/interrupt, which forces an OS-chosen PC; and writing the mode bit directly is itself privileged.
Mnemonic Matrix in one line
"Legal up only through the door (C/D); down is free (G); everything else user tries that's privileged = trap (B/E/H)."
#flashcards/hardware
In the scenario matrix, what happens in cell B (user runs a privileged instruction)? Hardware raises a trap/#GP before the effect; control jumps to the kernel fault handler.
How many mode transitions occur in one full read() syscall? Two — user→kernel on syscall, kernel→user on sret/iret.
Why can a user's while(1){} loop not freeze the machine? The kernel armed a hardware timer (privileged); when it fires, an interrupt atomically returns control to the OS.