Search code examples
coperating-systemxv6

What's the mechanism behind P2V, V2P macro in Xv6


I know there is a mapping mechanism in how a virtual address turns out into a physical.

Just like the following, a linear address contains three parts

  1. Page Directory index
  2. Page Table index
  3. Offset

Here is the illustration:

Page Translation Picture

Now, when I take a look at the source code of Xv6 in memorylayout.h

#define V2P(a) (((uint) (a)) - KERNBASE)
#define P2V(a) (((void *) (a)) + KERNBASE)

#define V2P_WO(x) ((x) - KERNBASE)    // same as V2P, but without casts
#define P2V_WO(x) ((x) + KERNBASE)    // same as P2V, but without casts

How can the V2P or P2V work correctly without doing the process of the address translation?


Solution

  • The V2P and P2V macros doesn't do more than you think they do. They are just subtract and add the KERNBASE constant, which marked at 2 GB.

    It seems you understand the MMU's hardware mapping mechanism correctly. The mapping rules are saved by a per process page tables. Those tables forms the process's virtual space.

    Specifically, in XV6, process's virtual space structure is being built (by the appropriate mappings) as follows: virtual address space layout

    As the diagram above shows, XV6 specifically builds the process's virtual address space so that 2GB - 4GB virtual addresses maps to 0 to PHYSTOP physical addresses (respectively). As explained in the XV6 official commentary:

    Xv6 includes all mappings needed for the kernel to run in every process’s page table; these mappings all appear above KERNBASE. It maps virtual addresses KERNBASE:KERNBASE+PHYSTOP to 0:PHYSTOP.

    The motivation for this decision is also specified:

    One reason for this mapping is so that the kernel can use its own instructions and data. Another reason is that the kernel sometimes needs to be able to write a given page of physical memory, for example when creating page table pages; having every physical page appear at a predictable virtual address makes this convenient.

    In other words, because the kernel made all page tables map 2GB virtual to 0 physical (and up to PHYSTOP), we can easily find the physical address of a virtual address that is above 2GB. For example: The physical address of the virtual address 0x10001000 can easily found: it is 0x00001000 we just subtract 2GB, because that's how we mapped it to be.

    This "workaround" can help the kernel do the conversion easily, for example, for building and manipulating page tables (where physical address needs to be calculated and written to memory).

    Of course that the above "workaround" is not free, as this makes us waste valuable virtual address space (2GB of it) because now every physical address has at least already 1 virtual address. even if we never going to use it. that leaves the real process only the 2GB left from the entire virtual address (it is 4 GB in total, because we use 32bit to count addresses). This also was explained in the XV6 official commentary:

    A defect of this arrangement is that xv6 cannot make use of more than 2 GB of physical memory

    I recommend you reading more about this manner in the XV6 commentary under "Process address space" header. XV6 Commentary