📄 kernelpage.c
字号:
//// Visopsys// Copyright (C) 1998-2005 J. Andrew McLaughlin// // This program is free software; you can redistribute it and/or modify it// under the terms of the GNU General Public License as published by the Free// Software Foundation; either version 2 of the License, or (at your option)// any later version.// // This program is distributed in the hope that it will be useful, but// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License// for more details.// // You should have received a copy of the GNU General Public License along// with this program; if not, write to the Free Software Foundation, Inc.,// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.//// kernelPage.c//// This file contains the C functions belonging to the kernel's // paging manager. It keeps lists of page directories and page tables,// and performs all the work of mapping and unmapping pages in the tables.#include "kernelPage.h"#include "kernelParameters.h"#include "kernelMemory.h"#include "kernelMultitasker.h"#include "kernelProcessorX86.h"#include "kernelLog.h"#include "kernelMisc.h"#include "kernelError.h"#include <string.h>// The kernel's page directory and first page tablestatic kernelPageDirectory *kernelPageDir = NULL;// A list of all the page directories and page tables we've created, so we // can keep track of all the physical vs. virtual addresses of these.static kernelPageDirectory pageDirMemory[MAX_PROCESSES];static kernelPageDirectory *pageDirList[MAX_PROCESSES];static volatile int numberPageDirectories = 0;static kernelPageTable pageTableMemory[MAX_PROCESSES];static kernelPageTable *pageTableList[MAX_PROCESSES];static volatile int numberPageTables = 0;// The physical memory location where we'll store the kernel's paging data.static unsigned kernelPagingData = 0;static volatile int initialized = 0;// Macros used internally#define getTableNumber(address) ((((unsigned) address) >> 22) & 0x000003FF)#define getPageNumber(address) ((((unsigned) address) >> 12) & 0x000003FF)static kernelPageTable *findPageTable(kernelPageDirectory *directory, int tableNumber){ kernelPageTable *table = NULL; int count; for (count = 0; count < numberPageTables; count ++) { if (pageTableList[count]->directory == directory) if (pageTableList[count]->tableNumber == tableNumber) { table = pageTableList[count]; break; } } return (table);}static unsigned countFreePages(kernelPageDirectory *directory){ // Returns the number of unallocated pages in all the page tables // of the supplied page directory kernelPageTable *table; int freePages = 0; int tableNumber = 0; int maxTableNumber = 0; if (directory == kernelPageDir) { tableNumber = getTableNumber(KERNEL_VIRTUAL_ADDRESS); maxTableNumber = PAGE_TABLES_PER_DIR; } else { tableNumber = 0; maxTableNumber = (getTableNumber(KERNEL_VIRTUAL_ADDRESS) - 1); } // Loop through all of the page tables for ( ; tableNumber < maxTableNumber; tableNumber ++) { table = findPageTable(directory, tableNumber); if (table) freePages += table->freePages; } return (freePages);}static int findFreeTableNumber(kernelPageDirectory *directory){ // This returns the first unused page table number. int tableNumber = 0; int maxTableNumber = 0; if (directory == kernelPageDir) { tableNumber = getTableNumber(KERNEL_VIRTUAL_ADDRESS); maxTableNumber = PAGE_TABLES_PER_DIR; } else { tableNumber = 0; maxTableNumber = (getTableNumber(KERNEL_VIRTUAL_ADDRESS) - 1); } for ( ; tableNumber < maxTableNumber; tableNumber ++) if (findPageTable(directory, tableNumber) == NULL) return (tableNumber); return (tableNumber = -1);}static int findFreePages(kernelPageDirectory *directory, int pages, void **virtualAddress){ // This function will find a range of unused pages in the supplied // page directory that is as large as the number of pages requested. // Sets a pointer representing the virtual address of the free pages // on success, and returns 0. On failure it returns negative. int status = 0; void *startAddress = NULL; int numberFree = 0; kernelPageTable *table = NULL; int tableNumber = 0; int maxTableNumber = 0; int pageNumber = 0; if (directory == kernelPageDir) { tableNumber = getTableNumber(KERNEL_VIRTUAL_ADDRESS); maxTableNumber = PAGE_TABLES_PER_DIR; } else { tableNumber = 0; maxTableNumber = (getTableNumber(KERNEL_VIRTUAL_ADDRESS) - 1); } // Loop through the supplied page directory. for ( ; tableNumber < maxTableNumber; tableNumber++) { // Get a pointer to this page table. table = findPageTable(directory, tableNumber); if (table == NULL) { numberFree = 0; startAddress = NULL; continue; } // Loop through the pages in this page table. If we find a free // page and numberFree is zero, set freeSpace to the corresponding // virtual address. If we find a used page, we reset both numberFree // and freeStart to NULL. If the table number is zero, skip the // first page for (pageNumber = (tableNumber == 0) ; pageNumber < PAGE_PAGES_PER_TABLE; pageNumber++) { if (table->virtual->page[pageNumber] == NULL) { if (numberFree == 0) startAddress = (void *) ((tableNumber << 22) | (pageNumber << 12)); numberFree++; if (numberFree >= pages) { *virtualAddress = startAddress; return (status = 0); } } else { numberFree = 0; startAddress = NULL; } } // If we fall through here, we're moving on to the next page table. } // If we fall through to here, we did not find enough free memory return (status = ERR_NOFREE);}static kernelPageTable *createPageTable(kernelPageDirectory *directory, int number, int kernel) // last param by Davide Airaghi{ // This function creates an empty page table and maps it into the // supplied page directory. int status = 0; kernelPageTablePhysicalMem *physicalAddr = NULL; kernelPageTableVirtualMem *virtualAddr = NULL; int kernelTableNumber = 0; int kernelPageNumber = 0; kernelPageTable *kernelTable = NULL; kernelPageTable *newTable = NULL; int count; // Allocate some physical memory for the page table physicalAddr = kernelMemoryGetPhysical(sizeof(kernelPageTablePhysicalMem), MEMORY_PAGE_SIZE, "page table"); if (physicalAddr == NULL) return (newTable = NULL); // Map it into the kernel's virtual address space. We can't use the // map function because it is the one that calls this function (we // don't want to get into a loop) when page table space is low. // If the directory is not the kernel directory, we have to be careful // to make sure that there's always one more free page available // in the kernel's directory for its own next page table if ((directory != kernelPageDir) && (countFreePages(kernelPageDir) < 2)) { // Recurse if (createPageTable(kernelPageDir, findFreeTableNumber(kernelPageDir),kernel) == NULL) // modified by Davide Airaghi // This is probably trouble for the kernel. We certainly don't care // about this user process return (newTable = NULL); } // Try to find 1 free page in kernel space for the table to occupy status = findFreePages(kernelPageDir, 1, (void **) &virtualAddr); if (status < 0) // Didn't find one return (newTable = NULL); // Get the kernel's kernelPageTable into which this new one will be mapped. kernelTableNumber = getTableNumber(virtualAddr); kernelPageNumber = getPageNumber(virtualAddr); kernelTable = findPageTable(kernelPageDir, kernelTableNumber); if (kernelTable == NULL) return (newTable = NULL); // Put the real address into the page table entry. Set the global bit, the // writable bit, and the page present bit. kernelTable->virtual->page[kernelPageNumber] = (unsigned) physicalAddr; kernelTable->virtual->page[kernelPageNumber] |= (PAGEFLAG_GLOBAL | PAGEFLAG_WRITABLE | PAGEFLAG_PRESENT); kernelTable->freePages--; // Clear this memory block, since kernelMemoryGetPhysical can't do it for us kernelMemClear((void *) virtualAddr, sizeof(kernelPageTableVirtualMem)); // Put our new table in the next available kernelPageTable slot of the // page table list, and increase the count of kernelPageTables newTable = pageTableList[numberPageTables++]; kernelMemClear((void *) newTable, sizeof(kernelPageTable)); // Fill in this page table newTable->directory = directory; newTable->tableNumber = number; newTable->freePages = PAGE_PAGES_PER_TABLE; newTable->physical = physicalAddr; newTable->virtual = virtualAddr; // Now we actually go into the page directory memory and add the // real page table to the requested slot number. Always enable // read/write and page-present directory->virtual->table[number] = (unsigned) newTable->physical; directory->virtual->table[number] |= (PAGEFLAG_WRITABLE | PAGEFLAG_PRESENT); // Set the 'user' bit, if this page table is not privileged if (directory->privilege != PRIVILEGE_SUPERVISOR || (kernel==0)) // last condition by Davide Airaghi directory->virtual->table[number] |= PAGEFLAG_USER; // A couple of extra things we do if this new page table belongs to the // kernel or one of its threads if (directory == kernelPageDir) { // Set the 'global' bit, so that if this is a Pentium Pro or better // processor, the page table won't be invalidated during a context // switch directory->virtual->table[number] |= PAGEFLAG_GLOBAL; // It needs to be 'shared' with all of the other real page directories. for (count = 0; count < numberPageDirectories; count ++) if (!(pageDirList[count]->parent)) pageDirList[count]->virtual->table[number] = kernelPageDir->virtual->table[number]; } // Return the table return (newTable);}static int deletePageTable(kernelPageDirectory *directory, kernelPageTable *table){ // This function is for the maintenance of our dynamic list of page table // pointers. It will remove the supplied page table from the list and // deallocate the memory that was reserved for it. Returns 0 on success, // negative otherwise. int status = 0; int kernelTableNumber = 0; int kernelPageNumber = 0; kernelPageTable *kernelTable = NULL; int listPosition = 0; int count; // First remove the table from the directory directory->virtual->table[table->tableNumber] = NULL; // If this page table belonged to the kernel or one of its threads, it // needs to be 'unshared' from all of the other real page directories. if (directory == kernelPageDir) for (count = 0; count < numberPageDirectories; count ++) if (!(pageDirList[count]->parent)) pageDirList[count]->virtual->table[table->tableNumber] = NULL; // Unmap it from the kernel's virtual address space. We can't use the // unmap function because it is the one that calls this function (we // don't want to get into a loop) when when a page table is empty // Get the kernel's kernelPageTable from which this new one will be // unmapped. kernelTableNumber = getTableNumber(table->virtual); kernelPageNumber = getPageNumber(table->virtual); kernelTable = findPageTable(kernelPageDir, kernelTableNumber); if (kernelTable == NULL) return (status = ERR_NOSUCHENTRY); // Erase the entry for the page of kernel memory that this table used kernelTable->virtual->page[kernelPageNumber] = NULL; kernelTable->freePages++; // Clear the TLB entry for the table's virtual memory kernelProcessorClearAddressCache(table->virtual); // Release the physical memory used by the table status = kernelMemoryReleasePhysical((void *) table->physical); if (status < 0) return (status = ERR_NOSUCHENTRY); // Now move the table to the unused list. This list is the same as // several other lists in the kernel. We remove this pointer from the // list by swapping its pointer in the list with that of the last item // in the list and decrementing the count (UNLESS: this is the last one, // or the only one). // Ok, now we need to find this page table in the list. for (listPosition = 0; listPosition < numberPageTables; ) if (pageTableList[listPosition] == table) break; else listPosition++; if ((listPosition == numberPageTables) || (pageTableList[listPosition] != table)) return (status = ERR_NOSUCHENTRY); // Decrease the count of page tables BEFORE the following operation numberPageTables--; if ((numberPageTables > 0) && (listPosition < numberPageTables)) { // Swap this item with the last item pageTableList[listPosition] = pageTableList[numberPageTables]; pageTableList[numberPageTables] = table; } // Return success return (status = 0);}static int findPageTableEntry(kernelPageDirectory *directory, void *virtualAddress, void **entry){ // Given a page directory and a virtual address, this function will find // the appropriate page table entry and return it. int status = 0; int tableNumber = 0; kernelPageTable *table = NULL; int pageNumber = 0; // virtualAddress is allowed to be NULL. if ((unsigned) virtualAddress % MEMORY_PAGE_SIZE) return (status = ERR_ALIGN); // Figure out which page table corresponds to this virtual address, and // get the page table tableNumber = getTableNumber(virtualAddress); pageNumber = getPageNumber(virtualAddress); table = findPageTable(directory, tableNumber); if (table == NULL) // We're hosed. This table should already exist. return (status = ERR_NODATA);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -