x86_64 icon indicating copy to clipboard operation
x86_64 copied to clipboard

Removing CR3 check from RecursivePageTable?

Open CWood1 opened this issue 4 years ago • 3 comments

This issue is mostly for discussion purposes, to clarify my understanding.

RecursivePageTable::new() requires the passed page table to be active, checked through CR3. However, I'm not entirely sure I grok why that is. Nothing else in RecursivePageTable references CR3 in any way, other than that one particular check.

The reason I'm looking to remove this, is because I'd like to be able to have my kernel map in new page tables, for processes/drivers/etc, by mapping in the new page table at some address within the kernel space, and calling that "recursive" - the algorithms would all be the same, it would simply be a different P4 being referenced. In this way, I don't need to have every page table mapped into the kernel constantly for no reason, I don't need to manually manage pages when there's a perfectly good implementation already written, and generally it would simplify the setup.

However, I don't consider myself familiar enough with the implementation to know why the CR3 check is in place, and whether or not it is safe to remove. Could somebody with this knowledge advise please?

CWood1 avatar May 23 '20 13:05 CWood1

See https://os.phil-opp.com/paging-implementation/#recursive-page-tables for an overview how recursive page tables work. The reason for the CR3 check is that the addresses derived from the recursive index only work if the given level 4 table is the active page table in the CPU. You can't just reference a different level 4 table because then the calculated addresses for the level 3, 2, and 1 tables are invalid (because the CPU always uses the level 4 table referenced by the CR3 register for the translation).

It is possible to access the page table hierachy of an inactive level 4 table, but it is a bit more complicated. The idea is to point the recursive entry of the active level 4 table to the inactive level 4 table, which is recursively mapped too. This way, you now access the tables of the inactive page table through recursive addresses. The challenge of this approach is to make the original level 4 table recursive again after you're done modifying the inactive page table hierarchy. The problem is that the original level 4 table is no longer accessible through recursive addresses, so you have to create some kind of temporary mapping for it in the new page table hierarchy in order to access it. See the (outdated) https://os.phil-opp.com/remap-the-kernel/#overview for more details.

Since the RecursivePageTable type can easily lead to undefined behavior if the passed page table is not active, we check that in the new method. In case you're really sure that the using a specific inactive level 4 table is fine in your case (e.g. when using the approach described above), you can use the unsafe RecursivePageTable::new_unchecked method to skip the CR3 check.

I hope this answers your question!

phil-opp avatar May 24 '20 12:05 phil-opp

@phil-opp Do you think we should update the Safety section of RecursivePageTable::new_unchecked to reflect the above? Right now the docs are unclear here.

josephlr avatar Oct 16 '20 09:10 josephlr

Yes, I agree that the current docs are a bit vague.

phil-opp avatar Oct 18 '20 10:10 phil-opp