So, basically the problem is how to access memory you only have a physical address of, at least during page frame creation. There are three common approaches:
1. Recursive mapping
You can make one entry in the PDPT point to the PDPT. That way, all paging structures are always available to be tampered with. The downside is that that requires one entire entry in the highest-level paging structure. While in 32-bit non-PAE paging, this would only be the 1024th part of all virtual memory, and in 64-bit paging it would be the 512th part, in 32-bit PAE paging it is one quarter of virtual address space. Also, in 32-bit PAE paging in particular, you would have to align the PDPT to page alignment and make sure the rest of the page is kept as zero for the trick to work. You can also only ever access the current paging structures this way. Changing the structures of another process is going to be hard, and accessing any physical memory besides paging structures will still require temporary mappings, which leads me to:
2. Temporary mapping
You prepare some page mapping and define it as a CPU-local temporary address. Say you use 0xFFFF_F000 as your temp address, then you make sure the last PDPT and PDT entries are always filled with valid addresses, and you have the last PT always mapped somewhere. Then, if you need to change some physical memory that is not yet mapped, you map it to the temporary address, invalidate the TLB for the address, and access it. That way, you can, for example, zero out a page of memory before linking it in the paging structures (else you would have a mapping to god-knows-where for a moment, and could get invalid TLB entries you didn't even realize).
3. Linear mapping
You map as much of physical memory as you need to some starting address in your kernel. E.g. if you have a higher-half kernel, you map up to 1GB of physical memory to 0xC000_0000. That way, getting a virtual address for a physical one is simply an addition. The downside is you can only handle a limited amount of physical memory this way.
In case you are wondering, Linux uses number three but supplements it with a dynamic mapping approach for things where that is possible. That is, it maps the first 768 MB of physical memory to 0xC000_0000, and in that range, physical-to-virtual translation is easy. For the remaining 256 MB of virtual kernel space, the mappings are dynamically swapped out so that other memory may be used. There is however a discussion going on about removing that second part from the kernel, because it is really big an complicated (and error-prone), and these days you tend not to have 32-bit systems with lots of physical memory. You have 32-bit systems with little memory (up to 1GB) and systems with more memory tend to be 64-bit, where the same problem really doesn't arise (my current approach can handle up to 64 TB of physical memory, so there is a bit of headroom left).
I would suggest you start with number three for now. Being able to handle 1GB of physical memory is better than nothing, right?
_________________ Carpe diem!
|