I'm reading the book "Understanding the Linux Kernel" and I'm wondering how Linux folds the Page Upper Directory or Page Middle directory in a 3- or 2-level paging system.
Based on my understanding:
This seems contradictory to me: if the 3 assumptions above are true, it means that the address translation will be conducted through these entries:
Page Global Directory Entry -> (Singleton) Page Upper Directory Entry -> (Singleton) Page Middle Directory Entry -> Page Table Entry
However, how come that the same Page Middle Directory Entry can point to different Page Table Entries?
I don't remember the book very well. I think what they mean is that the Page Upper Directory and Page Middle Directory are not used and that Page Global Directory is the table which selects the page table. The rest of your assumptions seem correct. Let's say you have virtual address 0x12345678 in 32 bits mode. 0x678 is the offset in the physical page (12 bits). The 10 bits in the middle are the offset in Page Table and the 10 most significant bits are the offset in Page Global Directory.
For 32-bit architectures with no Physical Address Extension, two paging levels are sufficient. Linux essentially eliminates the Page Upper Directory and the Page Middle Directory fields by saying that they contain zero bits. However, the positions of the Page Upper Directory and the Page Middle Directory in the sequence of pointers are kept so that the same code can work on 32-bit and 64-bit architectures. The kernel keeps a position for the Page Upper Directory and the Page Middle Directory by setting the number of entries in them to 1 and mapping these two entries into the proper entry of the Page Global Directory.
What the author means here is that the 2 levels in the middle are eliminated. Only the sequence of pointers is maintained when changing the tables (when swapping the process). This is to maintain the same code on 32 bits or 64 bits CPUs.
Where you don't understand is that to use 32 bits mode, the CPU needs to be in 32 bits mode. If you use 32 bits Linux, Linux won't boot the processor in 64 bits mode. The CPU will thus be in a "transition" state between 32 bits mode and 64 bits mode (long mode). This was done by Intel and AMD so that their CPUs maintain backward compatibility. You can thus boot the CPU into 32 bits mode and decide not to switch in 64 bits mode. This is done by setting certain bits into control registers of the processor. When the processor is in 32 bits mode, it translates virtual addresses using only 2 levels of page tables instead of 4.
Thanks! Yea, that makes sense that CPU in 32 bits mode will only use 2 levels of page tables, and thanks for pointing out my misunderstanding! And just to confirm, so for 32-bit architecture, there isn't a specific entry for Page Upper Directory and Page Middle Directory, it's just that the kernel maps the Page Upper Directory entry (and Page Middle Directory entry) back to the corresponding Page Global Directory entry: like for 0x12345678, the Page Upper Directory entry is the same as the Page Global Directory entry for the 10 most significant bits of 0x12345678, right?
I'm not sure what the kernel does exactly. It is pretty complex. I'd have to read further. What's important to understand is that the kernel forgets about these levels in some way. Another thing is that the actual tables in RAM don't have any reference to PUD or PMD. This is necessary because the processor is just a machine. For 32 bits mode, it will translate a virtual address using 2 levels of page tables regardless of what it finds in RAM. The kernel must make sure that the Global Directory doesn't contain any actual reference to the Upper or Middle directory.