Virtual Addresses

A 64-bit virtual addresses are structured as follows:

63          48 47            39 38            30 29            21 20         12 11         0
+-------------+----------------+----------------+----------------+-------------+------------+
| Sign Extend |    Page-Map    | Page-Directory | Page-directory |  Page-Table |  Physical  |
|             | Level-4 Offset |    Pointer     |     Offset     |   Offset    |   Offset   |
+-------------+----------------+----------------+----------------+-------------+------------+
              |                |                |                |             |            |
              +------- 9 ------+------- 9 ------+------- 9 ------+----- 9 -----+---- 12 ----+
                                          Virtual Address

Header include/threads/vaddr.h, and include/threads/mmu.h define these functions and macros for working with virtual addresses:


#define PGSHIFT { /* Omit details */ }
#define PGBITS { /* Omit details */ }

The bit index (0) and number of bits (12) of the offset part of a virtual address, respectively.


#define PGMASK { /* Omit details */ }

A bit mask with the bits in the page offset set to 1, the rest set to 0 (0xfff).


#define PGSIZE { /* Omit details */ }

The page size in bytes (4,096).


#define pg_ofs(va) { /* Omit details */ }

Extracts and returns the page offset in virtual address va.


#define pg_no(va) { /* Omit details */ }

Extracts and returns the page number in virtual address va.


#define pg_round_down(va) { /* Omit details */ }

Returns the start of the virtual page that va points within, that is, va with the page offset set to 0.


#define pg_round_up(va) { /* Omit details */ }

Returns va rounded up to the nearest page boundary.

Virtual memory in Pintos is divided into two regions: user virtual memory and kernel virtual memory (see Virtual Memory Layout).

The boundary between them is KERN_BASE:


#define KERN_BASE { /* Omit details */ }

Base address of kernel virtual memory. It defaults to 0x8004000000. User virtual memory ranges from virtual address 0 up to KERN_BASE. Kernel virtual memory occupies the rest of the virtual address space.


#define is_user_vaddr(vaddr) { /* Omit details */ }
#define is_kernel_vaddr(vaddr) { /* Omit details */ }

Returns true if va is a user or kernel virtual address, respectively, false otherwise.


The x86-64 doesn't provide any way to directly access memory given a physical address. This ability is often necessary in an operating system kernel, so Pintos works around it by mapping kernel virtual memory one-to-one to physical memory. That is, virtual address > KERN_BASE accesses physical address 0, virtual address KERN_BASE + 0x1234 accesses physical address 0x1234, and so on up to the size of the machine's physical memory. Thus, adding KERN_BASE to a physical address obtains a kernel virtual address that accesses that address; conversely, subtracting KERN_BASE from a kernel virtual address obtains the corresponding physical address.

Header include/threads/vaddr.h provides a pair of functions to do these translations:


#define ptov(paddr) { /* Omit details */ }

Returns the kernel virtual address corresponding to physical address pa, which should be between 0 and the number of bytes of physical memory.


#define vtop(vaddr) { /* Omit details */ }

Returns the physical address corresponding to va, which must be a kernel virtual address.

Header include/threads/mmu.h provides operations on page table:


#define is_user_pte(pte) { /* Omit details */ }
#define is_kern_pte(pte) { /* Omit details */ }

Query whether the page table entry (PTE) is owned by user or kernel, respectively.


#define is_writable(pte) { /* Omit details */ }

Query whether virtual address pointed by the page table entry (PTE) is wriatable or not.


typedef bool pte_for_each_func (uint64_t *pte, void *va, void *aux);
bool pml4_for_each (uint64_t *pml4, pte_for_each_func *func, void *aux);

For each valid entry under PML4, apply FUNC with auxillary value AUX. VA represents the virtual address of the entry. If pte_for_each_func returns false, stop iteration and return false.

Below shows an example func that could be fed to pml4_for_each:

static bool
stat_page (uint64_t *pte, void *va,  void *aux) {
        if (is_user_vaddr (va))
                printf ("user page: %llx\n", va);
        if (is_writable (va))
                printf ("writable page: %llx\n", va);
        return true;
}

results matching ""

    No results matching ""