📄 mmu.c
字号:
/*- * Copyright (c) 2005, Kohsuke Ohtani * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *//* * mmu.c - memory management unit support routines *//* * This module provides virtual/physical address translation for * intel x86 MMU. This kernel will do only page level translation * and protection and it does not use x86 segment mechanism. */#include <kernel.h>#include <bootinfo.h>#include <page.h>#include "cpu.h"#ifdef CONFIG_MMU/* * Map physical memory range into virtual address * * Returns 0 on success, or -1 on failure. * * Map type can be one of the following type. * PG_UNMAP - Remove mapping * PG_READ - Read only mapping * PG_WRITE - Read/write allowed * * Setup the appropriate page tables for mapping. If there is no * page table for the specified address, new page table is allocated. * * This routine does not return any error even if the specified * address has been already mapped to other physical address. * In this case, it will just override the existing mapping. * * In order to unmap the page, pg_type is specified as 0. * But, the page tables are not released even if there is no valid * page entry in it. All page tables are released when mmu_delmap() * is called when task is terminated. * * TODO: TLB should be flushed for specific page by invalpg in case of i486. */int mmu_map(pgd_t pgd, void *phys, void *virt, size_t size, int type){ long pg_type; page_table_t pte; void *pg; /* page */ phys = (void *)PAGE_ALIGN(phys); virt = (void *)PAGE_ALIGN(virt); size = PAGE_TRUNC(size); /* Build page type */ pg_type = 0; switch (type) { case PG_UNMAP: break; case PG_READ: pg_type = PTE_USER | PTE_PRESENT; break; case PG_WRITE: pg_type = PTE_USER | PTE_WRITE | PTE_PRESENT; break; } /* Map all pages */ while (size > 0) { if (pte_present(pgd, virt)) { /* Page table already exists for the address */ pte = pgd_to_pte(pgd, virt); } else { ASSERT(pg_type != 0); if ((pg = page_alloc(PAGE_SIZE)) == NULL) { printk("Error: MMU mapping failed\n"); return -1; } pgd[PAGE_DIR(virt)] = (u_long)pg | PDE_PRESENT | PDE_WRITE | PDE_USER; pte = phys_to_virt(pg); memset(pte, 0, PAGE_SIZE); } /* Set new entry into page table */ pte[PAGE_TABLE(virt)] = (u_long)phys | pg_type; /* Process next page */ phys += PAGE_SIZE; virt += PAGE_SIZE; size -= PAGE_SIZE; } flush_tlb(); return 0;}/* * Create new page map. * Returns a page directory on success, or NULL on failure. * This routine is called when new task is created. All page * map must have the same kernel page table in it. So, the kernel * page tables are copied to newly created map. */pgd_t mmu_newmap(void){ void *pg; pgd_t pgd, kern_pgd; u_long i; /* Allocate page directory */ if ((pg = page_alloc(PAGE_SIZE)) == NULL) return NULL; pgd = phys_to_virt(pg); memset(pgd, 0, PAGE_SIZE); /* Copy kernel page tables */ kern_pgd = phys_to_virt(KERNEL_PGD); i = PAGE_DIR(PAGE_OFFSET); memcpy(&pgd[i], &kern_pgd[i], 1024 - i); return pgd;}/* * Delete all page map. */void mmu_delmap(pgd_t pgd){ int i; page_table_t pte; flush_tlb(); /* Release all user page table */ for (i = 0; i < PAGE_DIR(PAGE_OFFSET); i++) { pte = (page_table_t)pgd[i]; if (pte != 0) page_free((void *)((u_long)pte & PTE_ADDRESS), PAGE_SIZE); } /* Release page directory */ page_free(virt_to_phys(pgd), PAGE_SIZE);}/* * Switch to new page directory * * This is called when context is switched. * Whole TLB are flushed automatically by loading * CR3 register. */void mmu_switch(pgd_t pgd){ set_cr3((u_long)virt_to_phys(pgd));}/* * Returns the physical address for the specified virtual address. * This routine checks if the virtual area actually exist. * It returns NULL if at least one page is not mapped. */void *mmu_extract(pgd_t pgd, void *virt, size_t size){ page_table_t pte; void *start, *end, *pg, *phys; start = (void *)PAGE_TRUNC(virt); end = (void *)PAGE_TRUNC(virt + size - 1); /* Check all pages exist */ for (pg = start; pg <= end; pg += PAGE_SIZE) { if (!pte_present(pgd, pg)) return NULL; pte = pgd_to_pte(pgd, pg); if (!page_present(pte, pg)) return NULL; } /* Get physical address */ pte = pgd_to_pte(pgd, start); pg = (void *)pte_to_page(pte, start); phys = pg + (virt - start); return phys;}/* * Initialize mmu * * Build kernel page directory. * Paging is already enabled in locore.S. And, physical address * 0-4M has been already mapped into kernel space in locore.S. * Now, all physical memory is mapped into kernel virtual address * as straight 1:1 mapping. User mode access is not allowed for * these kernel pages. * page_init() must be called before calling this routine. * * Note: This routine requires 4K bytes to map 4M bytes memory. So, * if the system has a lot of RAM, the "used memory" by kernel will * become large, too. For example, page table requires 512K bytes * for 512M bytes system RAM. */void mmu_init(void){ pgd_t kern_pgd; int nr_page, nr_pte; u_long pte_entry, pgd_index, *pte; int i, j; void *pg; kern_pgd = phys_to_virt(KERNEL_PGD); nr_page = boot_info->main_mem.size / PAGE_SIZE; nr_pte = (nr_page + 1023) / 1024; pgd_index = PAGE_DIR(PAGE_OFFSET); pte_entry = 0 | PTE_PRESENT | PTE_WRITE; /* Build kernel page tables for whole physical pages */ for (i = 0; i < nr_pte; i++) { /* Allocate new page table */ if ((pg = page_alloc(PAGE_SIZE)) == NULL) panic("mmu_init: no memory"); pte = phys_to_virt(pg); memset(pte, 0, PAGE_SIZE); /* Fill all entries in this page table */ for (j = 0; j < 1024; j++) { pte[j] = pte_entry; pte_entry += PAGE_SIZE; if (--nr_page <= 0) break; } /* Set the page table address into page directory. */ kern_pgd[pgd_index] = (u_long)pg | PDE_PRESENT | PDE_WRITE; pgd_index++; } /* Unmap address 0 for NULL pointer detection in kernel mode */ pte = phys_to_virt(kern_pgd[PAGE_DIR(PAGE_OFFSET)] & PDE_ADDRESS); pte[0] = 0; /* Flush translation look-aside buffer */ flush_tlb();}#endif /* CONFIG_MMU */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -