From f67d695254bf9e0d053e064ade4387665180f27d Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 18 Oct 2018 13:05:00 +0200 Subject: [PATCH] More paging stuff. The test userspace process now runs at linear address 0x300000 which is mapped to a dynamically allocated page from the MemoryManager. Cool! --- Kernel/MemoryManager.cpp | 104 ++++++++++++++++++++++++++++++++++++--- Kernel/MemoryManager.h | 63 ++++++++++++++++-------- Kernel/Syscall.cpp | 28 ----------- Kernel/Syscall.h | 32 ++++++++++-- Kernel/Task.cpp | 36 +++++++++++++- Kernel/Task.h | 11 +++++ Kernel/i386.cpp | 43 ++++++++++++++-- Kernel/i386.h | 43 ++++++++++++++++ Kernel/init.cpp | 4 +- Kernel/types.h | 41 +++++++++++++++ 10 files changed, 339 insertions(+), 66 deletions(-) diff --git a/Kernel/MemoryManager.cpp b/Kernel/MemoryManager.cpp index b9fccb577b..48dd737337 100644 --- a/Kernel/MemoryManager.cpp +++ b/Kernel/MemoryManager.cpp @@ -4,6 +4,7 @@ #include #include "i386.h" #include "StdLib.h" +#include "Task.h" static MemoryManager* s_the; @@ -16,6 +17,7 @@ MemoryManager::MemoryManager() { m_pageDirectory = (dword*)0x5000; m_pageTableZero = (dword*)0x6000; + m_pageTableOne = (dword*)0x7000; initializePaging(); } @@ -29,12 +31,19 @@ void MemoryManager::initializePaging() static_assert(sizeof(MemoryManager::PageDirectoryEntry) == 4); static_assert(sizeof(MemoryManager::PageTableEntry) == 4); memset(m_pageTableZero, 0, 4096); + memset(m_pageTableOne, 0, 4096); memset(m_pageDirectory, 0, 4096); kprintf("MM: Page directory @ %p\n", m_pageDirectory); - kprintf("MM: Page table zero @ %p [0]=%x\n", m_pageTableZero, m_pageTableZero[0]); - // Build a basic PDB that identity maps the first 1MB. + kprintf("MM: Page table zero @ %p\n", m_pageTableZero); + kprintf("MM: Page table one @ %p\n", m_pageTableOne); + identityMap(LinearAddress(0), 4 * MB); + + // Put pages between 4MB and 16MB in the page freelist. + for (size_t i = (4 * MB) + 1024; i < (16 * MB); i += PAGE_SIZE) { + m_freePages.append(PhysicalAddress(i)); + } asm volatile("movl %%eax, %%cr3"::"a"(m_pageDirectory)); asm volatile( @@ -57,6 +66,11 @@ auto MemoryManager::ensurePTE(LinearAddress linearAddress) -> PageTableEntry pde.setUserAllowed(true); pde.setPresent(true); pde.setWritable(true); + } else if (pageDirectoryIndex == 1) { + pde.setPageTableBase((dword)m_pageTableOne); + pde.setUserAllowed(false); + pde.setPresent(true); + pde.setWritable(false); } else { // FIXME: We need an allocator! ASSERT_NOT_REACHED(); @@ -75,10 +89,6 @@ void MemoryManager::identityMap(LinearAddress linearAddress, size_t length) pte.setUserAllowed(true); pte.setPresent(true); pte.setWritable(true); - if (pteAddress.get() == 0x6023) { - kprintf("kek\n"); - HANG; - } } } @@ -87,4 +97,86 @@ void MemoryManager::initialize() s_the = new MemoryManager; } +PageFaultResponse MemoryManager::handlePageFault(const PageFault& fault) +{ + kprintf("MM: handlePageFault(%w) at laddr=%p\n", fault.code(), fault.address().get()); + if (fault.isNotPresent()) { + kprintf(" >> NP fault!\n"); + } else if (fault.isProtectionViolation()) { + kprintf(" >> PV fault!\n"); + } + return PageFaultResponse::ShouldCrash; +} + +RetainPtr MemoryManager::createZone(size_t size) +{ + auto pages = allocatePhysicalPages(ceilDiv(size, PAGE_SIZE)); + if (pages.isEmpty()) { + kprintf("MM: createZone: no physical pages for size %u", size); + return nullptr; + } + return adopt(*new Zone(move(pages))); +} + +Vector MemoryManager::allocatePhysicalPages(size_t count) +{ + kprintf("MM: alloc %u pages from %u available\n", count, m_freePages.size()); + if (count > m_freePages.size()) + return { }; + + Vector pages; + pages.ensureCapacity(count); + for (size_t i = 0; i < count; ++i) + pages.append(m_freePages.takeLast()); + kprintf("MM: returning the pages (%u of them)\n", pages.size()); + return pages; +} + +byte* MemoryManager::quickMapOnePage(PhysicalAddress physicalAddress) +{ + auto pte = ensurePTE(LinearAddress(4 * MB)); + kprintf("quickmap %x @ %x {pte @ %p}\n", physicalAddress.get(), 4*MB, pte.ptr()); + pte.setPhysicalPageBase(physicalAddress.pageBase()); + pte.setPresent(true); + pte.setWritable(true); + return (byte*)(4 * MB); +} + +bool MemoryManager::unmapZonesForTask(Task& task) +{ + return true; +} + +bool MemoryManager::mapZonesForTask(Task& task) +{ + for (auto& mappedZone : task.m_mappedZones) { + auto& zone = *mappedZone.zone; + for (size_t i = 0; i < zone.m_pages.size(); ++i) { + auto pte = ensurePTE(mappedZone.linearAddress.offset(i * PAGE_SIZE)); + pte.setPhysicalPageBase(zone.m_pages[i].get()); + pte.setPresent(true); + } + } + return true; +} + +bool copyToZone(Zone& zone, const void* data, size_t size) +{ + if (zone.size() < size) { + kprintf("copyToZone: can't fit %u bytes into zone with size %u\n", size, zone.size()); + return false; + } + + auto* dataptr = (const byte*)data; + size_t remaining = size; + for (size_t i = 0; i < zone.m_pages.size(); ++i) { + byte* dest = MemoryManager::the().quickMapOnePage(zone.m_pages[i]); + kprintf("memcpy(%p, %p, %u)\n", dest, dataptr, min(PAGE_SIZE, remaining)); + memcpy(dest, dataptr, min(PAGE_SIZE, remaining)); + dataptr += PAGE_SIZE; + remaining -= PAGE_SIZE; + } + + return true; +} diff --git a/Kernel/MemoryManager.h b/Kernel/MemoryManager.h index 603557deb8..fe23d14e96 100644 --- a/Kernel/MemoryManager.h +++ b/Kernel/MemoryManager.h @@ -1,36 +1,39 @@ #pragma once -#include +#include "types.h" +#include "i386.h" +#include +#include +#include +#include -class PhysicalAddress { -public: - PhysicalAddress() { } - explicit PhysicalAddress(dword address) : m_address(address) { } +class Task; - dword get() const { return m_address; } - void set(dword address) { m_address = address; } - void mask(dword m) { m_address &= m; } - -private: - dword m_address { 0 }; +enum class PageFaultResponse { + ShouldCrash, + Continue, }; -class LinearAddress { +struct Zone : public Retainable { public: - LinearAddress() { } - explicit LinearAddress(dword address) : m_address(address) { } + ~Zone() { } + size_t size() const { return m_pages.size() * PAGE_SIZE; } - LinearAddress offset(dword o) const { return LinearAddress(m_address + o); } - dword get() const { return m_address; } - void set(dword address) { m_address = address; } - void mask(dword m) { m_address &= m; } - - bool operator==(const LinearAddress& other) const { return m_address == other.m_address; } + const Vector& pages() const { return m_pages; } private: - dword m_address { 0 }; + friend class MemoryManager; + friend bool copyToZone(Zone&, const void* data, size_t); + explicit Zone(Vector&& pages) + : m_pages(move(pages)) + { + } + + Vector m_pages; }; +bool copyToZone(Zone&, const void* data, size_t); + class MemoryManager { public: static MemoryManager& the(); @@ -38,6 +41,17 @@ public: PhysicalAddress pageDirectoryBase() const { return PhysicalAddress(reinterpret_cast(m_pageDirectory)); } static void initialize(); + + PageFaultResponse handlePageFault(const PageFault&); + + RetainPtr createZone(size_t); + + // HACK: don't use this jeez :( + byte* quickMapOnePage(PhysicalAddress); + + bool mapZonesForTask(Task&); + bool unmapZonesForTask(Task&); + private: MemoryManager(); ~MemoryManager(); @@ -46,6 +60,8 @@ private: void identityMap(LinearAddress, size_t length); + Vector allocatePhysicalPages(size_t count); + struct PageDirectoryEntry { explicit PageDirectoryEntry(dword* pde) : m_pde(pde) { } @@ -128,4 +144,9 @@ private: dword* m_pageDirectory; dword* m_pageTableZero; + dword* m_pageTableOne; + + HashMap> m_zones; + + Vector m_freePages; }; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index e6f10cae20..ebf0f71eef 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -45,34 +45,6 @@ void initialize() kprintf("syscall: int 0x80 handler installed\n"); } -DWORD invoke(DWORD function) -{ - DWORD result; - asm("int $0x80":"=a"(result):"a"(function)); - return result; -} - -DWORD invoke(DWORD function, DWORD arg1) -{ - DWORD result; - asm("int $0x80":"=a"(result):"a"(function),"d"(arg1)); - return result; -} - -DWORD invoke(DWORD function, DWORD arg1, DWORD arg2) -{ - DWORD result; - asm("int $0x80":"=a"(result):"a"(function),"d"(arg1),"c"(arg2)); - return result; -} - -DWORD invoke(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3) -{ - DWORD result; - asm volatile("int $0x80":"=a"(result):"a"(function),"d"(arg1),"c"(arg2),"b"(arg3)); - return result; -} - DWORD handle(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3) { switch (function) { diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 82e3c43f12..25f38b2c4d 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -19,9 +19,33 @@ enum Function { }; void initialize(); -DWORD invoke(DWORD function); -DWORD invoke(DWORD function, DWORD arg1); -DWORD invoke(DWORD function, DWORD arg1, DWORD arg2); -DWORD invoke(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3); + +inline DWORD invoke(DWORD function) +{ + DWORD result; + asm("int $0x80":"=a"(result):"a"(function)); + return result; +} + +inline DWORD invoke(DWORD function, DWORD arg1) +{ + DWORD result; + asm("int $0x80":"=a"(result):"a"(function),"d"(arg1)); + return result; +} + +inline DWORD invoke(DWORD function, DWORD arg1, DWORD arg2) +{ + DWORD result; + asm("int $0x80":"=a"(result):"a"(function),"d"(arg1),"c"(arg2)); + return result; +} + +inline DWORD invoke(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3) +{ + DWORD result; + asm volatile("int $0x80":"=a"(result):"a"(function),"d"(arg1),"c"(arg2),"b"(arg3)); + return result; +} } diff --git a/Kernel/Task.cpp b/Kernel/Task.cpp index aeba9996e3..34e58ab52a 100644 --- a/Kernel/Task.cpp +++ b/Kernel/Task.cpp @@ -92,6 +92,14 @@ void Task::allocateLDT() m_tss.ldt = newLDTSelector; } +bool Task::mapZone(LinearAddress address, RetainPtr&& zone) +{ + // FIXME: This needs sanity checks. What if this overlaps existing zones? + kprintf("mapped zone with size %u at %x\n", zone->size(), address.get()); + m_mappedZones.append({ address, move(zone) }); + return true; +} + Task::Task(void (*e)(), const char* n, IPC::Handle h, RingLevel ring) : m_name(n) , m_entry(e) @@ -100,6 +108,19 @@ Task::Task(void (*e)(), const char* n, IPC::Handle h, RingLevel ring) , m_state(Runnable) , m_ring(ring) { + if (!isRing0()) { + auto zone = MemoryManager::the().createZone(PAGE_SIZE); + ASSERT(zone); + + kprintf("New task zone: { size: %u }\n", zone->size()); + + bool success = mapZone(LinearAddress(0x300000), zone.copyRef()); + ASSERT(success); + + success = copyToZone(*zone, (void*)e, PAGE_SIZE); + ASSERT(success); + } + memset(&m_tss, 0, sizeof(m_tss)); memset(&m_ldtEntries, 0, sizeof(m_ldtEntries)); @@ -133,7 +154,13 @@ Task::Task(void (*e)(), const char* n, IPC::Handle h, RingLevel ring) m_tss.cr3 = MemoryManager::the().pageDirectoryBase().get(); - m_tss.eip = (DWORD)m_entry; + if (isRing0()) { + m_tss.eip = (DWORD)m_entry; + } else { + m_tss.eip = m_mappedZones[0].linearAddress.get(); + } + + kprintf("basically ready\n"); // NOTE: Each task gets 4KB of stack. // This memory is leaked ATM. @@ -165,7 +192,7 @@ Task::Task(void (*e)(), const char* n, IPC::Handle h, RingLevel ring) system.nprocess++; - kprintf("Task %u (%s) spawned @ %p\n", m_pid, m_name.characters(), m_entry); + kprintf("Task %u (%s) spawned @ %p\n", m_pid, m_name.characters(), m_tss.eip); } Task::~Task() @@ -300,6 +327,11 @@ static bool contextSwitch(Task* t) if (current->state() == Task::Running) current->setState(Task::Runnable); + bool success = MemoryManager::the().unmapZonesForTask(*current); + ASSERT(success); + success = MemoryManager::the().mapZonesForTask(*t); + ASSERT(success); + current = t; t->setState(Task::Running); diff --git a/Kernel/Task.h b/Kernel/Task.h index a2df7861de..347bafb2ca 100644 --- a/Kernel/Task.h +++ b/Kernel/Task.h @@ -11,6 +11,7 @@ //#define TASK_SANITY_CHECKS class FileHandle; +class Zone; class Task : public InlineLinkedListNode { friend class InlineLinkedListNode; @@ -97,6 +98,9 @@ public: static void taskDidCrash(Task*); private: + friend class MemoryManager; + + bool mapZone(LinearAddress, RetainPtr&&); FileHandle* openFile(String&&); void allocateLDT(); @@ -120,6 +124,13 @@ private: Vector> m_fileHandles; RingLevel m_ring { Ring0 }; int m_error { 0 }; + + struct MappedZone { + LinearAddress linearAddress; + RetainPtr zone; + }; + + Vector m_mappedZones; }; extern void task_init(); diff --git a/Kernel/i386.cpp b/Kernel/i386.cpp index f455eaa8c9..1406df5a96 100644 --- a/Kernel/i386.cpp +++ b/Kernel/i386.cpp @@ -4,6 +4,7 @@ #include "i386.h" #include "Assertions.h" #include "Task.h" +#include "MemoryManager.h" struct DescriptorTablePointer { WORD size; @@ -67,8 +68,8 @@ asm( \ " iret\n" \ ); -EH_ENTRY(13) - +// 13: General Protection Fault +EH_ENTRY(13); void exception_13_handler() { auto& regs = *reinterpret_cast(exception_state_dump); @@ -88,6 +89,42 @@ void exception_13_handler() Task::taskDidCrash(current); } +// 14: Page Fault +EH_ENTRY(14); +void exception_14_handler() +{ + dword faultAddress; + asm ("movl %%cr2, %%eax":"=a"(faultAddress)); + + auto& regs = *reinterpret_cast(exception_state_dump); + kprintf("%s page fault: %u(%s), %s laddr=%p\n", + current->isRing0() ? "Kernel" : "User", + current->pid(), + current->name().characters(), + exception_code & 2 ? "write" : "read", + faultAddress); + + kprintf("exception code: %w\n", exception_code); + kprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs); + kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); + kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, regs.esp, regs.esi, regs.edi); + + if (current->isRing0()) + HANG; + + auto response = MemoryManager::the().handlePageFault(PageFault(exception_code, LinearAddress(faultAddress))); + + if (response == PageFaultResponse::ShouldCrash) { + kprintf("Crashing after unresolved page fault\n"); + // NOTE: This will schedule a new task. + Task::taskDidCrash(current); + } else if (response == PageFaultResponse::Continue) { + kprintf("Continuing after resolved page fault\n"); + } else { + ASSERT_NOT_REACHED(); + } +} + #define EH(i, msg) \ static void _exception ## i () \ { \ @@ -227,7 +264,7 @@ void idt_init() registerInterruptHandler(0x0b, _exception11); registerInterruptHandler(0x0c, _exception12); registerInterruptHandler(0x0d, exception_13_entry); - registerInterruptHandler(0x0e, _exception14); + registerInterruptHandler(0x0e, exception_14_entry); registerInterruptHandler(0x0f, _exception15); registerInterruptHandler(0x10, _exception16); diff --git a/Kernel/i386.h b/Kernel/i386.h index c96001af22..b15b8b6069 100644 --- a/Kernel/i386.h +++ b/Kernel/i386.h @@ -2,6 +2,8 @@ #include "types.h" +#define PAGE_SIZE 4096u + union Descriptor { struct { WORD limit_lo; @@ -76,6 +78,42 @@ void writeGDTEntry(WORD selector, Descriptor&); /* Map IRQ0-15 @ ISR 0x50-0x5F */ #define IRQ_VECTOR_BASE 0x50 +struct PageFaultFlags { +enum Flags { + NotPresent = 0x00, + ProtectionViolation = 0x01, + Read = 0x00, + Write = 0x02, + UserMode = 0x04, + SupervisorMode = 0x00, + InstructionFetch = 0x08, +}; +}; + +class PageFault { +public: + PageFault(word code, LinearAddress address) + : m_code(code) + , m_address(address) + { + } + + LinearAddress address() const { return m_address; } + word code() const { return m_code; } + + bool isNotPresent() const { return (m_code & 1) == PageFaultFlags::NotPresent; } + bool isProtectionViolation() const { return (m_code & 1) == PageFaultFlags::ProtectionViolation; } + bool isRead() const { return (m_code & 2) == PageFaultFlags::Read; } + bool isWrite() const { return (m_code & 2) == PageFaultFlags::Write; } + bool isUser() const { return (m_code & 4) == PageFaultFlags::UserMode; } + bool isSupervisor() const { return (m_code & 4) == PageFaultFlags::SupervisorMode; } + bool isInstructionFetch() const { return (m_code & 8) == PageFaultFlags::InstructionFetch; } + +private: + word m_code; + LinearAddress m_address; +}; + struct RegisterDump { WORD gs; WORD fs; @@ -95,3 +133,8 @@ struct RegisterDump { DWORD eflags; } PACKED; +inline constexpr dword pageBaseOf(dword address) +{ + return address & 0xfffff000; +} + diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 81f032feed..fa91a36763 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -68,7 +68,7 @@ static void user_main() DO_SYSCALL_A3(0x3000, 2, 3, 4); // Crash ourselves! char* x = reinterpret_cast(0xbeefbabe); - //*x = 1; + *x = 1; HANG; for (;;) { // nothing? @@ -158,7 +158,7 @@ void init() //vfs->listDirectory("/"); -#if 1 +#if 0 { auto motdFile = vfs->open("/motd.txt"); ASSERT(motdFile); diff --git a/Kernel/types.h b/Kernel/types.h index 5c1f35d66d..8469e5cd6f 100644 --- a/Kernel/types.h +++ b/Kernel/types.h @@ -1,5 +1,7 @@ #pragma once +#include + #define PACKED __attribute__ ((packed)) #define NORETURN __attribute__ ((noreturn)) #define PURE __attribute__ ((pure)) @@ -26,3 +28,42 @@ struct FarPtr { DWORD offset { 0 }; WORD selector { 0 }; } PACKED; + +class PhysicalAddress { +public: + PhysicalAddress() { } + explicit PhysicalAddress(dword address) : m_address(address) { } + + dword get() const { return m_address; } + void set(dword address) { m_address = address; } + void mask(dword m) { m_address &= m; } + + byte* asPtr() { return reinterpret_cast(m_address); } + const byte* asPtr() const { return reinterpret_cast(m_address); } + + dword pageBase() const { return m_address & 0xfffff000; } + +private: + dword m_address { 0 }; +}; + +class LinearAddress { +public: + LinearAddress() { } + explicit LinearAddress(dword address) : m_address(address) { } + + LinearAddress offset(dword o) const { return LinearAddress(m_address + o); } + dword get() const { return m_address; } + void set(dword address) { m_address = address; } + void mask(dword m) { m_address &= m; } + + bool operator==(const LinearAddress& other) const { return m_address == other.m_address; } + + byte* asPtr() { return reinterpret_cast(m_address); } + const byte* asPtr() const { return reinterpret_cast(m_address); } + + dword pageBase() const { return m_address & 0xfffff000; } + +private: + dword m_address { 0 }; +};