x86 cr3 and linux swqpper_pg_dir

2

In linux source code(2.6.18):

movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0          /* ..and set paging (PG) bit */
ljmp $__BOOT_CS,$1f     /* Clear prefetch and normalize %eip */

And also the load_cr3(pgdir) and write_cr3(x) macro:

#define load_cr3(pgdir) write_cr3(__pa(pgdir))

#define write_cr3(x) \
__asm__ __volatile__("movl %0,%%cr3": :"r" (x))

It seems like that the whole cr3 control register store the address of Page Directory. However, when I reference the intel ia-32Developer's_Manual it tells a different story. The following is what the intel manual said:

name      0.............11   12.................31
cr3       flags              address of page directory
PDE       flags              address of page table
PTE       flags              address of 4kb page frame

The manual says that the 20 MSB of cr3 stores the address of the page directory instead of the whole cr3 register. It is also reasonable since the page directory is exactly 4kb so the 12 LSB of the address is always zero.

Isn't it a little bit strange cuz the linux code just assign the address of the page directory to the cr3 instead of the 20 MSB of the swapper_pg_dir.

My question is that what exactly cr3 register is store, address or the format that intel manual suggest?

The following link is the intel manual: http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html

linux
assembly
asked on Stack Overflow Oct 27, 2011 by mike820324 • edited Aug 26, 2020 by HoldOffHunger

2 Answers

3

For 32-bit paging, it is mandatory that the address of the page directory is a multiple of 4096, i.e. its 12 LSB are zero. However, the opcode for setting cr3 loads 32 bits, not 20 bits. When cr3 is loaded, its 20 upper bits are used for the page directory address, and the lower 12 bits are interpreted as flags which may affect paging behaviour in newer processor versions. The "safe" setting for these flags is zero, and that's precisely what Linux does: it loads cr3 with a 32-bit value which happens to have its 12 LSB equal to zero (because that 32-bit value has been taken as a memory address which is a multiple of 4096).

answered on Stack Overflow Oct 27, 2011 by Thomas Pornin
2

There's nothing strange if swapper_pg_dir-__PAGE_OFFSET is a multiple of 4096.

Zeroes in CR3 LSBs are valid:

  • reserved bits have to be set to 0
  • newer (since i80386) functionality can be disabled by setting the relevant bits to 0 as well and generally that's the way to keep x86 CPUs backward compatible with old software.
answered on Stack Overflow Oct 27, 2011 by Alexey Frunze

User contributions licensed under CC BY-SA 3.0