X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=kitten%2Farch%2Fx86_64%2Fmm%2Faspace.c;fp=kitten%2Farch%2Fx86_64%2Fmm%2Faspace.c;h=4078bf5cdfbd373ffc52bab256d338a298a0c6a1;hb=66a1a4c7a9edcd7d8bc207aca093d694a6e6b5b2;hp=0000000000000000000000000000000000000000;hpb=f7cf9c19ecb0a589dd45ae0d2c91814bd3c2acc2;p=palacios-OLD.git diff --git a/kitten/arch/x86_64/mm/aspace.c b/kitten/arch/x86_64/mm/aspace.c new file mode 100644 index 0000000..4078bf5 --- /dev/null +++ b/kitten/arch/x86_64/mm/aspace.c @@ -0,0 +1,449 @@ +/* Copyright (c) 2007,2008 Sandia National Laboratories */ + +#include +#include +#include +#include +#include /* TODO: remove */ +#include /* TODO: remove */ +#include + + +/** + * Architecture specific address space initialization. This allocates a new + * page table root for the aspace and copies the kernel page tables into it. + */ +int +arch_aspace_create( + struct aspace * aspace +) +{ + unsigned int i; + + /* Allocate a root page table for the address space */ + if ((aspace->arch.pgd = kmem_get_pages(0)) == NULL) + return -ENOMEM; + + /* Copy the current kernel page tables into the address space */ + for (i = pgd_index(PAGE_OFFSET); i < PTRS_PER_PGD; i++) + aspace->arch.pgd[i] = bootstrap_task.aspace->arch.pgd[i]; + + return 0; +} + + +/** + * Architecture specific address space destruction. This frees all page table + * memory that the aspace was using. + */ +void +arch_aspace_destroy( + struct aspace * aspace +) +{ + unsigned int i, j, k; + + xpte_t *pgd; /* Page Global Directory: level 0 (root of tree) */ + xpte_t *pud; /* Page Upper Directory: level 1 */ + xpte_t *pmd; /* Page Middle Directory: level 2 */ + xpte_t *ptd; /* Page Table Directory: level 3 */ + + /* Walk and then free the Page Global Directory */ + pgd = aspace->arch.pgd; + for (i = 0; i < pgd_index(PAGE_OFFSET); i++) { + if (!pgd[i].present) + continue; + + /* Walk and then free the Page Upper Directory */ + pud = __va(pgd[i].base_paddr << 12); + for (j = 0; j < 512; j++) { + if (!pud[j].present || pud[j].pagesize) + continue; + + /* Walk and then free the Page Middle Directory */ + pmd = __va(pud[j].base_paddr << 12); + for (k = 0; k < 512; k++) { + if (!pmd[k].present || pmd[k].pagesize) + continue; + + /* Free the last level Page Table Directory */ + ptd = __va(pmd[k].base_paddr << 12); + kmem_free_pages(ptd, 0); + } + kmem_free_pages(pmd, 0); + } + kmem_free_pages(pud, 0); + } + kmem_free_pages(pgd, 0); +} + + +/** + * Loads the address space object's root page table pointer into the calling + * CPU's CR3 register, causing the aspace to become active. + */ +void +arch_aspace_activate( + struct aspace * aspace +) +{ + asm volatile( + "movq %0,%%cr3" :: "r" (__pa(aspace->arch.pgd)) : "memory" + ); +} + + +/** + * Allocates a new page table and links it to a parent page table entry. + */ +static xpte_t * +alloc_page_table( + xpte_t * parent_pte +) +{ + xpte_t *new_table; + + new_table = kmem_get_pages(0); + if (!new_table) + return NULL; + + if (parent_pte) { + xpte_t _pte; + + memset(&_pte, 0, sizeof(_pte)); + _pte.present = 1; + _pte.write = 1; + _pte.user = 1; + _pte.base_paddr = __pa(new_table) >> 12; + + *parent_pte = _pte; + } + + return new_table; +} + + +/** + * Locates an existing page table entry or creates a new one if none exists. + * Returns a pointer to the page table entry. + */ +static xpte_t * +find_or_create_pte( + struct aspace * aspace, + vaddr_t vaddr, + vmpagesize_t pagesz +) +{ + xpte_t *pgd; /* Page Global Directory: level 0 (root of tree) */ + xpte_t *pud; /* Page Upper Directory: level 1 */ + xpte_t *pmd; /* Page Middle Directory: level 2 */ + xpte_t *ptd; /* Page Table Directory: level 3 */ + + xpte_t *pge; /* Page Global Directory Entry */ + xpte_t *pue; /* Page Upper Directory Entry */ + xpte_t *pme; /* Page Middle Directory Entry */ + xpte_t *pte; /* Page Table Directory Entry */ + + /* Calculate indices into above directories based on vaddr specified */ + const unsigned int pgd_index = (vaddr >> 39) & 0x1FF; + const unsigned int pud_index = (vaddr >> 30) & 0x1FF; + const unsigned int pmd_index = (vaddr >> 21) & 0x1FF; + const unsigned int ptd_index = (vaddr >> 12) & 0x1FF; + + /* Traverse the Page Global Directory */ + pgd = aspace->arch.pgd; + pge = &pgd[pgd_index]; + if (!pge->present && !alloc_page_table(pge)) + return NULL; + + /* Traverse the Page Upper Directory */ + pud = __va(pge->base_paddr << 12); + pue = &pud[pud_index]; + if (pagesz == VM_PAGE_1GB) + return pue; + else if (!pue->present && !alloc_page_table(pue)) + return NULL; + else if (pue->pagesize) + panic("BUG: Can't follow PUD entry, pagesize bit set."); + + /* Traverse the Page Middle Directory */ + pmd = __va(pue->base_paddr << 12); + pme = &pmd[pmd_index]; + if (pagesz == VM_PAGE_2MB) + return pme; + else if (!pme->present && !alloc_page_table(pme)) + return NULL; + else if (pme->pagesize) + panic("BUG: Can't follow PMD entry, pagesize bit set."); + + /* Traverse the Page Table Entry Directory */ + ptd = __va(pme->base_paddr << 12); + pte = &ptd[ptd_index]; + return pte; +} + + +/** + * Examines a page table to determine if it has any active entries. If not, + * the page table is freed. + */ +static int +try_to_free_table( + xpte_t * table, + xpte_t * parent_pte +) +{ + int i; + + /* Determine if the table can be freed */ + for (i = 0; i < 512; i++) { + if (table[i].present) + return -1; /* Nope */ + } + + /* Yup, free the page table */ + kmem_free_pages(table, 0); + memset(parent_pte, 0, sizeof(xpte_t)); + return 0; +} + + +/** + * Zeros a page table entry. If the page table that the PTE was in becomes + * empty (contains no active mappings), it is freed. Page table freeing + * continues up to the top of the page table tree (e.g., a single call may + * result in a PTD, PMD, and PUD being freed; the PGD is never freed by this + * function). + */ +static void +find_and_delete_pte( + struct aspace * aspace, + vaddr_t vaddr, + vmpagesize_t pagesz +) +{ + xpte_t *pgd; /* Page Global Directory: level 0 (root of tree) */ + xpte_t *pud; /* Page Upper Directory: level 1 */ + xpte_t *pmd; /* Page Middle Directory: level 2 */ + xpte_t *ptd; /* Page Table Directory: level 3 */ + + xpte_t *pge; /* Page Global Directory Entry */ + xpte_t *pue; /* Page Upper Directory Entry */ + xpte_t *pme; /* Page Middle Directory Entry */ + xpte_t *pte; /* Page Table Directory Entry */ + + /* Calculate indices into above directories based on vaddr specified */ + const unsigned int pgd_index = (vaddr >> 39) & 0x1FF; + const unsigned int pud_index = (vaddr >> 30) & 0x1FF; + const unsigned int pmd_index = (vaddr >> 21) & 0x1FF; + const unsigned int ptd_index = (vaddr >> 12) & 0x1FF; + + /* Traverse the Page Global Directory */ + pgd = aspace->arch.pgd; + pge = &pgd[pgd_index]; + if (!pge->present) + return; + + /* Traverse the Page Upper Directory */ + pud = __va(pge->base_paddr << 12); + pue = &pud[pud_index]; + if (!pue->present) { + return; + } else if (pagesz == VM_PAGE_1GB) { + if (!pue->pagesize) + panic("BUG: 1GB PTE has child page table attached.\n"); + + /* Unmap the 1GB page that this PTE was mapping */ + memset(pue, 0, sizeof(xpte_t)); + + /* Try to free PUD that the PTE was in */ + try_to_free_table(pud, pge); + return; + } + + /* Traverse the Page Middle Directory */ + pmd = __va(pue->base_paddr << 12); + pme = &pmd[pmd_index]; + if (!pme->present) { + return; + } else if (pagesz == VM_PAGE_2MB) { + if (!pme->pagesize) + panic("BUG: 2MB PTE has child page table attached.\n"); + + /* Unmap the 2MB page that this PTE was mapping */ + memset(pme, 0, sizeof(xpte_t)); + + /* Try to free the PMD that the PTE was in */ + if (try_to_free_table(pmd, pue)) + return; /* nope, couldn't free it */ + + /* Try to free the PUD that contained the PMD just freed */ + try_to_free_table(pud, pge); + return; + } + + /* Traverse the Page Table Entry Directory */ + ptd = __va(pme->base_paddr << 12); + pte = &ptd[ptd_index]; + if (pte->present) { + return; + } else { + /* Unmap the 4KB page that this PTE was mapping */ + memset(pme, 0, sizeof(xpte_t)); + + /* Try to free the PTD that the PTE was in */ + if (try_to_free_table(ptd, pme)) + return; /* nope, couldn't free it */ + + /* Try to free the PMD that contained the PTD just freed */ + if (try_to_free_table(pmd, pue)) + return; /* nope, couldn't free it */ + + /* Try to free the PUD that contained the PMD just freed */ + try_to_free_table(pud, pge); + return; + } +} + + +/** + * Writes a new value to a PTE. + * TODO: Determine if this is atomic enough. + */ +static void +write_pte( + xpte_t * pte, + paddr_t paddr, + vmflags_t flags, + vmpagesize_t pagesz +) +{ + xpte_t _pte; + memset(&_pte, 0, sizeof(_pte)); + + _pte.present = 1; + if (flags & VM_WRITE) + _pte.write = 1; + if (flags & VM_USER) + _pte.user = 1; + if (flags & VM_GLOBAL) + _pte.global = 1; + if ((flags & VM_EXEC) == 0) + _pte.no_exec = 1; + + if (pagesz == VM_PAGE_4KB) { + _pte.base_paddr = paddr >> 12; + } else if (pagesz == VM_PAGE_2MB) { + _pte.pagesize = 1; + _pte.base_paddr = paddr >> 21; + } else if (pagesz == VM_PAGE_1GB) { + _pte.pagesize = 1; + _pte.base_paddr = paddr >> 30; + } else { + panic("Invalid page size 0x%lx.", pagesz); + } + + *pte = _pte; +} + + +/** + * Maps a page into an address space. + * + * Arguments: + * [IN] aspace: Address space to map page into. + * [IN] start: Address in aspace to map page to. + * [IN] paddr: Physical address of the page to map. + * [IN] flags: Protection and memory type flags. + * [IN] pagesz: Size of the page being mapped, in bytes. + * + * Returns: + * Success: 0 + * Failure: Error Code, the page was not mapped. + */ +int +arch_aspace_map_page( + struct aspace * aspace, + vaddr_t start, + paddr_t paddr, + vmflags_t flags, + vmpagesize_t pagesz +) +{ + xpte_t *pte; + + /* Locate page table entry that needs to be updated to map the page */ + pte = find_or_create_pte(aspace, start, pagesz); + if (!pte) + return -ENOMEM; + + /* Update the page table entry */ + write_pte(pte, paddr, flags, pagesz); + + return 0; +} + + +/** + * Unmaps a page from an address space. + * + * Arguments: + * [IN] aspace: Address space to unmap page from. + * [IN] start: Address in aspace to unmap page from. + * [IN] pagesz: Size of the page to unmap. + */ +void +arch_aspace_unmap_page( + struct aspace * aspace, + vaddr_t start, + vmpagesize_t pagesz +) +{ + find_and_delete_pte(aspace, start, pagesz); +} + +int +arch_aspace_smartmap(struct aspace *src, struct aspace *dst, + vaddr_t start, size_t extent) +{ + size_t n = extent / SMARTMAP_ALIGN; + size_t i; + xpte_t *src_pgd = src->arch.pgd; + xpte_t *dst_pgd = dst->arch.pgd; + xpte_t *src_pge, *dst_pge; + + /* Make sure all of the source PGD entries are present */ + for (i = 0; i < n; i++) { + src_pge = &src_pgd[i]; + if (!src_pge->present && !alloc_page_table(src_pge)) + return -ENOMEM; + } + + /* Perform the SMARTMAP... just copy src PGEs to the dst PGD */ + for (i = 0; i < n; i++) { + src_pge = &src_pgd[i]; + dst_pge = &dst_pgd[(start >> 39) & 0x1FF]; + BUG_ON(dst_pge->present); + dst_pge = src_pge; + } + + return 0; +} + +int +arch_aspace_unsmartmap(struct aspace *src, struct aspace *dst, + vaddr_t start, size_t extent) +{ + size_t n = extent / SMARTMAP_ALIGN; + size_t i; + xpte_t *dst_pgd = dst->arch.pgd; + xpte_t *dst_pge; + + /* Unmap the SMARTMAP PGEs */ + for (i = 0; i < n; i++) { + dst_pge = &dst_pgd[(start >> 39) & 0x1FF]; + dst_pge->present = 0; + } + + return 0; +}