Privilege Levels & Protected/Long Mode
x86-64 Assembly
Chapter 8 · Privilege Levels & Protected/Long Mode
Every chapter so far has extended something the prior arc already covered — richer registers, richer addressing, richer instructions. This chapter is different: it's genuinely new ground. Nothing in assembly1 or cpu8bit1 has an equivalent for what's about to be covered, because neither LC-3 nor the 6502/Z80 enforce any concept of trust at all.
A Genuinely New Kind of Chapter
Every instruction covered across both prior courses could be executed by literally any code running on those chips. LC-3 has no privileged instructions; the 6502 and Z80 have none either — no rings, no memory protection, no hardware-enforced OS/user distinction anywhere in either design. x86-64 is fundamentally different: the CPU itself enforces a hierarchy of trust, and certain things simply cannot be done from the wrong level, no matter what the program tries.
Protection Rings — 0 Through 3
- Ring 0 — kernel/OS mode. Full access to every instruction, every memory location, every piece of hardware. This is where the operating system kernel itself runs.
- Ring 3 — user mode. Restricted. Ordinary application programs run here, and a specific category of instructions — ones that reconfigure paging, change privilege level, or touch hardware directly — are simply forbidden. Attempting one doesn't fail silently; it triggers a CPU exception.
This is the real hardware mechanism behind assembly1-8's own OS/hardware boundary concept — LC-3's TRAP was a clean, simplified stand-in for exactly this idea: user code cannot directly do privileged things, and has to go through a controlled channel instead. assembly2-10 covers that channel's real, modern form.
Virtual Memory via Paging
Every running process sees its own private, seemingly complete address space — even though many processes actually share the same physical RAM underneath. Paging is the mechanism: memory is divided into fixed-size chunks (pages, typically 4KB), and the CPU's own hardware, guided by OS-managed page tables, translates every virtual address a program's instructions reference into a real physical address, on every single memory access.
If a virtual address has no valid mapping in a process's own page table, any attempt to touch it raises a page fault — a real, controlled CPU exception.
cpu8bit1-3's own warn-box described a real 6502 hazard: a stack pushed past its limit doesn't error at all — SP silently wraps around and quietly corrupts whatever data used to be there. That's "a program touching memory it shouldn't" with zero protection. x86-64's page fault is the exact same category of problem — a program reaching memory it has no business touching — handled with real, hardware-enforced detection instead of silent corruption. This is genuine architectural progress on a problem this arc named concretely, several chapters ago.
Closing assembly2-1's Own Loop
Recall assembly2-1's own three modes: real, protected, and long. Now their real difference can be stated precisely:
- Real mode has no paging and no protection at all — a program running in real mode has exactly the same total, unguarded access to memory that every LC-3, 6502, and Z80 program in this entire arc has always had. Booting into real mode isn't just "acting like an old chip" in spirit — it's a genuine, temporary return to zero memory protection.
- Protected mode (introduced by the 80386,
assembly2-1's own lineage) is where paging and rings first appear. - Long mode — this course's real subject — uses an extended, deeper paging structure to address vastly more memory than 32-bit protected mode ever could.
An instruction attempted from the wrong ring doesn't just quietly fail — it raises a specific, named, well-documented exception: the General Protection Fault (#GP), a real CPU-level event any programmer doing low-level debugging eventually meets in person.
| Concept | LC-3 (assembly1) | 6502 / Z80 (cpu8bit1) | x86-64 (this chapter) |
|---|---|---|---|
| Privilege levels | None | None | Rings 0–3 (practically 0 and 3) |
| Memory protection | None — any address accessible | None — cpu8bit1-3's own unprotected stack wraparound | Paging + page faults, hardware-enforced |
| Who can run any instruction | Any code | Any code | Only ring 0 for privileged instructions — ring 3 gets a #GP fault |
Hands-On Exercises
Explain why LC-3 and the 6502/Z80 have no possible equivalent of "an instruction is forbidden in ring 3" at all — what would each of those architectures need to add before such a concept could even exist?
📄 View solutionUsing this chapter's own tip-box, explain how a page fault is a genuine architectural improvement over cpu8bit1-3's own silent 6502 stack-wraparound hazard — both are the same underlying problem, handled completely differently.
📄 View solutionUsing this chapter's own explanation of real mode, state exactly what memory protection a program running in real mode actually has, and compare that directly to the protection LC-3 and the 6502/Z80 provide.
📄 View solutionChapter 8 Quick Reference
- Genuinely new ground: LC-3 and the 6502/Z80 have no privilege concept at all — any code can execute any instruction
- Ring 0 (kernel) has full access; Ring 3 (user) is forbidden from privileged instructions — rings 1/2 exist but are rarely used by real OSes
- Paging — fixed-size pages, hardware-translated virtual-to-physical addresses, enforced per process
- A page fault is the hardware-enforced answer to exactly the kind of hazard cpu8bit1-3's own unprotected 6502 stack wraparound left completely undetected
- Real mode = zero protection, matching LC-3/6502/Z80 exactly; protected mode introduces rings/paging; long mode extends paging further
- A forbidden instruction from ring 3 raises a General Protection Fault (#GP) — a real, named, commonly-encountered CPU exception
- assembly2-10 covers the sanctioned channel (syscalls) user-mode code actually uses to ask the kernel for privileged work