Page 1 of 1

Virtual memory handling 1:1 mapping vs Recursion

Posted: Sun May 09, 2021 12:12 pm
by finarfin
I'm starting to implement the foundations of the virtual memory manager for my kernel, and now i need to decide how the kernel should access data in the Page Directories/table.

I knew about the recursion technique, implemented it in the past and probably should not take long to implement it again. But searching on alternatives i also saw that many peoples decide do do a 1-1 mapping of physical memory in a reserved address space (i.e. somewhere above 0xffffffff8000000).

Now the idea looks appealing to me so i was trying to read more about it. Found this blog post that explains different techniques pretty well: https://os.phil-opp.com/paging-implementation/, and according to the post:
This approach allows our kernel to access arbitrary physical memory, including page table frames of other address spaces. The reserved virtual memory range has the same size as before, with the difference that it no longer contains unmapped pages.

The disadvantage of this approach is that additional page tables are needed for storing the mapping of the physical memory. These page tables need to be stored somewhere, so they use up a part of physical memory, which can be a problem on devices with a small amount of memory.
So apparently on a modern computer memory space shouldn't be a problem, and i also suppose that in a multitasking environment, i can reuse the same variables that contains directory/tables of the physical memory mapping on every process, since they are kind of common between all processes, i need to do the mapping only once at boot time. Correct?

While on the other hand disadvantages of recursion are:
  • It occupies a large amount of virtual memory (512GiB). This isn't a big problem in the large 48-bit address space, but it might lead to suboptimal cache behavior.
  • It only allows accessing the currently active address space easily. Accessing other address spaces is still possible by changing the recursive entry, but a temporary mapping is required for switching back. We described how to do this in the (outdated) Remap The Kernel post.
  • It heavily relies on the page table format of x86 and might not work on other architectures.
I don't think that the first one is a real problem, but maybe the last one is the more important, i'm not planning to support multiple archtectures, but if one day i or someone wants to try to port the kernel on another achitecture it could cause taht more code needs to be rewritter.

So here is my question: Why 90% of the tutorials and 90% of amateur oses are using recursion instead of different techniques that looks even easier to understand (it took me a while to grab the logic behind recursion).

It has some hidden advantages?

Apparently even linux is mapping the whole phyisical memory on the ram:
ffff888000000000 | -119.5 TB | ffffc87fffffffff | 64 TB | direct mapping of all physical memory (page_offset_base)

full mapping available here: https://www.kernel.org/doc/Documentatio ... _64/mm.txt

I can't see many disadvantages of the memory mapping mode, if there any, can one can help me to choose? What are the cons or pros of both?

Is there something that is going to be more complicated to implement chosing one or another in the future?

Thanks to everyone who help! :)

Re: Virtual memory handling 1:1 mapping vs Recursion

Posted: Sun May 09, 2021 1:47 pm
by rdos
It primarily depends on the operation mode. In 32-bit mode, it's impossible to map all physical memory in the linear address space, and a bitmap approach will use both minimal linear and physical memory. In long mode, this is not an issue, but having a lock-free physical memory allocator could be an advantage for lazy mapping of physical memory, and it's impossible to do a lock free allocator with linked lists.

Also, I consider it a potential safety issue to have all physical memory mapped in kernel space. In essence, what you cannot access you cannot corrupt.

Re: Virtual memory handling 1:1 mapping vs Recursion

Posted: Sun May 09, 2021 3:53 pm
by finarfin
rdos wrote:It primarily depends on the operation mode. In 32-bit mode, it's impossible to map all physical memory in the linear address space, and a bitmap approach will use both minimal linear and physical memory. In long mode, this is not an issue,
Sorry forgot to mention that i'm implementing a 64bit kernel

rdos wrote: Also, I consider it a potential safety issue to have all physical memory mapped in kernel space. In essence, what you cannot access you cannot corrupt.
Ok that makes sense.

But now i have a question, in 32bit usually the entry designed as recursive entry is the last one 1023 (counting from 0), now in 64 bit, with the kernel mapped in the higher half, at 0xffffffff8000000 in the p3 table the 511th entry is alreadyt taken, so i guest i should pick a different entry for recursive mapping. Is there already a convention for that? A "safe" number, or i can pick any? (maybe close to the end of the table?)

Thanks!

Re: Virtual memory handling 1:1 mapping vs Recursion

Posted: Sun May 09, 2021 4:58 pm
by kzinti
I use entry 510. But you can pick any entry you want. I would think that you want something in higher address space as to not interfere with user space addresses.

Code: Select all

0xFFFFFF00 00000000 - 0xFFFFFF7F FFFFFFFF   Page Mapping Level 1 (Page Tables)
0xFFFFFF7F 80000000 - 0xFFFFFF7F BFFFFFFF   Page Mapping Level 2 (Page Directories)
0xFFFFFF7F BFC00000 - 0xFFFFFF7F BFDFFFFF   Page Mapping Level 3 (PDPTs / Page-Directory-Pointer Tables)
0xFFFFFF7F BFDFE000 - 0xFFFFFF7F BFDFEFFF   Page Mapping Level 4 (PML4)

Re: Virtual memory handling 1:1 mapping vs Recursion

Posted: Mon May 10, 2021 12:47 am
by rdos
finarfin wrote: But now i have a question, in 32bit usually the entry designed as recursive entry is the last one 1023 (counting from 0), now in 64 bit, with the kernel mapped in the higher half, at 0xffffffff8000000 in the p3 table the 511th entry is alreadyt taken, so i guest i should pick a different entry for recursive mapping. Is there already a convention for that? A "safe" number, or i can pick any? (maybe close to the end of the table?)

Thanks!
I think you can use any entry. I'm in 32-bit mode, and I don't use the last entry. I actually use two entries. One is for a system mapping that will have all memory allocated in system memory space mapped. This is where system pages are allocated. The other is for accessing the memory of the current process. The pagefault handler will copy entries from system memory space to the process.

Re: Virtual memory handling 1:1 mapping vs Recursion

Posted: Tue May 11, 2021 3:21 pm
by finarfin
Ok so now let's assume i'm going to use recursion. this scenario rise more questions:
  • The kernel is mapped on the higher half, and the first page data structures created during bootup are are accessible directly, is safe/correct enough to do the initialization mappings (i.e. Framebuffer, ACPI/RSDT stuff, etc), or is considered bad habit?
  • This is more for curiosity, does anyone here use the available bits for whatever reason? (i was thinking they can be useful to identify especially the non freeable/unmappable area, or kernel specific addresses or just that these are identity mapped addresse. So the OS during the PF can decide what to do also reading these values, is taht useful? Or that is easier to handle with VMM?
  • Using 510th PML4 entry for recursion, should be safe enough to prevent the case of a resource that when identity mapped is going to need to stay exactly on PML4[510], i think that this address should be high enough, correct?
  • Referring to my first question, if the kernel is mapped in the higher half with PML4, PML3, PML2, PML1 mapped, and since the kernel technically need to be always the same, is safe to use them as a template when i will implement multitasking to create all the initial mappings (Without having to reinitialize every time the data structure from scratch?)
  • [And for example if the kernel is contained in one or more page_table/s, can i reuse the same page_table/s when creating a new address space for the new process? What i mean is that if i have the variable page_table[0...511] that has the mapping for the kernel, when creating a new task and initializing it's address space. Can i reuse the same page_table for the new address space, since this is never going to change and all entries are already taken, so there is no risk of allcoating them.
  • And i think that until i don't start to write multi-tasking stuff, all that i need to worry is just to map the resources required by the kernel to work properly (and i think that before multitasking i have to write the virtual memory manager :D)
Thanks! :)

Re: Virtual memory handling 1:1 mapping vs Recursion

Posted: Tue May 11, 2021 9:16 pm
by sj95126
finarfin wrote:Referring to my first question, if the kernel is mapped in the higher half with PML4, PML3, PML2, PML1 mapped, and since the kernel technically need to be always the same, is safe to use them as a template when i will implement multitasking to create all the initial mappings (Without having to reinitialize every time the data structure from scratch?)
If I'm understanding your question correctly, no, you do not need to duplicate all four levels of the paging tables to make the kernel higher half available to user process page tables. You only need to duplicate the entries at the PML4 level, and multiple PML4s can point to the same PDPTs.

For example, let's say you have your initial kernel PML4 where "K" is an entry in the higher half of the table, which points to PDPT "A", which points to PD "B", which points to PT "A". That would look like this:

Code: Select all

+-----+
|  K  | ---> "A" ---> "B" ----> "C"
|     |
|     |
+-----+
Now you create a user process PML4 with a lower half entry pointing to PDPT "D", etc. You can add an entry in the higher half for the kernel region that points to exactly the same page tables as before, in addition to the user space page tables, like this:

Code: Select all

+-----+
|  K  | ---> "A" ---> "B" ---> "C"
|     |
|  U  | ---> "D" ---> "E" ---> "F"
+-----+
Of course, make sure that the entries for "K" have the appropriate supervisor-only bits set so userspace can't see them when CPL=3.

Re: Virtual memory handling 1:1 mapping vs Recursion

Posted: Tue May 11, 2021 10:16 pm
by Octocontrabass
finarfin wrote:Using 510th PML4 entry for recursion, should be safe enough to prevent the case of a resource that when identity mapped is going to need to stay exactly on PML4[510], i think that this address should be high enough, correct?
You don't need to identity map anything except code to enable or disable paging. You can't disable paging in long mode, so you'll never identity map anything above 4GiB.