⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mmu.c

📁 微内核软实时操作系统
💻 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 + -