/// <summary> /// Creates a new page directory using only physical memory (used in Init) /// </summary> /// <param name="flags">The flags</param> /// <returns>The page directory</returns> public static PageDirectory *CreateNewDirectoryPhysically(PageFlags flags) { // Allocate a new block of physical memory to store our physical page in PageDirectory *directory = (PageDirectory *)Heap.AlignedAlloc(0x1000, sizeof(PageDirectory)); directory->PhysicalDirectory = directory; if (directory == null) { Panic.DoPanic("directory == null"); } // Allocate the tables for (int i = 0; i < 1024; i++) { PageTable *table = (PageTable *)PhysicalMemoryManager.Alloc(); if (table == null) { Panic.DoPanic("table == null"); } Memory.Memclear(table, sizeof(PageTable)); // Note: At this point, virtual address == physical address due to identity mapping directory->PhysicalTables[i] = (int)table | (int)flags; directory->VirtualTables[i] = (int)table; } return(directory); }
/// <summary> /// Maps a page in a directory /// </summary> /// <param name="directory">The page directory</param> /// <param name="phys">The physical address</param> /// <param name="virt">The virtual address</param> /// <param name="flags">The flags</param> public static void MapPage(PageDirectory *directory, int phys, int virt, PageFlags flags) { // Get indices int pageIndex = virt / 0x1000; int tableIndex = pageIndex / 1024; // Set page using its virtual address PageTable *table = (PageTable *)directory->VirtualTables[tableIndex]; table->Pages[pageIndex & (1024 - 1)] = ToFrameAddress(phys) | (int)flags; }
/// <summary> /// Maps a physical address (range) to a free virtual address (range) /// </summary> /// <param name="directory">The page directory</param> /// <param name="phys">The physical address</param> /// <param name="size">The size of the range</param> /// <param name="flags">The flags</param> /// <returns>The virtual address</returns> public static void *MapToVirtual(PageDirectory *directory, int phys, int size, PageFlags flags) { int sizeAligned = (int)AlignUp((uint)size) / 0x1000; int free = bitmap.FindFirstFreeRange(sizeAligned, true); int virt = free * 0x1000; for (int i = 0; i < sizeAligned; i++) { int offset = i * 0x1000; MapPage(directory, phys + offset, virt + offset, flags); PhysicalMemoryManager.Set(phys + offset); } Paging.setDirectoryInternal(Paging.CurrentDirectoryPhysical); return((void *)virt); }
/// <summary> /// Sets the page directory /// </summary> /// <param name="directoryVirtual">The virtual address of the page directory</param> /// <param name="directoryPhysical">The physical address of the page directory</param> public static void SetPageDirectory(PageDirectory *directoryVirtual, PageDirectory *directoryPhysical) { /** * Note: This is a way to solve the issue that a page directory is not available * in every other page directory (due to security). * So: upon a task switch, setting a directory physically works for the CPU * but we also need its virtual address. * Setting the virtual address with a getter and updating the CR3 with the physical address * also wont work, because it's not guaranteed you can read the page directory * as it may not be available in the current task. */ CurrentDirectory = directoryVirtual; CurrentDirectoryPhysical = directoryPhysical; setDirectoryInternal(directoryPhysical); }
/// <summary> /// Frees a page directory /// </summary> /// <param name="directory">The directory</param> public static void FreeDirectory(PageDirectory *directory) { if (directory == CurrentDirectory) { Panic.DoPanic("Tried to free the current page directory"); } if (directory == KernelDirectory) { Panic.DoPanic("Tried to free the kernel page directory"); } // The directory was cloned, so it was allocated in one huge block PageDirectory *virt = CurrentDirectory; PageDirectory *phys = CurrentDirectoryPhysical; SetPageDirectory(KernelDirectory, KernelDirectory); Heap.Free(directory); SetPageDirectory(virt, phys); }
/// <summary> /// Gets the physical address from a virtual address /// </summary> /// <param name="pageDir">The page directory to look into</param> /// <param name="virt">The virtual address</param> /// <returns>The physical address</returns> public static void *GetPhysicalFromVirtual(PageDirectory *pageDir, void *virt) { // Get indices int address = (int)virt; int remaining = address % 0x1000; int frame = address / 0x1000; // Find corresponding table to the virtual address PageTable *table = (PageTable *)pageDir->VirtualTables[frame / 1024]; if (table == null) { Panic.DoPanic("table == null"); } // Calculate page int page = table->Pages[frame & (1024 - 1)]; return((void *)(GetFrameAddress(page) + remaining)); }
/// <summary> /// Clones a page directory and its tables /// </summary> /// <param name="source">The source page directory</param> /// <returns>The cloned page directory</returns> public static PageDirectory *CloneDirectory(PageDirectory *source) { // Note: sizeof(PageDirectory) is not neccesarily a page int pageDirSizeAligned = (int)AlignUp((uint)sizeof(PageDirectory)); // One block for the page directory and the page tables int allocated = (int)Heap.AlignedAlloc(0x1000, pageDirSizeAligned + 1024 * sizeof(PageTable)); if (allocated == 0) { Panic.DoPanic("Couldn't clone page directory because there is no memory left"); } PageDirectory *destination = (PageDirectory *)allocated; destination->PhysicalDirectory = (PageDirectory *)GetPhysicalFromVirtual((void *)allocated); for (int i = 0; i < 1024; i++) { int sourceTable = source->VirtualTables[i]; if (sourceTable == 0) { Panic.DoPanic("sourceTable == 0?!"); } // Get the pointer without the flags and the flags seperately PageTable *sourceTablePtr = (PageTable *)sourceTable; int flags = source->PhysicalTables[i] & 0xFFF; // Calculate addresses int addressOffset = pageDirSizeAligned + i * sizeof(PageTable); PageTable *newTable = (PageTable *)(allocated + addressOffset); int newTablePhysical = (int)GetPhysicalFromVirtual(newTable); // Copy table data and set pointers Memory.Memcpy(newTable, sourceTablePtr, sizeof(PageTable)); destination->PhysicalTables[i] = newTablePhysical | flags; destination->VirtualTables[i] = (int)newTable; } return(destination); }
/// <summary> /// Sets the current paging directory in the CR3 register /// </summary> /// <param name="directory">The page directory</param> private static extern void setDirectoryInternal(PageDirectory *directory);