diff --git a/libmaxsi/memory.cpp b/libmaxsi/memory.cpp
index dc858173..bd4686d1 100644
--- a/libmaxsi/memory.cpp
+++ b/libmaxsi/memory.cpp
@@ -177,10 +177,10 @@ namespace Maxsi
{
Memory::Set(Bins, 0, sizeof(Bins));
#ifdef SORTIX_KERNEL
- Wilderness = (byte*) Sortix::VirtualMemory::heapUpper;
+ Wilderness = (byte*) Sortix::Memory::HEAPUPPER;
#else
// TODO: This is very 32-bit specific!
- Wilderness = (byte*) 0x80000000;
+ Wilderness = (byte*) 0x80000000UL;
#endif
HeapStart = Wilderness;
WildernessSize = 0;
@@ -199,7 +199,7 @@ namespace Maxsi
#ifdef SORTIX_KERNEL
// Check if the wilderness would grow larger than the kernel memory area.
- if ( ( ((uintptr_t) Wilderness) - Sortix::VirtualMemory::heapLower ) < NeededSize ) { return false; }
+ if ( ( ((uintptr_t) Wilderness) - Sortix::Memory::HEAPLOWER ) < NeededSize ) { return false; }
#endif
// Figure out how where the new wilderness will be.
@@ -235,8 +235,13 @@ namespace Maxsi
// Get a raw unused physical page.
addr_t Page = Sortix::Page::Get();
- if ( Page == 0 )
+ // Map the physical page to a virtual one.
+ addr_t VirtualAddr = NewWilderness + 4096 * PagesLeft;
+
+ if ( Page == 0 || !Sortix::Memory::MapKernel(Page, VirtualAddr) )
{
+ if ( Page != 0 ) { Sortix::Page::Put(Page); }
+
// If none is available, simply let the allocation fail
// and unallocate everything we did allocate so far.
while ( PagesLeft < NumPages )
@@ -244,16 +249,11 @@ namespace Maxsi
PagesLeft++;
addr_t OldVirtual = NewWilderness + 4096 * PagesLeft;
- addr_t OldPage = Sortix::VirtualMemory::UnmapKernel(OldVirtual);
+ addr_t OldPage = Sortix::Memory::UnmapKernel(OldVirtual);
Sortix::Page::Put(OldPage);
}
return false;
- }
-
- // Map the physical page to a virtual one.
- addr_t VirtualAddr = NewWilderness + 4096 * PagesLeft;
-
- Sortix::VirtualMemory::MapKernel(VirtualAddr, Page);
+ }
}
#endif
diff --git a/sortix/Makefile b/sortix/Makefile
index 66a508be..7f527f4b 100644
--- a/sortix/Makefile
+++ b/sortix/Makefile
@@ -26,7 +26,8 @@ endif
ifdef X86FAMILY
CPUOBJS:=$(CPUOBJS) \
- $(CPU)/memorymanagement-asm.o \
+ $(CPU)/memorymanagement.o \
+ x86-family/memorymanagement.o \
$(CPU)/interrupt.o \
$(CPU)/gdt.o \
x86-family/x86-family.o
@@ -51,7 +52,6 @@ time.o \
log.o \
panic.o \
keyboard.o \
-memorymanagement.o \
scheduler.o \
syscall.o \
sound.o \
diff --git a/sortix/elf.cpp b/sortix/elf.cpp
index 4085b8ba..875b2f05 100644
--- a/sortix/elf.cpp
+++ b/sortix/elf.cpp
@@ -35,6 +35,15 @@ namespace Sortix
{
namespace ELF
{
+ // This works around an optimizer bug I ran into, where the memcpy below
+ // somehow gets executed prior to the memory was mapped. Somehow, when I
+ // tried to debug it, it suddenly worked. So here's some deep magic that
+ // somehow fixes my code.
+ void PreventHazardousCodeReordering()
+ {
+ Log::Print("");
+ }
+
addr_t Construct32(Process* process, const void* file, size_t filelen)
{
if ( filelen < sizeof(Header32) ) { return 0; }
@@ -85,22 +94,24 @@ namespace Sortix
return 0;
}
- if ( !VirtualMemory::MapRangeUser(mapto, mapbytes) )
+ if ( !Memory::MapRangeUser(mapto, mapbytes) )
{
return 0;
}
// Insert our newly allocated memory into the processes segment
// list such that it can be reclaimed later.
- if ( process->segments ) { process->segments->prev = segment;}
+ if ( process->segments ) { process->segments->prev = segment; }
segment->next = process->segments;
process->segments = segment;
+ PreventHazardousCodeReordering();
+
// Copy as much data as possible and memset the rest to 0.
byte* memdest = (byte*) virtualaddr;
- byte* memsource = (byte*) ( (addr_t)file + pht->offset);
- Memory::Copy(memdest, memsource, pht->filesize);
- Memory::Set(memdest + pht->filesize, 0, pht->memorysize - pht->filesize);
+ byte* memsource = (byte*) ( ((addr_t)file) + pht->offset);
+ Maxsi::Memory::Copy(memdest, memsource, pht->filesize);
+ Maxsi::Memory::Set(memdest + pht->filesize, 0, pht->memorysize - pht->filesize);
}
return entry;
diff --git a/sortix/interrupt.cpp b/sortix/interrupt.cpp
index 18c82d76..8153b21b 100644
--- a/sortix/interrupt.cpp
+++ b/sortix/interrupt.cpp
@@ -34,6 +34,8 @@ namespace Sortix
{
namespace Interrupt
{
+ const bool debugexception = true;
+
size_t numknownexceptions = 19;
const char* exceptions[] =
{ "Divide by zero", "Debug", "Non maskable interrupt", "Breakpoint",
@@ -59,8 +61,18 @@ namespace Sortix
const char* message = ( regs->int_no < numknownexceptions )
? exceptions[regs->int_no] : "Unknown";
+ if ( debugexception )
+ {
+ Log::PrintF("cs=0x%x, eax=0x%zx, ebx=0x%zx, ecx=0x%zx, "
+ "edx=0x%zx, esi=0x%zx, edi=0x%zx, esp=0x%zx, "
+ "useresp=0x%zx, test=0x%zx\n",
+ regs->cs, regs->eax, regs->ebx, regs->ecx,
+ regs->edx, regs->esi, regs->edi, regs->esp,
+ regs->useresp, regs->useresp);
+ }
+
// Halt and catch fire if we are the kernel.
- if ( (regs->cs & (0x4-1)) == 0 || regs->int_no == 13 )
+ if ( (regs->cs & (0x4-1)) == 0 )
{
PanicF("Unhandled CPU Exception id %zu '%s' at eip=0x%zx "
"(cr2=0x%p, err_code=0x%p)", regs->int_no, message,
diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp
index 4c0d826f..8931fba4 100644
--- a/sortix/kernel.cpp
+++ b/sortix/kernel.cpp
@@ -176,8 +176,8 @@ namespace Sortix
PCI::Init();
#endif
- // Initialize the paging.
- Page::Init(BootInfo);
+ // Initialize the paging and virtual memory.
+ Memory::Init(BootInfo);
uint8_t* initrd = NULL;
size_t initrdsize = 0;
@@ -206,9 +206,6 @@ namespace Sortix
if ( BootInfo == NULL ) { Panic("kernel.cpp: The bootinfo structure was NULL. Are your bootloader multiboot compliant?"); }
- // Initialize virtual memory. TODO: This is not fully working yet.
- VirtualMemory::Init();
-
// Initialize the kernel heap.
Maxsi::Memory::Init();
@@ -224,11 +221,11 @@ namespace Sortix
Thread::Entry initstart = RunApplication;
// Create an address space for the first process.
- addr_t addrspace = VirtualMemory::CreateAddressSpace();
+ addr_t addrspace = Memory::Fork();
// Use the new address space!
- VirtualMemory::SwitchAddressSpace(addrspace);
-
+ Memory::SwitchAddressSpace(addrspace);
+
// Create the first process!
Process* process = new Process(addrspace);
if ( process == 0 ) { Panic("kernel.cpp: Could not allocate the first process!"); }
diff --git a/sortix/memorymanagement.cpp b/sortix/memorymanagement.cpp
deleted file mode 100644
index 68569fd3..00000000
--- a/sortix/memorymanagement.cpp
+++ /dev/null
@@ -1,650 +0,0 @@
-/******************************************************************************
-
- COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
-
- This file is part of Sortix.
-
- Sortix 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 3 of the License, or (at your option) any later
- version.
-
- Sortix 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 Sortix. If not, see .
-
- memorymanagement.cpp
- Handles memory for the x86 architecture.
-
-******************************************************************************/
-
-#include "platform.h"
-#include
-#include "log.h"
-#include "panic.h"
-#include "multiboot.h"
-#include "memorymanagement.h"
-
-using namespace Maxsi;
-
-namespace Sortix
-{
- const addr_t KernelStart = 0x000000UL;
- const size_t KernelLength = 0x200000UL;
- const size_t KernelLeadingPages = KernelLength / 0x1000UL;
-
- namespace Page
- {
- struct UnallocPage // Must like this due to assembly.
- {
- size_t Magic;
- void* Next;
- size_t ContinuousPages;
- };
-
- // Refers to private assembly functions.
- addr_t GetPrivate();
- void PutPrivate(addr_t page);
- void Fragmentize();
-
- UnallocPage* volatile UnallocatedPage; // Must have this name and namespace due to assembly.
- size_t pagesTotal;
- size_t pagesUsed;
- size_t pagesFree;
-
- const size_t UnallocPageMagic = 0xABBAACDC; // Must this value due to assembly
-
- // Creates the Great Linked List of All Linked Lists!
- void Init(multiboot_info_t* BootInfo)
- {
- UnallocatedPage = NULL;
- pagesTotal = 0;
-
- if ( !( BootInfo->flags & MULTIBOOT_INFO_MEM_MAP ) )
- {
- Panic("memorymanagement.cpp: The memory map flag was't set in the multiboot structure. Are your bootloader multiboot compliant?");
- }
-
- for (
- multiboot_memory_map_t* MMap = (multiboot_memory_map_t*) BootInfo->mmap_addr;
- (uintptr_t) MMap < BootInfo->mmap_addr + BootInfo->mmap_length;
- MMap = (multiboot_memory_map_t *) ((uintptr_t) MMap + MMap->size + sizeof(MMap->size))
- )
- {
-
- // Check that we can use this kind of RAM.
- if ( MMap->type != 1 ) { continue; }
-
- //Log::PrintF("RAM at 0x%64x\t of length 0x%64zx\n", MMap->addr, MMap->len);
-
- // The kernels code may split this memory area into multiple pieces.
- struct { uintptr_t Base; size_t Length; } Entries[2]; nat Num = 1;
-
- // Attempt to crop the entry so that we only map what we can address.
- Entries[0].Base = (uintptr_t) MMap->addr;
- Entries[0].Length = MMap->len;
- #ifdef PLATFORM_X86
- // Figure out if the memory area is addressable (are our pointers big enough?)
- if ( 0xFFFFFFFF < MMap->addr ) { continue; }
- if ( 0xFFFFFFFF < MMap->addr + MMap->len ) { Entries[0].Length = 0xFFFFFFFF - MMap->addr; }
- #endif
-
- // Detect if this memory is completely covered by the kernel.
- if ( KernelStart <= Entries[0].Base && Entries[0].Base + Entries[0].Length <= KernelStart + KernelLength ) { continue; }
-
- // Detect if this memory is partially covered by the kernel (from somewhere in the memory to somewhere else in the memory)
- else if ( Entries[0].Base <= KernelStart && KernelStart + KernelLength <= Entries[0].Base + Entries[0].Length )
- {
- Entries[1].Base = KernelStart + KernelLength;
- Entries[1].Length = (Entries[0].Base + Entries[0].Length) - Entries[1].Base;
- Entries[0].Length = KernelStart - Entries[0].Base;
- Num = 2;
- }
-
- // Detect if this memory is partially covered by the kernel (from the left to somewhere in the memory)
- else if ( Entries[0].Base <= KernelStart + KernelLength && KernelStart + KernelLength <= Entries[0].Base + Entries[0].Length )
- {
- Entries[0].Length = (Entries[0].Base + Entries[0].Length) - (KernelStart + KernelLength);
- Entries[0].Base = KernelStart + KernelLength;
- }
-
- // Detect if this memory is partially covered by the kernel (from somewhere in the memory to the right)
- else if ( Entries[0].Base <= KernelStart && KernelStart <= Entries[0].Base + Entries[0].Length )
- {
- Entries[0].Length = KernelStart - Entries[0].Base;
- }
-
- for ( nat I = 0; I < Num; I++ )
- {
-
- // Align our entries on page boundaries.
- uintptr_t NewBase = (Entries[I].Base + 0xFFF) / 0x1000 * 0x1000;
- Entries[I].Length = (Entries[I].Base + Entries[I].Length ) - NewBase;
- Entries[I].Length /= 0x1000;
- Entries[I].Base = NewBase;
-
- if ( Entries[I].Length == 0 ) { continue; }
-
- #ifdef PLATFORM_X64
- Log::Print("Halt: CPU X64 cannot continue as the virtual memory isn't disabled (kernel bug) and the code is about to access non-mapped memory.\n");
- Log::Print("Sorry, it simply isn't possible to fully boot Sortix in x64 mode yet.\n");
- Log::Print("X64 may be working when Sortix 0.5 comes out, or try the git master.\n");
- while(true);
- #endif
-
- UnallocPage* Page = (UnallocPage*) Entries[I].Base;
- Page->Magic = UnallocPageMagic;
- Page->Next = UnallocatedPage;
- Page->ContinuousPages = Entries[I].Length - 1;
-
- pagesTotal += Entries[I].Length;
-
- UnallocatedPage = Page;
- }
- }
-
- if ( pagesTotal == 0 ) { Panic("memorymanagement.cpp: no RAM were available for paging"); }
-
- // Alright, time to make our linked list into a lot of small entries.
- // This speeds up the system when it's fully up and running. It only
- // takes a few miliseconds to run this operation on my laptop.
- Fragmentize();
-
- pagesFree = pagesTotal;
- pagesUsed = 0;
-
- ASSERT(pagesFree + pagesUsed == pagesTotal);
-
-#ifndef PLATFORM_SERIAL
- //Log::PrintF("%zu pages are available for paging (%zu MiB RAM)\n", PagesTotal, PagesTotal >> 8 /* * 0x1000 / 1024 / 1024*/);
-#endif
- }
-
- addr_t Get()
- {
- addr_t result = GetPrivate();
-
- if ( result != 0 )
- {
- ASSERT(pagesFree > 0);
- pagesUsed++;
- pagesFree--;
- }
- else
- {
- ASSERT(pagesFree == 0);
- }
-
- ASSERT(pagesFree + pagesUsed == pagesTotal);
- return result;
- }
-
- void Put(addr_t page)
- {
- pagesFree++;
- pagesUsed--;
-
- PutPrivate(page);
- }
-
- void Insert(addr_t page)
- {
- pagesFree++;
- pagesTotal++;
-
- PutPrivate(page);
- }
-
- void GetStats(size_t* pagesize, size_t* numfree, size_t* numused)
- {
- *pagesize = 4096UL;
- *numfree = pagesFree;
- *numused = pagesUsed;
- }
- }
-
- namespace VirtualMemory
- {
- const size_t TABLE_PRESENT = (1<<0);
- const size_t TABLE_WRITABLE = (1<<1);
- const size_t TABLE_USER_SPACE = (1<<2);
- const size_t TABLE_RESERVED1 = (1<<3); // Used internally by the CPU.
- const size_t TABLE_RESERVED2 = (1<<4); // Used internally by the CPU.
- const size_t TABLE_ACCESSED = (1<<5);
- const size_t TABLE_DIRTY = (1<<6);
- const size_t TABLE_RESERVED3 = (1<<7); // Used internally by the CPU.
- const size_t TABLE_RESERVED4 = (1<<8); // Used internally by the CPU.
- const size_t TABLE_AVAILABLE1 = (1<<9);
- const size_t TABLE_AVAILABLE2 = (1<<10);
- const size_t TABLE_AVAILABLE3 = (1<<11);
- const size_t TABLE_FLAGS = (0xFFFUL); // Bits used for the flags.
- const size_t TABLE_ADDRESS = (~0xFFFUL); // Bits used for the address.
-
- const size_t DIR_PRESENT = (1<<0);
- const size_t DIR_WRITABLE = (1<<1);
- const size_t DIR_USER_SPACE = (1<<2);
- const size_t DIR_WRITE_THROUGH = (1<<3);
- const size_t DIR_DISABLE_CACHE = (1<<4);
- const size_t DIR_ACCESSED = (1<<5);
- const size_t DIR_RESERVED1 = (1<<6);
- const size_t DIR_4MIB_PAGES = (1<<7);
- const size_t DIR_RESERVED2 = (1<<8);
- const size_t DIR_AVAILABLE1 = (1<<9);
- const size_t DIR_AVAILABLE2 = (1<<10);
- const size_t DIR_AVAILABLE3 = (1<<11);
- const size_t DIR_FLAGS = (0xFFFUL); // Bits used for the flags.
- const size_t DIR_ADDRESS = (~0xFFFUL); // Bits used for the address.
-
- const size_t ENTRIES = 4096 / sizeof(addr_t);
-
- struct Table
- {
- addr_t page[ENTRIES];
- };
-
- struct Dir
- {
- addr_t table[ENTRIES];
- };
-
- #ifdef PLATFORM_X86
- // These structures are always virtually mapped to these addresses.
- Table* const makingTable = (Table*) 0xFFBFC000UL;
- Dir* const makingDir = (Dir*) 0xFFBFD000UL;
- Dir* const kernelDir = (Dir*) 0xFFBFE000UL;
- Dir* const currentDir = (Dir*) 0xFFBFF000UL;
- Table* const currentTables = (Table*) 0xFFC00000UL;
- #endif
-
- #ifdef PLATFORM_X64
- // TODO: These are dummy values!
- const Dir* currentDir = (Dir*) 0xFACEB00C;
- const Table* currentTables = (Table*) 0xFACEB00C;
- #endif
-
- addr_t currentDirPhysical;
-
- #ifdef PLATFORM_X86
- Table* BootstrapCreateTable(Dir* dir, addr_t where);
- void BootstrapMap(Dir* dir, addr_t where, addr_t physical);
- void BootstrapMapStructures(Dir* dir);
- addr_t SwitchDirectory(addr_t dir);
- addr_t CreateDirectory();
- #endif
-
- void Init()
- {
- #ifdef PLATFORM_X86
-
- // Initialize variables.
- currentDirPhysical = 0;
-
- // Allocate a page we can use for our kernel page directory.
- Dir* dirphys = (Dir*) Page::Get();
- if ( dirphys == NULL ) { Panic("memorymanagement.cpp: Could not allocate page dir"); }
-
- Memory::Set(dirphys, 0, sizeof(Dir));
-
- // Identity map the kernel.
- for ( addr_t ptr = KernelStart; ptr < KernelStart + KernelLength; ptr += 0x1000UL )
- {
- BootstrapMap(dirphys, ptr, ptr);
- }
-
- // Create every table used in the kernel half. We do it now such that
- // any copies of the kernel dir never gets out of date.
- for ( addr_t ptr = 0x80000000UL; ptr != 0UL; ptr += ENTRIES * 0x1000UL )
- {
- BootstrapCreateTable(dirphys, ptr);
- }
-
- // Map the paging structures themselves.
- BootstrapMapStructures(dirphys);
-
- // We have now created a minimal virtual environment where the kernel
- // is mapped, the paging structures are ready, and the paging
- // structures are mapped. We are now ready to enable pages.
-
- // Switch the current dir - this enables paging.
- SwitchDirectory((addr_t) dirphys);
-
- // Hello, virtual world!
-
- #else
- #warning "Virtual Memory is not available on this arch"
- while(true);
- #endif
- }
-
- #ifdef PLATFORM_X86
- inline addr_t GetTableId(addr_t where) { return where / (4096UL * ENTRIES); }
- inline addr_t GetPageId(addr_t where) { return ( where / 4096UL ) % ENTRIES; }
-
- Table* BootstrapCreateTable(Dir* dir, addr_t where)
- {
- size_t tableid = GetTableId(where);
- addr_t tabledesc = dir->table[tableid];
-
- if ( tabledesc != 0 )
- {
- return (Table*) (tabledesc & TABLE_ADDRESS);
- }
- else
- {
- addr_t tablepage = Page::Get();
- if ( tablepage == 0 )
- {
- PanicF("memorymanagement.cpp: Could not allocate bootstrap page table for 0x%p", where);
- }
- Memory::Set((void*) tablepage, 0, sizeof(Table));
- tabledesc = tablepage | TABLE_PRESENT | TABLE_WRITABLE;
- dir->table[tableid] = tabledesc;
- ASSERT((Table*) tablepage == BootstrapCreateTable(dir, where));
- return (Table*) tablepage;
- }
- }
-
- void BootstrapMap(Dir* dir, addr_t where, addr_t physical)
- {
- Table* table = BootstrapCreateTable(dir, where);
-
- size_t pageid = GetPageId(where);
- table->page[pageid] = physical | TABLE_PRESENT | TABLE_WRITABLE;
- }
-
- void BootstrapMapStructures(Dir* dir)
- {
- // Map the dir itself.
- BootstrapMap(dir, (addr_t) kernelDir, (addr_t) dir);
- BootstrapMap(dir, (addr_t) currentDir, (addr_t) dir);
-
- // Map the tables.
- for ( size_t i = 0; i < ENTRIES; i++ )
- {
- addr_t tabledesc = dir->table[i];
- if ( tabledesc == 0 ) { continue; }
- addr_t mapto = (addr_t) &(currentTables[i]);
- addr_t mapfrom = (tabledesc & TABLE_ADDRESS);
- BootstrapMap(dir, mapto, mapfrom);
- }
- }
- #endif
-
- #ifdef PLATFORM_X86
-
- addr_t Lookup(addr_t where)
- {
- // Make sure we are only mapping kernel-approved pages.
- size_t tableid = GetTableId(where);
- addr_t tabledesc = currentDir->table[tableid];
- if ( !(tabledesc & DIR_PRESENT) ) { return 0; }
-
- size_t pageid = GetPageId(where);
- return currentTables[tableid].page[pageid];
- }
-
- // Enables paging and flushes the Translation Lookaside Buffer (TLB).
- void Flush()
- {
- asm volatile("mov %0, %%cr3":: "r"(currentDirPhysical));
- size_t cr0; \
- asm volatile("mov %%cr0, %0": "=r"(cr0));
- cr0 |= 0x80000000UL; // Enable paging!
- asm volatile("mov %0, %%cr0":: "r"(cr0));
- }
-
- addr_t CreateAddressSpace()
- {
- return CreateDirectory();
- }
-
- addr_t SwitchAddressSpace(addr_t addrspace)
- {
- return SwitchDirectory(addrspace);
- }
-
- addr_t SwitchDirectory(addr_t dir)
- {
- // Don't switch if we are already there.
- if ( dir == currentDirPhysical ) { return currentDirPhysical; }
-
- addr_t previous = currentDirPhysical;
- asm volatile("mov %0, %%cr3":: "r"(dir));
- currentDirPhysical = dir;
- Flush();
- return previous;
- }
-
- addr_t CreateDirectory()
- {
- // Allocate the thread pages we need, one for the new pagedir,
- // and two for the last two 8 MiB of the pagedir.
- addr_t newdir = Page::Get();
- if ( newdir == 0 ) { return 0; }
- addr_t newstructmap1 = Page::Get();
- if ( newdir == 0 ) { Page::Put(newdir); return 0; }
- addr_t newstructmap2 = Page::Get();
- if ( newdir == 0 ) { Page::Put(newdir); Page::Put(newstructmap1); return 0; }
-
- // Map the new pagedir, clone the kernel dir, and change the last
- // 8 MiB, such that we can map the new page structures there.
- MapKernel((addr_t) makingDir, newdir);
- Memory::Copy(makingDir, kernelDir, sizeof(Dir));
- makingDir->table[1024-2] = newstructmap1 | DIR_PRESENT | DIR_WRITABLE;
- makingDir->table[1024-1] = newstructmap2 | DIR_PRESENT | DIR_WRITABLE;
-
- // Build the new page structures.
- MapKernel((addr_t) makingTable, newstructmap1);
- Memory::Set(makingTable, 0, sizeof(Table));
- makingTable->page[1024-2] = currentTables[1024-2].page[1024-2];
- makingTable->page[1024-1] = newdir | TABLE_PRESENT | TABLE_WRITABLE;
-
- // Build the new page structures.
- MapKernel((addr_t) makingTable, newstructmap2);
- for ( size_t i = 0; i < 1024-2; i++ )
- {
- makingTable->page[i] = currentTables[1024-1].page[i];
- }
- makingTable->page[1024-2] = newstructmap1 | TABLE_PRESENT | TABLE_WRITABLE;
- makingTable->page[1024-1] = newstructmap2 | TABLE_PRESENT | TABLE_WRITABLE;
-
- return newdir;
- }
-
-
-
- void MapKernel(addr_t where, addr_t physical)
- {
- // Make sure we are only mapping kernel-approved pages.
- size_t tableid = GetTableId(where);
- addr_t tabledesc = currentDir->table[tableid];
- ASSERT(tabledesc != 0);
- ASSERT((tabledesc & DIR_USER_SPACE) == 0);
-
- size_t pageid = GetPageId(where);
- addr_t pagedesc = physical | TABLE_PRESENT | TABLE_WRITABLE;
- currentTables[tableid].page[pageid] = pagedesc;
-
- ASSERT(Lookup(where) == pagedesc);
-
- // TODO: Only update the single page!
- Flush();
- }
-
- addr_t UnmapKernel(addr_t where)
- {
- // Make sure we are only unmapping kernel-approved pages.
- size_t tableid = GetTableId(where);
- addr_t tabledesc = currentDir->table[tableid];
- ASSERT(tabledesc != 0);
- ASSERT((tabledesc & DIR_USER_SPACE) == 0);
-
- size_t pageid = GetPageId(where);
- addr_t result = currentTables[tableid].page[pageid];
- ASSERT((result & TABLE_PRESENT) != 0);
- result &= TABLE_ADDRESS;
- currentTables[tableid].page[pageid] = 0;
-
- // TODO: Only update the single page!
- Flush();
-
- return result;
- }
-
- Table* CreateUserTable(addr_t where, bool maycreate)
- {
- size_t tableid = GetTableId(where);
- addr_t tabledesc = currentDir->table[tableid];
-
- Table* table = &(currentTables[tableid]);
-
- if ( tabledesc == 0 )
- {
- ASSERT(maycreate);
- addr_t tablepage = Page::Get();
- if ( tablepage == 0 ) { return NULL; }
- tabledesc = tablepage | TABLE_PRESENT | TABLE_WRITABLE | TABLE_USER_SPACE;
- currentDir->table[tableid] = tabledesc;
- MapKernel((addr_t) table, tablepage);
-
- // TODO: Only update the single page!
- Flush();
-
- addr_t lookup = Lookup((addr_t) table) & TABLE_ADDRESS;
- ASSERT(lookup == tablepage);
-
- Memory::Set(table, 0, sizeof(Table));
- }
-
- // Make sure we only touch dirs permitted for use by user-space!
- ASSERT((tabledesc & TABLE_USER_SPACE) != 0);
-
- return table;
- }
-
- bool MapUser(addr_t where, addr_t physical)
- {
- // Make sure we are only mapping user-space-approved pages.
- Table* table = CreateUserTable(where, true);
- if ( table == NULL ) { return false; }
-
- size_t pageid = GetPageId(where);
- addr_t pagedesc = physical | TABLE_PRESENT | TABLE_WRITABLE | TABLE_USER_SPACE;
-
- table->page[pageid] = pagedesc;
-
- Flush();
-
- ASSERT(Lookup(where) == pagedesc);
-
- // TODO: Only update the single page!
- Flush();
-
- return true;
- }
-
- addr_t UnmapUser(addr_t where)
- {
- // Make sure we are only mapping user-space-approved pages.
- Table* table = CreateUserTable(where, false);
- ASSERT(table != NULL);
-
- size_t pageid = GetPageId(where);
- addr_t pagedesc = table->page[pageid];
- ASSERT((pagedesc & TABLE_PRESENT) != 0);
- addr_t result = pagedesc & TABLE_ADDRESS;
- table->page[pageid] = 0;
-
- // TODO: Only update the single page!
- Flush();
-
- return result;
- }
-
- bool MapRangeKernel(addr_t where, size_t bytes)
- {
- for ( addr_t page = where; page < where + bytes; page += 4096UL )
- {
- addr_t physicalpage = Page::Get();
- if ( physicalpage == 0 )
- {
- while ( where < page )
- {
- page -= 4096UL;
- physicalpage = UnmapKernel(page);
- Page::Put(physicalpage);
- }
- return false;
- }
-
- MapKernel(page, physicalpage);
- }
-
- return true;
- }
-
- void UnmapRangeKernel(addr_t where, size_t bytes)
- {
- for ( addr_t page = where; page < where + bytes; page += 4096UL )
- {
- addr_t physicalpage = UnmapKernel(page);
- Page::Put(physicalpage);
- }
- }
-
- bool MapRangeUser(addr_t where, size_t bytes)
- {
- for ( addr_t page = where; page < where + bytes; page += 4096UL )
- {
- addr_t physicalpage = Page::Get();
- if ( physicalpage == 0 || !MapUser(page, physicalpage) )
- {
- while ( where < page )
- {
- page -= 4096UL;
- physicalpage = UnmapUser(page);
- Page::Put(physicalpage);
- }
- return false;
- }
- }
-
- return true;
- }
-
- void UnmapRangeUser(addr_t where, size_t bytes)
- {
- for ( addr_t page = where; page < where + bytes; page += 4096UL )
- {
- addr_t physicalpage = UnmapUser(page);
- Page::Put(physicalpage);
- }
- }
-
- #else
-
- #warning "Virtual Memory is not available on this arch"
-
- addr_t Lookup(addr_t where) { while(true); return 0; }
- void Flush() { while(true); }
- addr_t CreateAddressSpace() { while(true); return 0; }
- addr_t SwitchAddressSpace(addr_t addrspace) { while(true); return 0; }
- addr_t SwitchDirectory(addr_t dir) { while(true); return 0; }
- addr_t CreateDirectory() { while(true); return 0; }
- void MapKernel(addr_t where, addr_t physical) { while(true); }
- addr_t UnmapKernel(addr_t where) { while(true); return 0; }
- Table* CreateUserTable(addr_t where, bool maycreate) { while(true); return NULL; }
- bool MapUser(addr_t where, addr_t physical) { while(true); return false; }
- addr_t UnmapUser(addr_t where) { while(true); return 0; }
- bool MapRangeKernel(addr_t where, size_t bytes) { while(true); return false; }
- void UnmapRangeKernel(addr_t where, size_t bytes) { while(true); }
- bool MapRangeUser(addr_t where, size_t bytes) { while(true); return false; }
- void UnmapRangeUser(addr_t where, size_t bytes) { while(true); }
-
- #endif
- }
-}
diff --git a/sortix/memorymanagement.h b/sortix/memorymanagement.h
index 693fc66e..986e0dd0 100644
--- a/sortix/memorymanagement.h
+++ b/sortix/memorymanagement.h
@@ -18,7 +18,7 @@
with Sortix. If not, see .
memorymanagement.h
- Handles memory for the x86 architecture.
+ Functions that allow modification of virtual memory.
******************************************************************************/
@@ -30,328 +30,41 @@ typedef struct multiboot_info multiboot_info_t;
namespace Sortix
{
- // This is the physical page allocator API. It splits the physical memory
- // of the local machine into chunks known as pages. Each page is usually
- // 4096 bytes (depends on your CPU). Pages are page-aligned, meaning they
- // all are located on a multiple of the page size (such as 4096 byte).
- //
- // A long list of memory addresses is used to store and retrieve page
- // addresses from. To allocate a physical page of memory, simply call the
- // Page::Get() function. When you are done using it, you can free it using
- // the Page::Put() function, which makes the page available for other uses.
- //
- // To use a physical page, the CPU must be in physical mode. Since it is
- // undesirable to be in physical mode, using the physical page requires
- // using the virtual memory API (see below).
- //
- // This API completely bypasses the memory swapping system.
- //
- // If you just want memory for use by the kernel, allocate it using 'new'.
namespace Page
{
- // Initializes the paging system. Accepts a multiboot structure
- // containing the layout of the physical memory in this machine.
- void Init(multiboot_info_t* bootinfo);
-
- // Allocates a physical page and returns its physical address, or
- // returns 0 if no page currently is available in the system.
addr_t Get();
-
- // Deallocates a physical page allocated using Get(), which lets the
- // the system reuse the page for other purposes. Page must have been
- // allocated using Get().
void Put(addr_t page);
- // Inserts a physical page into the paging system. This page must not
- // have been allocated using Get() and must have been allocated safely
- // through other means (such as information provided by the bootloader).
- void Insert(addr_t page);
-
// Rounds a memory address down to nearest page.
inline addr_t AlignDown(addr_t page) { return page & ~(0xFFFUL); }
// Rounds a memory address up to nearest page.
inline addr_t AlignUp(addr_t page) { return AlignDown(page + 0xFFFUL); }
-
- // Retrieves statistics about the current page usage in the system, how
- // big pages are, now many are free, and how many are used. Each
- // parameter must be a legal pointer to the locations wherein the stats
- // will be stored. This function always succeeds.
- void GetStats(size_t* pagesize, size_t* numfree, size_t* numused);
}
- // This the virtual memory API. Virtual Memory is a clever way to make the
- // RAM just as you want it. In effect, it it makes the RAM completely empty
- // (there is no RAM). You can then add memory where you desire. You can take
- // a physical page and put it in several places, and you can even add
- // permissions to it (read-only, read-write, kernel-only). Naturally, the
- // amount of physical pages is a limit (out of memory), but using swapping
- // and a not-RAM storage unit, we could have potentially much more memory.
- //
- // There can exist several virtual address spaces, and it is possible for
- // them to share physical pages. Each process in the system has its own
- // virtual address space, but the kernel is always mapped in each address
- // space. While the kernel can access all of the virtual address space,
- // user-space programs can only access what they are allowed, cannot access
- // the kernel's memory and cannot access each other's address spaces. This
- // prevents programs from tampering with each other and from bringing the
- // whole system down.
- //
- // Sortix has several conventions for the layout of the virtual address
- // space. The kernel uses the top of the address space, and user-space is
- // generally allowed to use the bottom and the middle for stuff such as
- // program code, variables, the stack, the heap, and other stuff.
- //
- // To access physical memory, you must allocate a physical page of memory
- // and map it to a virtual address. You can then modify the memory through
- // a pointer to that address.
- //
- // You should select the correct set of functions when writing new code.
- // Using the functions incorrectly, using the wrong functions, or mixing
- // incompatible functions can lead to gruesome bugs.
- //
- // For conveniece, the functions have been grouped. Combining (un)mapping
- // functions from groups is bad style and is possibly buggy. Assertions may
- // be present to detect bad combination.
- //
- // If you modify kernel virtual pages, then the effects will be share across
- // all virtual address spaces.
- //
- // If you modify user-space virtual pages, then the effects will be limited
- // to the current process and its personal virtual address space.
- //
- // If you just want memory for use by the kernel, allocate it using 'new'.
- namespace VirtualMemory
+ namespace Memory
{
- // Initializes the virtual memory system. This bootstraps the kernel
- // paging system (if needed) such that the initial kernel's virtual
- // address space is created and the current process's page structures
- // are mapped to conventional locations.
- // This system depends on the physical page allocator being initialized.
- void Init();
-
- // Creates a new, fresh address space only containing the kernel memory
- // regions, and a fresh user-space frontier of nothingness. The current
- // address space remains active and is not modified (besides some kernel
- // pages used for this purpose). Returns the physical address of the new
- // top level paging structure, which is an opaque identifier as it is
- // not identity mapped. Returns 0 if insufficient memory was available.
- addr_t CreateAddressSpace();
-
- // Switches the current address space to the virtual address space
- // 'addrspace', which must be the result of CreateAddressSpace() or
- // another function that creates or copies address spaces.
+ void Init(multiboot_info_t* bootinfo);
+ void InvalidatePage(addr_t addr);
+ void Flush();
+ addr_t Fork();
addr_t SwitchAddressSpace(addr_t addrspace);
-
- // =====================================================================
- // Function Group 1.
- // Mapping a single kernel page.
- //
- // These functions allow you to map a single physical page for use by
- // the kernel.
- //
- // Usage:
- //
- // addr_t physicalpage = Page::Get();
- // if ( physicalpage == 0 ) { /* handle error */ }
- //
- // const addr_t mapto = 0xF00;
- // VirtualMemory::MapKernel(mapto, physicalpage);
- //
- // /* access the memory */
- // char* mem = (char*) mapto;
- // mem[0] = 'K';
- //
- // ...
- //
- // addr_t physpage = VirtualMemory::UnmapKernel(mapto);
- //
- // /* when physpage is no longer referred, free it */
- // Page::Put(physpage);
-
- // Maps the physical page 'physical' to the virtual address 'where',
- // with read and write flags set, but only accessible to the kernel.
- // 'where' must be a virtual address in the range available to the
- // kernel, and must not currently be used. Given legal input, this
- // function will always succeed - illegal input is a gruesome bug.
- // 'where' must be page aligned. The effect of this function will be
- // shared in all addresses spaces - it is a global change.
- // You are allowed to map the same physical page multiple times, even
- // with Group 2. functions, just make sure to call the proper Unmap
- // functions for each virtual address you map it to, and don't free the
- // physical page until it is no longer referred to.
- // The virtual page 'where' will point to 'physical' instantly after
- // this function returns.
- void MapKernel(addr_t where, addr_t physical);
-
- // This function is equal to unmapping 'where', then mapping 'where' to
- // 'newphysical' and returns previous physical page 'where' pointed to.
- // The same requirements for MapKernel and UnmapKernel applies.
- addr_t RemapKernel(addr_t where, addr_t newphysical);
-
- // Unmaps the virtual address 'where' such that it no longer points to
- // a valid physical address. Accessing the page at the virtual address
- // 'where' will result in an access violation (panicing/crashing the
- // kernel). 'where' must be a virtual address already mapped in the
- // kernel virtual memory ranges. 'where' must be page aligned.
- // 'where' must already be a legal kernel virtual page.
- // Returns the address of the physical memory page 'where' points to
- // before being cleared. Before returning the physical page to the page
- // allocator, make sure that it is not used by other virtual pages.
- addr_t UnmapKernel(addr_t where);
-
- // =====================================================================
- // Function Group 2.
- // Mapping a single user-space page.
- //
- // These functions allow you to map a single physical page for use by
- // user-space programs.
- //
- // Usage:
- //
- // addr_t physicalpage = Page::Get();
- // if ( physicalpage == 0 ) { /* handle error */ }
- //
- // const addr_t mapto = 0xF00;
- // if ( !VirtualMemory::MapUser(mapto, physicalpage) )
- // {
- // Page::Put(physicalpage);
- // /* handle error */
- // }
- //
- // /* let user-space use memory safely */
- //
- // addr_t physpage = VirtualMemory::UnmapUser(mapto);
- //
- // /* when physpage is no longer referred, free it */
- // Page::Put(physpage);
-
- // Maps the physical page 'physical' to the virtual address 'where',
- // with read and write flags set, accessible to both kernel and user-
- // space. 'where' must be a virtual address not in the kernel ranges,
- // that is, available to userspace. 'where' must be page aligned and
- // not currently used. Returns false if insufficient memory is available
- // and returns with the address space left unaltered. Illegal input is
- // also a gruesome bug. This function only changes the address space of
- // the current process.
- // You are allowed to map the same physical page multiple times, even
- // with Group 1. functions, just make sure to call the proper Unmap
- // functions for each virtual address you map it to, and don't free the
- // physical page until it is no longer referred to.
- // The virtual page 'where' will point to 'physical' instantly after
- // this function returns.
- bool MapUser(addr_t where, addr_t physical);
-
- // This function is equal to unmapping 'where', then mapping 'where' to
- // 'newphysical' and returns previous physical page 'where' pointed to.
- // The same requirements for MapUser and UnmapUser applies.
- addr_t RemapUser(addr_t where, addr_t newphysical);
-
- // Unmaps the virtual address 'where' such that it no longer points to
- // a valid physical address. Accessing the page at the virtual address
- // 'where' will result in an access violation (panicing/crashing the
- // program/kernel). 'where' must be a virtual address already mapped
- // outside the kernel virtual memory ranges. 'where' must be page
- //aligned. 'where' must already be a legal user-space virtual page.
- // Returns the address of the physical memory page 'where' points to
- // before being cleared. Before returning the physical page to the page
- // allocator, make sure that it is not used by other virtual pages.
- addr_t UnmapUser(addr_t where);
-
- // =====================================================================
- // Function Group 3.
- // Mapping a range of kernel pages.
- //
- // These functions allow you to specify a range of virtual pages that
- // shall be usable by the kernel. Memory will be allocated accordingly.
- //
- // Usage:
- //
- // const addr_t mapto = 0x4000UL;
- // size_t numpages = 8;
- // size_t bytes = numpages * 4096UL;
- // if ( !VirtualMemory::MapRangeKernel(mapto, bytes) )
- // {
- // /* handle error */
- // }
- //
- // /* use memory here */
- // ...
- //
- // VirtualMemory::UnmapRangeKernel(mapto, bytes);
-
- // Allocates the needed pages and maps them to 'where'. Returns false if
- // not enough memory is available. 'where' must be page aligned. 'bytes'
- // need not be page aligned and is rounded up to nearest page. The
- // region of 'where' and onwards must not be currently mapped.
- // The memory will only be readable and writable by the kernel.
- // The memory will be available the instant the function returns.
- // The whole region must be within the kernel virtual page ranges.
bool MapRangeKernel(addr_t where, size_t bytes);
-
- // Deallocates the selected pages, and unmaps the selected region.
- // 'where' must be paged aligned. 'bytes' need not be page aligned and
- // is rounded up to nearest page. Each page in the region must have been
- // allocated by MapRangeKernel. You need not free a whole region at once
- // and you may even combine pages from adjacent regions.
void UnmapRangeKernel(addr_t where, size_t bytes);
-
- // =====================================================================
- // Function Group 4.
- // Mapping a range of user-space pages.
- //
- // These functions allow you to specify a range of virtual pages that
- // shall be usable by the user-space. Memory will be allocated
- // accordingly.
- //
- // Usage:
- //
- // const addr_t mapto = 0x4000UL;
- // size_t numpages = 8;
- // size_t bytes = numpages * 4096UL;
- // if ( !VirtualMemory::MapRangeUser(mapto, bytes) )
- // {
- // /* handle error */
- // }
- //
- // /* let user-space use memory here */
- // ...
- //
- // VirtualMemory::UnmapRangeUser(mapto, bytes);
-
- // Allocates the needed pages and maps them to 'where'. Returns false if
- // not enough memory is available. 'where' must be page aligned. 'bytes'
- // need not be page aligned and is rounded up to nearest page. The
- // region of 'where' and onwards must not be currently mapped.
- // The memory will be readable and writable by user-space.
- // The memory will be available the instant the function returns.
- // The whole region must be outside the kernel virtual page ranges.
bool MapRangeUser(addr_t where, size_t bytes);
-
- // Deallocates the selected pages, and unmaps the selected region.
- // 'where' must be paged aligned. 'bytes' need not be page aligned and
- // is rounded up to nearest page. Each page in the region must have been
- // allocated by MapRangeUser. You need not free a whole region at once
- // and you may even combine pages from adjacent regions.
void UnmapRangeUser(addr_t where, size_t bytes);
+ bool MapKernel(addr_t physical, addr_t mapto);
+ bool MapUser(addr_t physical, addr_t mapto);
+ addr_t UnmapKernel(addr_t mapto);
+ addr_t UnmapUser(addr_t mapto);
-#ifdef PLATFORM_X86
- // Define where the kernel heap is located, used by the heap code.
- const addr_t heapLower = 0x80000000UL;
- const addr_t heapUpper = 0xFF800000UL;
-
- // Physical pages may be safely temporarily mapped to this address and a
- // good dozen of pages onwards. Beware that this is only meant to be
- // a temporary place to put memory.
- const addr_t tempaddr = 0xFF800000UL;
-
+#if defined(PLATFORM_X86)
+ const addr_t HEAPLOWER = 0x80000000UL;
+ const addr_t HEAPUPPER = 0xFF800000UL;
#elif defined(PLATFORM_X64)
- // This isn't supported yet, so just use random values.
- const addr_t heapLower = 0x80000000UL;
- const addr_t heapUpper = 0xFF800000UL;
- const addr_t tempaddr = 0xFF800000UL;
+ const addr_t HEAPLOWER = 0xFFFF800000000000UL;
+ const addr_t HEAPUPPER = 0xFFFFFE8000000000UL;
#endif
-
}
}
diff --git a/sortix/process.cpp b/sortix/process.cpp
index b58dee65..7c1ea6ab 100644
--- a/sortix/process.cpp
+++ b/sortix/process.cpp
@@ -70,7 +70,7 @@ namespace Sortix
ProcessSegment* tmp = segments;
while ( tmp != NULL )
{
- VirtualMemory::UnmapRangeUser(tmp->position, tmp->size);
+ Memory::UnmapRangeUser(tmp->position, tmp->size);
ProcessSegment* todelete = tmp;
tmp = tmp->next;
delete todelete;
diff --git a/sortix/scheduler.cpp b/sortix/scheduler.cpp
index b41fc78f..45ce1811 100644
--- a/sortix/scheduler.cpp
+++ b/sortix/scheduler.cpp
@@ -202,7 +202,7 @@ namespace Sortix
AllocatedThreadId = 1;
// Create an address space for the idle process.
- addr_t noopaddrspace = VirtualMemory::CreateAddressSpace();
+ addr_t noopaddrspace = Memory::Fork();
// Create the noop process.
Process* noopprocess = new Process(noopaddrspace);
@@ -225,7 +225,8 @@ namespace Sortix
uintptr_t MapTo = 0x80000000;
- VirtualMemory::MapKernel(MapTo, (uintptr_t) KernelStackPage);
+ Memory::MapKernel((addr_t) KernelStackPage, MapTo);
+ Memory::InvalidatePage(KernelStackPage);
GDT::SetKernelStack((size_t*) (MapTo+4096));
}
@@ -271,9 +272,9 @@ namespace Sortix
uintptr_t StackPos = 0x80000000UL;
uintptr_t MapTo = StackPos - 4096UL;
- addr_t OldAddrSpace = VirtualMemory::SwitchAddressSpace(Process->GetAddressSpace());
+ addr_t OldAddrSpace = Memory::SwitchAddressSpace(Process->GetAddressSpace());
- VirtualMemory::MapUser(MapTo, PhysStack);
+ Memory::MapUser(PhysStack, MapTo);
size_t* Stack = (size_t*) StackPos;
// Prepare the parameters for the entry function (C calling convention).
@@ -290,7 +291,7 @@ namespace Sortix
thread->SetState(Thread::State::RUNNABLE);
// Avoid side effects by restoring the old address space.
- VirtualMemory::SwitchAddressSpace(OldAddrSpace);
+ Memory::SwitchAddressSpace(OldAddrSpace);
#endif
@@ -354,7 +355,7 @@ namespace Sortix
}
// If applicable, switch the virtual address space.
- VirtualMemory::SwitchAddressSpace(NextThread->GetProcess()->GetAddressSpace());
+ Memory::SwitchAddressSpace(NextThread->GetProcess()->GetAddressSpace());
currentThread = NextThread;
diff --git a/sortix/vga.cpp b/sortix/vga.cpp
index 332dbd7c..9a7ba1a4 100644
--- a/sortix/vga.cpp
+++ b/sortix/vga.cpp
@@ -72,17 +72,17 @@ namespace Sortix
// TODO: Check if mapto collides with any other memory section!
- if ( !VirtualMemory::MapUser(mapto, page) )
+ if ( !Memory::MapUser(page, mapto) )
{
Page::Put(page); R->eax = 0; return;
}
- Memory::Set(userframe, 0, sizeof(UserFrame));
+ Maxsi::Memory::Set(userframe, 0, sizeof(UserFrame));
DevVGAFrame* frame = new DevVGAFrame();
if ( frame == NULL )
{
- VirtualMemory::UnmapUser(mapto);
+ Memory::UnmapUser(mapto);
Page::Put(page); R->eax = 0; return;
}
@@ -90,7 +90,7 @@ namespace Sortix
if ( fd < 0 )
{
delete frame;
- VirtualMemory::UnmapUser(mapto);
+ Memory::UnmapUser(mapto);
Page::Put(page); R->eax = 0; return;
}
@@ -142,31 +142,33 @@ namespace Sortix
if ( currentframe->process != process )
{
- VirtualMemory::SwitchAddressSpace(currentframe->process->GetAddressSpace());
+ Memory::SwitchAddressSpace(currentframe->process->GetAddressSpace());
}
// Remap the pages in the owning process.
// TODO: Check if userframe is actually user-space writable!
- VirtualMemory::UnmapUser((addr_t) currentframe->userframe);
- VirtualMemory::MapUser((addr_t) currentframe->userframe, currentframe->physical);
+ Memory::UnmapUser((addr_t) currentframe->userframe);
+ Memory::MapUser(currentframe->physical, (addr_t) currentframe->userframe);
+ Memory::InvalidatePage((addr_t) frame->userframe);
// Restore the contents of this frame to the VGA framebuffer.
- Memory::Copy(currentframe->userframe, vga, sizeof(UserFrame));
+ Maxsi::Memory::Copy(currentframe->userframe, vga, sizeof(UserFrame));
if ( currentframe->process != process )
{
- VirtualMemory::SwitchAddressSpace(process->GetAddressSpace());
+ Memory::SwitchAddressSpace(process->GetAddressSpace());
}
currentframe->onscreen = false;
}
// Now move the contents of this frame to the VGA framebuffer.
- Memory::Copy(vga, frame->userframe, sizeof(UserFrame));
+ Maxsi::Memory::Copy(vga, frame->userframe, sizeof(UserFrame));
// Remap the pages such that the current process now uses the vga.
- VirtualMemory::UnmapUser((addr_t) frame->userframe);
- VirtualMemory::MapUser((addr_t) frame->userframe, (addr_t) vga);
+ Memory::UnmapUser((addr_t) frame->userframe);
+ Memory::MapUser((addr_t) vga, (addr_t) frame->userframe);
+ Memory::InvalidatePage((addr_t) frame->userframe);
frame->onscreen = true;
currentframe = frame;
@@ -201,7 +203,7 @@ namespace Sortix
DevVGAFrame::~DevVGAFrame()
{
if ( process != NULL ) { ASSERT(CurrentProcess() == process); }
- if ( userframe != NULL ) { VirtualMemory::UnmapUser((addr_t) userframe); }
+ if ( userframe != NULL ) { Memory::UnmapUser((addr_t) userframe); Memory::InvalidatePage((addr_t) userframe); }
if ( physical != 0 ) { Page::Put(physical); }
}
diff --git a/sortix/x64/boot.s b/sortix/x64/boot.s
index bcf77044..f42d85e4 100644
--- a/sortix/x64/boot.s
+++ b/sortix/x64/boot.s
@@ -58,24 +58,29 @@ multiboot_entry:
# Store the magic value.
mov %eax, 0x100004
- # Clear the first 4096*4 bytes following 0x1000.
+ # Clear the first $0xE000 bytes following 0x1000.
movl $0x1000, %edi
mov %edi, %cr3
xorl %eax, %eax
- movl $4096, %ecx
+ movl $0xE000, %ecx
rep stosl
movl %cr3, %edi
# Set the initial page tables.
+
+ # Page-Map Level 4
movl $0x2003, (%edi)
addl $0x1000, %edi
+ # Page-Directory Pointer Table
movl $0x3003, (%edi)
addl $0x1000, %edi
+ # Page-Directory
movl $0x4003, (%edi)
addl $0x1000, %edi
+ # Page-Table
# Memory map the first 2 MiB.
movl $0x3, %ebx
movl $512, %ecx
diff --git a/sortix/x64/memorymanagement.cpp b/sortix/x64/memorymanagement.cpp
new file mode 100644
index 00000000..f420addf
--- /dev/null
+++ b/sortix/x64/memorymanagement.cpp
@@ -0,0 +1,110 @@
+/******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
+
+ This file is part of Sortix.
+
+ Sortix 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 3 of the License, or (at your option) any later
+ version.
+
+ Sortix 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 Sortix. If not, see .
+
+ memorymanagement.cpp
+ Handles memory for the x64 architecture.
+
+******************************************************************************/
+
+#include "platform.h"
+#include
+#include "multiboot.h"
+#include "panic.h"
+#include "../memorymanagement.h"
+#include "x86-family/memorymanagement.h"
+
+namespace Sortix
+{
+ namespace Page
+ {
+ extern size_t stackused;
+ extern size_t stacklength;
+ }
+
+ namespace Memory
+ {
+ extern addr_t currentdir;
+
+ void InitCPU()
+ {
+ // The x64 boot code already set up virtual memory and identity
+ // mapped the first 2 MiB. This code finishes the job such that
+ // virtual memory is fully usable and manageable.
+
+ // boot.s already initialized everything from 0x1000UL to 0xE000UL
+ // to zeroes. Since these structures are already used, doing it here
+ // will be very dangerous.
+
+ PML* const BOOTPML4 = (PML* const) 0x01000UL;
+ PML* const BOOTPML3 = (PML* const) 0x06000UL;
+ PML* const BOOTPML2 = (PML* const) 0x07000UL;
+ PML* const BOOTPML1 = (PML* const) 0x08000UL;
+
+ // First order of business is to map the virtual memory structures
+ // to the pre-defined locations in the virtual address space.
+ addr_t flags = PML_PRESENT | PML_WRITABLE;
+
+ // Fractal map the PML1s.
+ BOOTPML4->entry[511] = (addr_t) BOOTPML4 | flags;
+
+ // Fractal map the PML2s.
+ BOOTPML4->entry[510] = (addr_t) BOOTPML3 | flags | PML_FORK;
+ BOOTPML3->entry[511] = (addr_t) BOOTPML4 | flags;
+
+ // Fractal map the PML3s.
+ BOOTPML3->entry[510] = (addr_t) BOOTPML2 | flags | PML_FORK;
+ BOOTPML2->entry[511] = (addr_t) BOOTPML4 | flags;
+
+ // Fractal map the PML4s.
+ BOOTPML2->entry[510] = (addr_t) BOOTPML1 | flags | PML_FORK;
+ BOOTPML1->entry[511] = (addr_t) BOOTPML4 | flags;
+
+ // Add some predefined room for forking address spaces.
+ PML* const FORKPML2 = (PML* const) 0x09000UL;
+ PML* const FORKPML1 = (PML* const) 0x0A000UL;
+
+ BOOTPML3->entry[0] = (addr_t) FORKPML2 | flags | PML_FORK;
+ BOOTPML2->entry[0] = (addr_t) FORKPML1 | flags | PML_FORK;
+
+ currentdir = (addr_t) BOOTPML4;
+
+ // The virtual memory structures are now available on the predefined
+ // locations. This means the virtual memory code is bootstrapped. Of
+ // course, we still have no physical page allocator, so that's the
+ // next step.
+
+ PML* const PHYSPML3 = (PML* const) 0x0B000UL;
+ PML* const PHYSPML2 = (PML* const) 0x0C000UL;
+ PML* const PHYSPML1 = (PML* const) 0x0D000UL;
+ PML* const PHYSPML0 = (PML* const) 0x0E000UL;
+
+ BOOTPML4->entry[509] = (addr_t) PHYSPML3 | flags;
+ PHYSPML3->entry[0] = (addr_t) PHYSPML2 | flags;
+ PHYSPML2->entry[0] = (addr_t) PHYSPML1 | flags;
+ PHYSPML1->entry[0] = (addr_t) PHYSPML0 | flags;
+
+ Page::stackused = 0;
+ Page::stacklength = 4096UL / sizeof(addr_t);
+
+ // The physical memory allocator should now be ready for use. Next
+ // up, the calling function will fill up the physical allocator with
+ // plenty of nice physical pages. (see Page::InitPushRegion)
+ }
+ }
+}
diff --git a/sortix/x64/memorymanagement.h b/sortix/x64/memorymanagement.h
new file mode 100644
index 00000000..ea00d288
--- /dev/null
+++ b/sortix/x64/memorymanagement.h
@@ -0,0 +1,56 @@
+/******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
+
+ This file is part of Sortix.
+
+ Sortix 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 3 of the License, or (at your option) any later
+ version.
+
+ Sortix 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 Sortix. If not, see .
+
+ memorymanagement.h
+ Handles memory for the x64 architecture.
+
+******************************************************************************/
+
+#ifndef SORTIX_X64_MEMORYMANAGEMENT_H
+#define SORTIX_X64_MEMORYMANAGEMENT_H
+
+namespace Sortix
+{
+ namespace Memory
+ {
+ const size_t TOPPMLLEVEL = 4;
+ const size_t ENTRIES = 4096UL / sizeof(addr_t);
+ const size_t TRANSBITS = 9;
+
+ PML* const PMLS[TOPPMLLEVEL + 1] =
+ {
+ (PML* const) 0x0,
+ (PML* const) 0xFFFFFF8000000000UL,
+ (PML* const) 0xFFFFFF7FC0000000UL,
+ (PML* const) 0XFFFFFF7FBFE00000UL,
+ (PML* const) 0xFFFFFF7FBFDFF000UL,
+ };
+
+ PML* const FORKPML = (PML* const) 0xFFFFFF0000000000UL;
+ }
+
+ namespace Page
+ {
+ addr_t* const STACK = (addr_t* const) 0xFFFFFE8000000000UL;
+ const size_t MAXSTACKSIZE = (512UL*1024UL*1024UL*1024UL);
+ const size_t MAXSTACKLENGTH = MAXSTACKSIZE / sizeof(addr_t);
+ }
+}
+
+#endif
diff --git a/sortix/x86-family/memorymanagement.cpp b/sortix/x86-family/memorymanagement.cpp
new file mode 100644
index 00000000..62ceb826
--- /dev/null
+++ b/sortix/x86-family/memorymanagement.cpp
@@ -0,0 +1,544 @@
+/******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
+
+ This file is part of Sortix.
+
+ Sortix 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 3 of the License, or (at your option) any later
+ version.
+
+ Sortix 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 Sortix. If not, see .
+
+ memorymanagement.cpp
+ Handles memory for the x86 family of architectures.
+
+******************************************************************************/
+
+#include "platform.h"
+#include
+#include "multiboot.h"
+#include "panic.h"
+#include "../memorymanagement.h"
+#include "memorymanagement.h"
+
+namespace Sortix
+{
+ const addr_t KERNELEND = 0x200000UL;
+
+ namespace Page
+ {
+ void InitPushRegion(addr_t position, size_t length);
+ size_t pagesnotonstack;
+ size_t stackused;
+ size_t stacklength;
+ }
+
+ namespace Memory
+ {
+ addr_t currentdir;
+
+ void InitCPU();
+ void AllocateKernelPMLs();
+
+ void Init(multiboot_info_t* bootinfo)
+ {
+ Page::pagesnotonstack = 0;
+
+ if ( !( bootinfo->flags & MULTIBOOT_INFO_MEM_MAP ) )
+ {
+ Panic("memorymanagement.cpp: The memory map flag was't set in "
+ "the multiboot structure. Are your bootloader multiboot "
+ "specification compliant?");
+ }
+
+ // Initialize CPU-specific things.
+ InitCPU();
+
+ typedef const multiboot_memory_map_t* mmap_t;
+
+ // Loop over every detected memory region.
+ for (
+ mmap_t mmap = (mmap_t) bootinfo->mmap_addr;
+ (addr_t) mmap < bootinfo->mmap_addr + bootinfo->mmap_length;
+ mmap = (mmap_t) ((addr_t) mmap + mmap->size + sizeof(mmap->size))
+ )
+ {
+ // Check that we can use this kind of RAM.
+ if ( mmap->type != 1 ) { continue; }
+
+ // The kernels code may split this memory area into multiple pieces.
+ addr_t base = (addr_t) mmap->addr;
+ size_t length = mmap->len;
+
+ #ifdef PLATFORM_X86
+ // Figure out if the memory area is addressable (are our pointers big enough?)
+ if ( 0xFFFFFFFFULL < mmap->addr ) { continue; }
+ if ( 0xFFFFFFFFULL < mmap->addr + mmap->len ) { length = 0x100000000ULL - mmap->addr; }
+ #endif
+
+ // Detect if this memory is completely covered by the kernel.
+ if ( base + length <= KERNELEND ) { continue; }
+
+ // Detect if this memory is partially covered by the kernel.
+ if ( base <= KERNELEND && KERNELEND <= base + length )
+ {
+ length = (base + length) - KERNELEND;
+ base = KERNELEND;
+ }
+
+ Page::InitPushRegion(base, length);
+ }
+
+ // If the physical allocator couldn't handle the vast amount of
+ // physical pages, it may decide to drop some. This shouldn't happen
+ // until the pebibyte era of RAM.
+ if ( 0 < Page::pagesnotonstack )
+ {
+ Log::PrintF("%zu bytes of RAM aren't used due to technical"
+ "restrictions.\n", Page::pagesnotonstack * 0x1000UL);
+ }
+
+ // Finish allocating the top level PMLs for the kernels use.
+ AllocateKernelPMLs();
+ }
+
+ // Prepare the non-forkable kernel PMLs such that forking the kernel
+ // address space will always keep the kernel mapped.
+ void AllocateKernelPMLs()
+ {
+ const addr_t flags = PML_PRESENT | PML_WRITABLE;
+
+ PML* const pml = PMLS[TOPPMLLEVEL];
+
+ size_t start = ENTRIES / 2;
+ size_t end = ENTRIES;
+
+ for ( size_t i = start; i < end; i++ )
+ {
+ if ( pml->entry[i] & PML_PRESENT ) { continue; }
+
+ addr_t page = Page::Get();
+ if ( !page ) { Panic("out of memory allocating boot PMLs"); }
+
+ pml->entry[i] = page | flags;
+ }
+ }
+ }
+
+ namespace Page
+ {
+ void ExtendStack()
+ {
+ // This call will always succeed, if it didn't, then the stack
+ // wouldn't be full, and thus this function won't be called.
+ addr_t page = Get();
+
+ // This call will also succeed, since there are plenty of physical
+ // pages available and it might need some.
+ Memory::MapKernel(page, (addr_t) (STACK + stacklength));
+
+ // TODO: This may not be needed during the boot process!
+ //Memory::InvalidatePage((addr_t) (STACK + stacklength));
+
+ stacklength += 4096UL / sizeof(addr_t);
+ }
+
+ void InitPushRegion(addr_t position, size_t length)
+ {
+ // Align our entries on page boundaries.
+ addr_t newposition = Page::AlignUp(position);
+ length = Page::AlignDown((position + length) - newposition);
+ position = newposition;
+
+ while ( length )
+ {
+ if ( unlikely(stackused == stacklength) )
+ {
+ if ( stackused == MAXSTACKLENGTH )
+ {
+ pagesnotonstack += length / 4096UL;
+ return;
+ }
+
+ ExtendStack();
+ }
+
+ STACK[stackused++] = position;
+
+ length -= 4096UL;
+ position += 4096UL;
+ }
+ }
+
+ addr_t Get()
+ {
+ // TODO: Set out of memory errno here!
+ if ( unlikely(stackused == 0) ) { return 0; }
+
+ return STACK[--stackused];
+ }
+
+ void Put(addr_t page)
+ {
+ ASSERT(stackused < MAXSTACKLENGTH);
+ STACK[stackused++] = page;
+ }
+ }
+
+ namespace Memory
+ {
+ void InvalidatePage(addr_t /*addr*/)
+ {
+ // TODO: Actually just call the instruction.
+ Flush();
+ }
+
+ // Flushes the Translation Lookaside Buffer (TLB).
+ void Flush()
+ {
+ asm volatile("mov %0, %%cr3":: "r"(currentdir));
+ }
+
+ addr_t SwitchAddressSpace(addr_t addrspace)
+ {
+ // Don't switch if we are already there.
+ if ( addrspace == currentdir ) { return currentdir; }
+
+ addr_t previous = currentdir;
+
+ // Swtich and flush the TLB.
+ asm volatile("mov %0, %%cr3":: "r"(addrspace));
+
+ currentdir = addrspace;
+
+ return previous;
+ }
+
+ bool MapRangeKernel(addr_t where, size_t bytes)
+ {
+ for ( addr_t page = where; page < where + bytes; page += 4096UL )
+ {
+ addr_t physicalpage = Page::Get();
+ if ( physicalpage == 0 )
+ {
+ while ( where < page )
+ {
+ page -= 4096UL;
+ physicalpage = UnmapKernel(page);
+ Page::Put(physicalpage);
+ }
+ return false;
+ }
+
+ MapKernel(physicalpage, page);
+ }
+
+ return true;
+ }
+
+ void UnmapRangeKernel(addr_t where, size_t bytes)
+ {
+ for ( addr_t page = where; page < where + bytes; page += 4096UL )
+ {
+ addr_t physicalpage = UnmapKernel(page);
+ Page::Put(physicalpage);
+ }
+ }
+
+ bool MapRangeUser(addr_t where, size_t bytes)
+ {
+ for ( addr_t page = where; page < where + bytes; page += 4096UL )
+ {
+ addr_t physicalpage = Page::Get();
+ if ( physicalpage == 0 || !MapUser(physicalpage, page) )
+ {
+ while ( where < page )
+ {
+ page -= 4096UL;
+ physicalpage = UnmapUser(page);
+ Page::Put(physicalpage);
+ }
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void UnmapRangeUser(addr_t where, size_t bytes)
+ {
+ for ( addr_t page = where; page < where + bytes; page += 4096UL )
+ {
+ addr_t physicalpage = UnmapUser(page);
+ Page::Put(physicalpage);
+ }
+ }
+
+ template
+ bool Map(addr_t physical, addr_t mapto)
+ {
+ const addr_t userflags = userspace ? (PML_USERSPACE | PML_FORK) : 0;
+ const addr_t flags = userflags | PML_PRESENT | PML_WRITABLE;
+
+ const size_t MASK = (1<> (12+(i-1)*TRANSBITS)) & MASK;
+ }
+
+ size_t offset = 0;
+ for ( size_t i = TOPPMLLEVEL; i > 1; i-- )
+ {
+ size_t childid = pmlchildid[i];
+ PML* pml = PMLS[i] + offset;
+
+ addr_t& entry = pml->entry[childid];
+
+ if ( !(entry & PML_PRESENT) )
+ {
+ // TODO: Possible memory leak when page allocation fails.
+ addr_t page = Page::Get();
+ if ( page == 0 ) { return false; }
+ entry = page | flags;
+
+ // Invalidate the new PML and reset it to zeroes.
+ addr_t pmladdr = (addr_t) (PMLS[i-1] + childid);
+ InvalidatePage(pmladdr);
+ Maxsi::Memory::Set((void*) pmladdr, 0, sizeof(PML));
+ }
+ else if ( userspace && !(entry & PML_USERSPACE) )
+ {
+ PanicF("attempted to map physical page %p to virtual page "
+ "%p with userspace permissions, but the virtual page"
+ "wasn't in an userspace PML[%zu]. This is a bug in the"
+ "code calling this function", physical, mapto, i-1);
+ }
+
+ offset = offset * ENTRIES + childid;
+ }
+
+ (PMLS[1] + offset)->entry[pmlchildid[1]] = physical | flags;
+
+ if ( invalidate )
+ {
+ InvalidatePage(mapto);
+ }
+
+ return true;
+ }
+
+ template
+ addr_t Unmap(addr_t mapto)
+ {
+ const size_t MASK = (1<> (12+(i-1)*TRANSBITS)) & MASK;
+ }
+
+ size_t offset = 0;
+ for ( size_t i = TOPPMLLEVEL; i > 1; i-- )
+ {
+ size_t childid = pmlchildid[i];
+ PML* pml = PMLS[i] + offset;
+
+ addr_t& entry = pml->entry[childid];
+
+ if ( !(entry & PML_PRESENT) )
+ {
+ PanicF("attempted to unmap virtual page %p with userspace, "
+ " but the virtual page wasn't mapped. This is a bug "
+ "in the code calling this function", mapto);
+ }
+ else if ( userspace && !(entry & PML_USERSPACE) )
+ {
+ PanicF("attempted to unmap virtual page %p it wasn't in an"
+ "userspace PML[%zu]. This is a bug in the code"
+ "calling this function", mapto, i-1);
+ }
+
+ offset = offset * ENTRIES + childid;
+ }
+
+ addr_t& entry = (PMLS[1] + offset)->entry[pmlchildid[1]];
+ addr_t result = entry & PML_ADDRESS;
+ entry = 0;
+
+ // TODO: If all the entries in PML[N] are not-present, then who
+ // unmaps its entry from PML[N-1]?
+
+ if ( invalidate )
+ {
+ Flush();
+ }
+
+ return result;
+ }
+
+ bool MapKernel(addr_t physical, addr_t mapto)
+ {
+ return Map(physical, mapto);
+ }
+
+ bool MapUser(addr_t physical, addr_t mapto)
+ {
+ return Map(physical, mapto);
+ }
+
+ addr_t UnmapKernel(addr_t mapto)
+ {
+ return Unmap(mapto);
+ }
+
+ addr_t UnmapUser(addr_t mapto)
+ {
+ return Unmap(mapto);
+ }
+
+ // TODO: Copying every frame is endlessly useless in many uses. It'd be
+ // nice to upgrade this to a copy-on-demand algorithm.
+ addr_t Fork()
+ {
+ addr_t newtoppmladdr = Page::Get();
+ if ( newtoppmladdr == 0 ) { return 0; }
+
+ // This is either bad code or very clever code and probably is both.
+ size_t positionstack[TOPPMLLEVEL+1];
+ positionstack[TOPPMLLEVEL] = 0;
+ size_t level = TOPPMLLEVEL;
+ size_t pmloffset = 0;
+ bool failure = false;
+
+ // This call always succeeds.
+ MapKernel(newtoppmladdr, (addr_t) (FORKPML + level));
+ InvalidatePage((addr_t) (FORKPML + level));
+
+ while ( positionstack[TOPPMLLEVEL] < ENTRIES )
+ {
+ if ( level == 1 )
+ {
+ //Log::PrintF("[%zu > %zu]", positionstack[2], positionstack[1]);
+ }
+ else
+ {
+ //Log::PrintF("[%zu]", positionstack[2]);
+ }
+
+ const size_t pos = positionstack[level];
+
+ if ( pos == ENTRIES )
+ {
+ //Log::PrintF(" done with level\n");
+ (positionstack[++level])++;
+ pmloffset /= ENTRIES;
+ continue;
+ }
+
+ addr_t entry = (PMLS[level] + pmloffset)->entry[pos];
+
+ // If the entry should be forked, fork it!
+ if ( likely(entry & PML_FORK) )
+ {
+ // Pop the physical address of somewhere unused.
+ addr_t phys = Page::Get();
+
+ if ( unlikely(phys == 0) )
+ {
+ //Log::PrintF(" out of memory!\n");
+ // Oh no. Out of memory! We'll have to undo everything
+ // we just did. Argh!
+ failure = true;
+ break;
+ }
+
+ // Map the destination page.
+ addr_t destaddr = (addr_t) (FORKPML + level-1);
+ MapKernel(phys, destaddr);
+ InvalidatePage(destaddr);
+
+ // Set its entry in the owner.
+ addr_t flags = entry & PML_FLAGS;
+ (FORKPML + level)->entry[pos] = phys | flags;
+
+ if ( level == 1 )
+ {
+ //Log::PrintF(" copy\n");
+
+ // Determine the source page's address.
+ const void* src = (const void*) (pmloffset * 4096UL);
+
+ // Determine the destination page's address.
+ void* dest = (void*) (FORKPML + level - 1);
+
+ Maxsi::Memory::Copy(dest, src, sizeof(PML));
+ }
+ else
+ {
+ //Log::PrintF(" recurse\n");
+
+ // Fork the PML recursively!
+ pmloffset = pmloffset * ENTRIES + pos;
+ positionstack[--level] = 0;
+ continue;
+ }
+ }
+
+ // If this entry should be linked, link it.
+ else
+ {
+ //Log::PrintF(" link\n");
+ FORKPML[level].entry[pos] = entry;
+ }
+
+ positionstack[level]++;
+ }
+
+ //Log::PrintF("Fork: Loop Terminated\n");
+
+ if ( !failure )
+ {
+ // Now, the new top pml needs to have its fractal memory fixed.
+ const addr_t flags = PML_PRESENT | PML_WRITABLE;
+ addr_t mapto;
+ addr_t childaddr;
+
+ (FORKPML + TOPPMLLEVEL)->entry[ENTRIES-1] = newtoppmladdr | flags;
+ childaddr = (FORKPML + TOPPMLLEVEL)->entry[ENTRIES-2] & PML_ADDRESS;
+
+ for ( size_t i = TOPPMLLEVEL-1; i > 0; i-- )
+ {
+ mapto = (addr_t) (FORKPML + i);
+ MapKernel(childaddr, mapto);
+ InvalidatePage(mapto);
+ (FORKPML + i)->entry[ENTRIES-1] = newtoppmladdr | flags;
+ childaddr = (FORKPML + i)->entry[ENTRIES-2] & PML_ADDRESS;
+ }
+
+ //Log::PrintF("Fork: Done\n");
+
+ return newtoppmladdr;
+ }
+
+ // The fork failed, so'll have to clean up the new address space and
+ // free all the pages we forked so far. It'd be nice to detect that
+ // this would happen early on, but it seems to impractical or
+ // inefficient. Let's just do the dirty work and clean up.
+
+ // TODO: Fix this error condition by deleting the new pages.
+ Panic("Out of memory during fork. This isn't supported yet.");
+
+ return 0;
+ }
+ }
+}
+
diff --git a/sortix/x86-family/memorymanagement.h b/sortix/x86-family/memorymanagement.h
new file mode 100644
index 00000000..20d05d4b
--- /dev/null
+++ b/sortix/x86-family/memorymanagement.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
+
+ This file is part of Sortix.
+
+ Sortix 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 3 of the License, or (at your option) any later
+ version.
+
+ Sortix 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 Sortix. If not, see .
+
+ memorymanagement.h
+ Handles memory for the x86 family of architectures.
+
+******************************************************************************/
+
+#ifndef SORTIX_X86_FAMILY_MEMORYMANAGEMENT_H
+#define SORTIX_X86_FAMILY_MEMORYMANAGEMENT_H
+
+namespace Sortix
+{
+ struct PML
+ {
+ addr_t entry[4096 / sizeof(addr_t)];
+ };
+
+ namespace Memory
+ {
+ const addr_t PML_PRESENT = (1<<0);
+ const addr_t PML_WRITABLE = (1<<1);
+ const addr_t PML_USERSPACE = (1<<2);
+ const addr_t PML_AVAILABLE1 = (1<<9);
+ const addr_t PML_AVAILABLE2 = (1<<10);
+ const addr_t PML_AVAILABLE3 = (1<<11);
+ const addr_t PML_FORK = PML_AVAILABLE1;
+ const addr_t PML_FLAGS = (0xFFFUL); // Bits used for the flags.
+ const addr_t PML_ADDRESS = (~0xFFFUL); // Bits used for the address.
+ }
+}
+
+#ifdef PLATFORM_X86
+#include "../x86/memorymanagement.h"
+#endif
+
+#ifdef PLATFORM_X64
+#include "../x64/memorymanagement.h"
+#endif
+
+#endif
diff --git a/sortix/x86/memorymanagement.cpp b/sortix/x86/memorymanagement.cpp
new file mode 100644
index 00000000..fc78619d
--- /dev/null
+++ b/sortix/x86/memorymanagement.cpp
@@ -0,0 +1,104 @@
+/******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
+
+ This file is part of Sortix.
+
+ Sortix 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 3 of the License, or (at your option) any later
+ version.
+
+ Sortix 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 Sortix. If not, see .
+
+ memorymanagement.cpp
+ Handles memory for the x86 architecture.
+
+******************************************************************************/
+
+#include "platform.h"
+#include
+#include "multiboot.h"
+#include "panic.h"
+#include "../memorymanagement.h"
+#include "x86-family/memorymanagement.h"
+
+namespace Sortix
+{
+ namespace Page
+ {
+ extern size_t stackused;
+ extern size_t stacklength;
+ }
+
+ namespace Memory
+ {
+ extern addr_t currentdir;
+
+ void InitCPU()
+ {
+ PML* const BOOTPML2 = (PML* const) 0x01000UL;
+ PML* const BOOTPML1 = (PML* const) 0x02000UL;
+ PML* const FORKPML1 = (PML* const) 0x03000UL;
+ PML* const IDENPML1 = (PML* const) 0x04000UL;
+
+ // Initialize the memory structures with zeroes.
+ Maxsi::Memory::Set(BOOTPML1, 0, 0x6000UL);
+
+ // Identity map the first 4 MiB.
+ addr_t flags = PML_PRESENT | PML_WRITABLE;
+
+ BOOTPML2->entry[0] = ((addr_t) IDENPML1) | flags;
+
+ for ( size_t i = 0; i < ENTRIES; i++ )
+ {
+ IDENPML1->entry[i] = (i * 4096UL) | flags;
+ }
+
+ // Next order of business is to map the virtual memory structures
+ // to the pre-defined locations in the virtual address space.
+
+ // Fractal map the PML1s.
+ BOOTPML2->entry[1023] = (addr_t) BOOTPML2 | flags;
+
+ // Fractal map the PML2s.
+ BOOTPML2->entry[1022] = (addr_t) BOOTPML1 | flags | PML_FORK;
+ BOOTPML1->entry[1023] = (addr_t) BOOTPML2 | flags;
+
+ // Add some predefined room for forking address spaces.
+ BOOTPML1->entry[0] = (addr_t) FORKPML1 | flags | PML_FORK;
+
+ // The virtual memory structures are now available on the predefined
+ // locations. This means the virtual memory code is bootstrapped. Of
+ // course, we still have no physical page allocator, so that's the
+ // next step.
+
+ PML* const PHYSPML1 = (PML* const) 0x05000UL;
+ PML* const PHYSPML0 = (PML* const) 0x06000UL;
+
+ BOOTPML2->entry[1021] = (addr_t) PHYSPML1 | flags;
+ PHYSPML1->entry[0] = (addr_t) PHYSPML0 | flags;
+
+ // Alright, enable virtual memory!
+ SwitchAddressSpace((addr_t) BOOTPML2);
+
+ size_t cr0;
+ asm volatile("mov %%cr0, %0": "=r"(cr0));
+ cr0 |= 0x80000000UL; /* Enable paging! */
+ asm volatile("mov %0, %%cr0":: "r"(cr0));
+
+ Page::stackused = 0;
+ Page::stacklength = 4096UL / sizeof(addr_t);
+
+ // The physical memory allocator should now be ready for use. Next
+ // up, the calling function will fill up the physical allocator with
+ // plenty of nice physical pages. (see Page::InitPushRegion)
+ }
+ }
+}
diff --git a/sortix/x86/memorymanagement.h b/sortix/x86/memorymanagement.h
new file mode 100644
index 00000000..45f8263b
--- /dev/null
+++ b/sortix/x86/memorymanagement.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
+
+ This file is part of Sortix.
+
+ Sortix 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 3 of the License, or (at your option) any later
+ version.
+
+ Sortix 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 Sortix. If not, see .
+
+ memorymanagement.h
+ Handles memory for the x64 architecture.
+
+******************************************************************************/
+
+#ifndef SORTIX_X64_MEMORYMANAGEMENT_H
+#define SORTIX_X64_MEMORYMANAGEMENT_H
+
+namespace Sortix
+{
+ namespace Memory
+ {
+ const size_t TOPPMLLEVEL = 2;
+ const size_t ENTRIES = 4096UL / sizeof(addr_t);
+ const size_t TRANSBITS = 10;
+
+ PML* const PMLS[TOPPMLLEVEL + 1] =
+ {
+ (PML* const) 0x0,
+ (PML* const) 0xFFC00000UL,
+ (PML* const) 0xFFBFF000UL,
+ };
+
+ PML* const FORKPML = (PML* const) 0xFF800000UL;
+ }
+
+ namespace Page
+ {
+ addr_t* const STACK = (addr_t* const) 0xFF400000UL;
+ const size_t MAXSTACKSIZE = (4UL*1024UL*1024UL);
+ const size_t MAXSTACKLENGTH = MAXSTACKSIZE / sizeof(addr_t);
+ }
+}
+
+#endif