x86pagingvirtual-memorypage-tables

linear address space x86 - bits segmentation calculation


I have a problem which I am not sure how to solve correctly based on the final answer I have. Given that :

  1. Page Size is 4KB (2^12).
  2. Virtual address space is 32 bit.
  3. Physical memory size is 16TB (2^44B).
  4. PTE stores only PFN.
  5. Inner page tables are in a size of a page.

I need to calculate bits segmentation of the 32bits. The final answer (with no explanation) is 10 bits for DIR, 10 bits for Inner Table, 12 bits for offset. I do understand where the 12 offset's bits come from (log2(#Page)), but then I am stuck with the other 20 bits which I don't understand how to split. Thanks in advance :)


Solution

  • It appears this isn't actually about x86 page tables. The sizes of other things happen to be the same as legacy x86 (non-PAE), but they're assuming a different PTE format.

    The answer given makes sense if we take PTE stores only PFN to mean there are no bits for present / dirty / permissions / etc that actual x86 has, so literally every bit of each PTE is part of the PFN. So for 44-bit physical addresses with 12 of those bits being offset-within-page, PTEs need to hold 44 - 12 = 32-bit page-frame (physical page) numbers. That gives you 4-byte PTEs, same size as legacy x86 (non-PAE, non-x86-64).

    But in real x86, the low 12 bits of the PTE are flags, so no shifting, only mask/or, is needed to split or merge a physical address to or from a PTE's PFN field and an offset-within-page (low 12 bits that don't need to be translated). https://wiki.osdev.org/Paging#MMU has diagrams of the format for the page-directory and page-table entries, both of which are basically the same. With the high 20 bits being a PFN, legacy x86 paging translates 32-bit virtual to 32-bit physical addresses.

    Given 4-byte PTEs and everything else the same as actual x86 (except for physical address size), you end up with the same geometry as actual x86: 1024 PTEs per page, so 10 bits per level of the page table. 10 + 10 + 12 = 32 so this handles 32-bit virtual addresses.

    Inner page tables are in a size of a page. are what tells you that PTEs come in contiguous groups of 4096 bytes, 1024 PTEs. If not for that, the choice would be arbitrary, and you could have a 3-level design like 6 bits (top level), 10 bits (mid level), 4 bits (bottom level). But then to pack thing efficiently, the OS's allocator would have to manage smaller units of memory.


    By comparison, PAE and x86-64 page-table formats have 8-byte PTEs, allowing wider physical addresses while still having room for the necessary flag/metadata bits, still in the low 12. For x86-64, this means 4 levels that translate 9 bits each => 4x9 + 12 = 48-bit virtual. Why in x86-64 the virtual address are 4 bits shorter than physical (48 bits vs. 52 long)?


    This is of course totally impractical for real use, and not surprising that you wouldn't think of a design this dumb / unrealistic.

    Every set of page tables would need to be "complete" (all 1024 2nd-level tables populated) because there's no way for a PTE to indicate that it's not present. (Or in the first level of the page table, that the whole 4MiB is not present in the PTE). So you could never have a #PF exception, defeating multiple of the major benefits of virtualizing memory.

    Any memory regions you didn't have space allocated for could all map to the same physical page somewhere, so you don't actually need 4GiB of physical memory per process. (And could even share the 2nd-level page tables for those regions). But the effect of out-of-bounds access would be reading / writing whatever garbage was there from other processes, instead of actually detecting a software bug via a segfault.

    You might be able to use segment limits to cut off usage of some parts of virtual address space, probably at least allowing protection of a kernel from user-space. And perhaps doing a little more, but IDK about really replacing #PF exceptions with #SS and #GP exceptions, unless this hypothetical machine has a mechanism to communicate the fault address to the exception handler. (Like CR2 for #PF in actual x86, giving the kernel the final linear virtual address that the addressing mode + segment base tried to access but didn't find present and allowed in the page tables.)