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.
Rings 1 and 2 exist, but are rarely used
x86-64's design theoretically supports four rings, but real modern operating systems — Linux and Windows both — effectively use only rings 0 and 3. The full four-ring design is more a historical/theoretical capability than something real-world software actually exploits.

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.

The same problem, two very different answers
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.

ConceptLC-3 (assembly1)6502 / Z80 (cpu8bit1)x86-64 (this chapter)
Privilege levelsNoneNoneRings 0–3 (practically 0 and 3)
Memory protectionNone — any address accessibleNone — cpu8bit1-3's own unprotected stack wraparoundPaging + page faults, hardware-enforced
Who can run any instructionAny codeAny codeOnly ring 0 for privileged instructions — ring 3 gets a #GP fault

Hands-On Exercises

Exercise 1

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 solution
Exercise 2

Using 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 solution
Exercise 3

Using 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 solution

Chapter 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