Five registers. Between them they decide whether the processor is in protected mode, whether it is paging, where the page tables are, how wide an address is, whether the timer can read the clock, whether a performance counter can be read, and whether the supervisor can execute a user page. Every one of those decisions is made by a bit that a user process cannot read.
That makes this concept the course's purest example of the distinction the whole course is built on. There is no table of CR0's value in this course, because there cannot be one. What there is instead is the complete bit layout, printed as a reference and stamped NOT MEASURED, and then a measurement of what each of the interesting bits does — which is a consequence, narrows the set of readings, and is not a reading.
CR0 is a mode register, CR2 is a mailbox, CR3 is a pointer with four address-size fields in it, and CR4 is a feature-enable word that has been appended to one bit at a time for thirty years. CR1 and CR5 through CR7 are reserved, and CR8 is not a control register at all in the usual sense: it is the task priority register, and it is not privileged on x86-64, which is a genuinely surprising exception.
$ ./sysdump | sed -n '/1B2\./,/^1B3/p'
1B2. CR3. THE PAGE-DIRECTORY BASE AND THE ADDRESS-SIZE BITS.
CR3 | 0-11 | PCID | address space tag, USED ONLY IF CR4.PCIDE=1
CR3 | 12 | PWT | page-table writes are write-through
CR3 | 13 | PCD | page-table writes bypass the cache
CR3 | 14-47 | BASE | physical address of the PML4, 4 KiB aligned
CR3 | 48-51 | SEAM | reserved, and MUST EQUAL bit 47 of BASE:
CR3 | | | this is the sign-extension of the base and
CR3 | | | it is the only thing in CR3 that is not a bit
CR3 | | | you can set to a constant
CR3 | 52-63 | ZERO | must be zero in 4-level paging; with CR4.LA57=1
THE FOUR ADDRESS-SIZE BITS, which is what a compiler backend has to
get right and what CR0.CR3 and CR4.LA57 select between:
4-level paging CR3[63:52] reserved-zero, CR3[51:48] sign-extend
5-level paging CR3[63:58] reserved-zero, CR3[57:52] sign-extend
The address bits come from the CPUID, not from CR3, and section 2
measures which of the two this machine reports.
That SEAM row is the one a backend has to get right. CR3[47:0] is a 4 KiB-aligned physical address, and a physical address has no sign; the field at 51:48 exists so that the register's top bits are a sign extension rather than four independent bits, which means you cannot write CR3 with a mask and an OR. You have to shift the base left by four, OR the sign, and shift back. It is one instruction in a hand-written loader and it is the difference between booting and faulting before the first byte of the kernel is mapped.
And the CR0 table has a row that is the best single illustration of why architectures accumulate scars:
$ ./sysdump | sed -n '/CR0 | 4 /,/CR0 | 5 /p'
CR0 | 4 | ET | extension type, RESERVED and ALWAYS 1 since the 486
CR0 | 4 | ET | extension type, RESERVED and ALWAYS 1 since the 486
A bit with no meaning, no way to read it, and a hardware rule that it must be 1. On the 386 it selected between the 80287 and the 80387 math coprocessors, software wrote zero to it because it was the reset value, and a 486 was built that faulted on the zero. Three decades later it is a reserved-must-be-1 row in every reference and nobody can say what setting it to zero would do. Compatibility scars are not mistakes; they are the price of a promise somebody kept.
Sixteen attempts — five registers read, four written, plus the one that is not privileged:
$ ./sysdump | grep -E '^ EDGE \| mov (cr|reg)'
EDGE | mov cr0 -> reg | control registers | FAULTED | sig 11 | si_code 128 | si_addr 0x000000000000
EDGE | mov cr2 -> reg | control registers | FAULTED | sig 11 | si_code 128 | si_addr 0x000000000000
EDGE | mov cr3 -> reg | control registers | FAULTED | sig 11 | si_code 128 | si_addr 0x000000000000
EDGE | mov cr4 -> reg | control registers | FAULTED | sig 11 | si_code 128 | si_addr 0x000000000000
EDGE | mov cr8 -> reg | control registers | FAULTED | sig 11 | si_code 128 | si_addr 0x000000000000
EDGE | mov reg -> cr0 | control registers | FAULTED | sig 11 | si_code 128 | si_addr 0x000000000000
EDGE | mov reg -> cr3 | control registers | FAULTED | sig 11 | si_code 128 | si_addr 0x000000000000
EDGE | mov reg -> cr4 | control registers | FAULTED | sig 11 | si_code 128 | si_addr 0x000000000000
EDGE | mov reg -> cr8 | control registers | FAULTED | sig 11 | si_code 128 | si_addr 0x000000000000
Nine of nine, all with si_code 128 and an address of zero: a #GP with an empty error code, which is why 128 keeps appearing in this course. CR8 is in the table because the surprise is worth measuring rather than asserting: the task priority register is documented as privileged, and it is privileged except on x86-64, where the SDM says “in 64-bit mode, CR8 is not a privileged register.” The measurement is a fault, and it is a fault on this AMD part, so either the exception is vendor-specific or the reading is. This file does not say which, because a disagreement between a manual and a measurement on one part is a question, and a question printed is worth more than a confident sentence.
CR4 that decide the mostTake the thirty-one CR4 rows and ask which ones change what this process can do. Five, and the artifact names them in its own prose:
$ ./sysdump | sed -n '/FIVE ROWS OF THAT TABLE/,/it cannot read/p'
FIVE ROWS OF THAT TABLE DECIDE WHETHER THIS PROCESS CAN OBSERVE
ANYTHING AT ALL, and every one of them is a bit in a register it
cannot read: CR4.UMIP decides whether SIDT and SGDT are legal at
CPL 3. CR4.PCE decides whether RDPMC is legal at CPL 3.
CR4.TSD decides whether RDTSC is legal at CPL 3. CR4.PCIDE and
CR4.LA57 decide the shape of CR3. Section 3 measures the
CONSEQUENCES and section 2 measures what the kernel says the bits
are. What it CANNOT do is read CR4, and that is the limit.
That is a real narrowing and it is worth being precise about what it is worth. Knowing UMIP=0 rules out one of two values; it does not tell you who cleared it, when, or whether a kernel upgrade would clear it again. A consequence is a constraint, not a value — and the third concept's field-order result is the same kind of statement, arrived at from the other direction: a measurement that predicted an exact number rather than ruling one out.
The second half of the reference is not the bits but what each write costs, because a backend that has to reason about a CR3 reload has to know whether the TLB survives:
$ ./sysdump | sed -n '/WRITING CR0 invalidates/,/instruction and the assertion is one/p'
WRITING CR0 invalidates: CR0.PG 0->1 flushes the TLB completely.
CR0.WP and CR0.AM change no TLB entry, so
the SDM requires NO invalidation for them
and this file has no instrument either.
So the one row in the table that is not a mode and not a feature is CR0.PG, because clearing and setting it is a whole-TLB flush, and a context switch that reloads CR3 with a different PCID does not need one. That is the entire argument for the process-context identifier: a bit in CR3[11:0] that makes the most expensive operation in the MMU unnecessary. The memory course measured the cost of the thing being avoided; this is where the mechanism is.
CR2, the one control register that is a mailboxCR2 is not a mode. It is where the CPU writes the linear address of a page fault, and it is the only control register with a readable-in-a-handler contract, because the handler runs at CPL 0 by construction. That makes it a debugging aid rather than a control, and it is also a trap: CR2 is a 64-bit register on x86-64 in long mode, so a kernel that reads only its low half will work perfectly until a process has a mapping above 4 GiB, at which point it will report a fault address of zero.
What a ring-3 process gets instead is si_addr, which is notCR2. Section 4 of the artifact measures the difference three ways:
si_addr = 0, si_code = 1. Here they agree, because the linear address really was zero.si_addr is the page, si_code = 2. Again agreement, and again the number cannot distinguish it from the next case.si_addr = 0, si_code = 128. Here they disagree, and the disagreement is the whole point: the CPU never made it an address, so CR2 was never written, and the kernel's “address” is a zero it invented.CR8 that is supposed to be readable. Write a ten-line program that does mov %rax, %cr8 and runs it. (Expect SIGSEGV on this machine. Then read the SDM sentence about 64-bit mode and decide whether you are looking at an erratum, a vendor difference, or a misreading — and write down which of the three you think it is before you check, because the answer you write first is the one you will remember.)CR4.TSD=0 because RDTSC returned. Write the complementary test: what would you have to observe to conclude CR4.TSD=1? (Expect: nothing from ring 3. The observation is impossible, which is what makes this a constraint rather than a value — and it is the difference between the two sentences “the bit is clear” and “the bit does not stop RDTSC.”)CR3 setter that is correct in both paging depths. Take a base, shift it left by four, sign-extend into bits 63:48, and OR in a PCID. (Expect the 4-level case to be four instructions and the 5-level case to need a different shift, because the sign field moves from four bits to six. That difference is the entire cost of CR4.LA57 to a kernel, and it is why a hypervisor that offers both has to present two CR3 formats to the same guest.)CR0 and CR4 tables and count the rows that say “reserved” and the one that says “reserved and always 1.” (Expect a great many, and expect the count to be a better predictor of architecture age than the instruction set. A field is reserved because something used it, and a field that is reserved-and-one is a scar from a promise somebody had to keep.)CR4.UMIP is a decision with a security argument on both sides: set it and your own debugging tools lose SGDT; leave it and a user process can read the GDT's address. (Expect there to be no answer that satisfies both, and expect every production kernel to have made the same choice for the same reason. The bit is one row of thirty-one and it decides more than thirty rows of anything else in this course.)Backwards, the previous concept measured the consequence of CR4.UMIP without naming it, and this concept names it and says the naming does not help. The privilege course's harness concept is where arch_prctl and the SMEP/SMAP report come from, and the difference between a report and a reading is the difference between the third row of this page and the first. The memory course's translation concept owns the why of a page directory pointer; this page is the reference for what is in the pointer.
Forwards. The fifth concept is the other set of registers — DR0 to DR7 — and it is the same problem in a smaller register with a nastier answer, because the debug registers have two authoritative layouts and this course cannot tell you which one its own CPU implements. Then the sixth and seventh concepts move from the control registers to what they control: the address space, and the four structures inside it.
Outward. Everything on this page is a kernel interface. That is the reason this course exists as a separate thing from the privilege course: the privilege course can measure a boundary from outside, and this course has to say “here is the complete interface, and here is the part of it you can see, and here is the boundary between those two things.” A hypervisor author and an OS author are the two readers this table is for, and they need different halves of it.