mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
Initial work on swapping page tables per process
This commit is contained in:
@@ -219,6 +219,31 @@ page_manager::init(
|
||||
new (&g_kernel_memory_manager) kutil::memory_manager(
|
||||
reinterpret_cast<void *>(end),
|
||||
mm_grow_callback);
|
||||
|
||||
m_kernel_pml4 = get_pml4();
|
||||
}
|
||||
|
||||
page_table *
|
||||
page_manager::create_process_map()
|
||||
{
|
||||
page_table *table = get_table_page();
|
||||
kutil::memcpy(table, m_kernel_pml4, page_size);
|
||||
|
||||
// Create the initial user stack
|
||||
map_pages(
|
||||
initial_stack - (initial_stack_pages * page_size),
|
||||
initial_stack_pages,
|
||||
true, // This is the ring3 stack, user = true
|
||||
table);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void
|
||||
page_manager::delete_process_map(page_table *table)
|
||||
{
|
||||
// TODO: recurse table entries and mark them free
|
||||
unmap_pages(table, 1);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -255,9 +280,11 @@ page_manager::dump_blocks()
|
||||
}
|
||||
|
||||
void
|
||||
page_manager::dump_pml4()
|
||||
page_manager::dump_pml4(page_table *pml4)
|
||||
{
|
||||
get_pml4()->dump();
|
||||
if (pml4 == nullptr)
|
||||
pml4 = get_pml4();
|
||||
pml4->dump();
|
||||
}
|
||||
|
||||
page_block *
|
||||
@@ -345,10 +372,11 @@ page_manager::consolidate_blocks()
|
||||
}
|
||||
|
||||
void *
|
||||
page_manager::map_pages(addr_t address, size_t count)
|
||||
page_manager::map_pages(addr_t address, size_t count, bool user, page_table *pml4)
|
||||
{
|
||||
void *ret = reinterpret_cast<void *>(address);
|
||||
page_table *pml4 = get_pml4();
|
||||
if (pml4 == nullptr)
|
||||
pml4 = get_pml4();
|
||||
|
||||
while (count) {
|
||||
kassert(m_free, "page_manager::map_pages ran out of free pages!");
|
||||
@@ -365,7 +393,10 @@ page_manager::map_pages(addr_t address, size_t count)
|
||||
page_block_flags::mapped;
|
||||
page_block::insert(m_used, block);
|
||||
|
||||
page_in(pml4, phys, address, n);
|
||||
log::debug(logs::memory, "Paging in %d pages at p:%016lx to v:%016lx into %016lx table",
|
||||
n, phys, address, pml4);
|
||||
|
||||
page_in(pml4, phys, address, n, user);
|
||||
|
||||
address += n * page_size;
|
||||
count -= n;
|
||||
|
||||
@@ -26,13 +26,46 @@ public:
|
||||
/// Offset from physical where page tables are mapped.
|
||||
static const addr_t page_offset = 0xffffff8000000000;
|
||||
|
||||
/// Initial process thread's stack address
|
||||
static const addr_t initial_stack = 0x0000800000000000;
|
||||
|
||||
/// Initial process thread's stack size, in pages
|
||||
static const unsigned initial_stack_pages = 1;
|
||||
|
||||
page_manager();
|
||||
|
||||
/// Helper to read the PML4 table from CR3.
|
||||
/// \returns A pointer to the current PML4 table.
|
||||
static inline page_table * get_pml4()
|
||||
{
|
||||
addr_t pml4 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (pml4) );
|
||||
return reinterpret_cast<page_table *>((pml4 & ~0xfffull) + page_offset);
|
||||
}
|
||||
|
||||
/// Helper to set the PML4 table pointer in CR3.
|
||||
/// \arg pml4 A pointer to the PML4 table to install.
|
||||
static inline void set_pml4(page_table *pml4)
|
||||
{
|
||||
addr_t p = reinterpret_cast<addr_t>(pml4) - page_offset;
|
||||
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p & ~0xfffull) );
|
||||
}
|
||||
|
||||
/// Allocate but don't switch to a new PML4 table. This table
|
||||
/// should only have global kernel pages mapped.
|
||||
/// \returns A pointer to the PML4 table
|
||||
page_table * create_process_map();
|
||||
|
||||
/// Deallocate a process' PML4 table.
|
||||
void delete_process_map(page_table *table);
|
||||
|
||||
/// Allocate and map pages into virtual memory.
|
||||
/// \arg address The virtual address at which to map the pages
|
||||
/// \arg count The number of pages to map
|
||||
/// \arg user True is this memory is user-accessible
|
||||
/// \arg pml4 The pml4 to map into - null for the current one
|
||||
/// \returns A pointer to the start of the mapped region
|
||||
void * map_pages(addr_t address, size_t count);
|
||||
void * map_pages(addr_t address, size_t count, bool user = false, page_table *pml4 = nullptr);
|
||||
|
||||
/// Allocate and map contiguous pages into virtual memory, with
|
||||
/// a constant offset from their physical address.
|
||||
@@ -70,8 +103,9 @@ public:
|
||||
/// Log the current free/used block lists.
|
||||
void dump_blocks();
|
||||
|
||||
/// Dump the current PML4 to the console
|
||||
void dump_pml4();
|
||||
/// Dump the given or current PML4 to the console
|
||||
/// \arg pml4 The page table to use, null for the current one
|
||||
void dump_pml4(page_table *pml4 = nullptr);
|
||||
|
||||
/// Get the system page manager.
|
||||
/// \returns A pointer to the system page manager
|
||||
@@ -108,23 +142,6 @@ private:
|
||||
/// to the cache.
|
||||
void consolidate_blocks();
|
||||
|
||||
/// Helper to read the PML4 table from CR3.
|
||||
/// \returns A pointer to the current PML4 table.
|
||||
static inline page_table * get_pml4()
|
||||
{
|
||||
addr_t pml4 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (pml4) );
|
||||
return reinterpret_cast<page_table *>((pml4 & ~0xfffull) + page_offset);
|
||||
}
|
||||
|
||||
/// Helper to set the PML4 table pointer in CR3.
|
||||
/// \arg pml4 A pointer to the PML4 table to install.
|
||||
static inline void set_pml4(page_table *pml4)
|
||||
{
|
||||
addr_t p = reinterpret_cast<addr_t>(pml4) - page_offset;
|
||||
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p & ~0xfffull) );
|
||||
}
|
||||
|
||||
/// Helper function to allocate a new page table. If table entry `i` in
|
||||
/// table `base` is empty, allocate a new page table and point `base[i]` at
|
||||
/// it.
|
||||
@@ -163,6 +180,8 @@ private:
|
||||
/// \returns The number of pages retrieved
|
||||
size_t pop_pages(size_t count, addr_t *address);
|
||||
|
||||
page_table *m_kernel_pml4; ///< The PML4 of just kernel pages
|
||||
|
||||
page_block *m_free; ///< Free pages list
|
||||
page_block *m_used; ///< In-use pages list
|
||||
|
||||
|
||||
@@ -4,18 +4,13 @@
|
||||
#include "gdt.h"
|
||||
#include "interrupts.h"
|
||||
#include "log.h"
|
||||
#include "page_manager.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
scheduler scheduler::s_instance(nullptr);
|
||||
static const uint32_t quantum = 5000000;
|
||||
static const uint32_t quantum = 2000000;
|
||||
|
||||
const int stack_size = 0x1000;
|
||||
char taskAstack0[stack_size];
|
||||
char taskAstack3[stack_size];
|
||||
char taskBstack0[stack_size];
|
||||
char taskBstack3[stack_size];
|
||||
|
||||
uint64_t taskAcount = 0;
|
||||
|
||||
extern "C" void taskA();
|
||||
extern "C" void taskB();
|
||||
@@ -29,19 +24,18 @@ scheduler::scheduler(lapic *apic) :
|
||||
}
|
||||
|
||||
static process
|
||||
create_process(uint16_t pid, void *stack0, void *stack3, void (*rip)())
|
||||
create_process(uint16_t pid, void (*rip)())
|
||||
{
|
||||
uint64_t flags;
|
||||
__asm__ __volatile__ ( "pushf; pop %0" : "=r" (flags) );
|
||||
|
||||
// This is a hack for now, until we get a lot more set up.
|
||||
// I just want to see task switching working inside ring0 first
|
||||
uint16_t kcs = (1 << 3) | 0;
|
||||
uint16_t cs = (5 << 3) | 3;
|
||||
uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0
|
||||
uint16_t cs = (5 << 3) | 3; // User CS is GDT entry 5, ring 3
|
||||
|
||||
uint16_t kss = (2 << 3) | 0;
|
||||
uint16_t ss = (4 << 3) | 3;
|
||||
uint16_t kss = (2 << 3) | 0; // Kernel SS is GDT entry 2, ring 0
|
||||
uint16_t ss = (4 << 3) | 3; // User SS is GDT entry 4, ring 3
|
||||
|
||||
void *stack0 = kutil::malloc(stack_size);
|
||||
void *sp0 = kutil::offset_pointer(stack0, stack_size);
|
||||
cpu_state *state = reinterpret_cast<cpu_state *>(sp0) - 1;
|
||||
kutil::memset(state, 0, sizeof(cpu_state));
|
||||
@@ -51,14 +45,15 @@ create_process(uint16_t pid, void *stack0, void *stack3, void (*rip)())
|
||||
state->rflags = 0x202; // testing. TODO: 0x202
|
||||
state->rip = reinterpret_cast<uint64_t>(rip);
|
||||
|
||||
void *sp3 = kutil::offset_pointer(stack3, stack_size);
|
||||
state->user_rsp = reinterpret_cast<uint64_t>(sp3);
|
||||
page_table *pml4 = page_manager::get()->create_process_map();
|
||||
state->user_rsp = page_manager::initial_stack;
|
||||
|
||||
log::debug(logs::task, "Creating PID %d:", pid);
|
||||
log::debug(logs::task, " RSP0 %016lx", state);
|
||||
log::debug(logs::task, " RSP3 %016lx", sp3);
|
||||
log::debug(logs::task, " RSP3 %016lx", state->user_rsp);
|
||||
log::debug(logs::task, " PML4 %016lx", pml4);
|
||||
|
||||
return {pid, reinterpret_cast<addr_t>(state)};
|
||||
return {pid, reinterpret_cast<addr_t>(state), pml4};
|
||||
}
|
||||
|
||||
void
|
||||
@@ -68,9 +63,9 @@ scheduler::start()
|
||||
|
||||
m_apic->enable_timer(isr::isrTimer, 128, quantum, false);
|
||||
|
||||
m_processes.append({0, 0}); // The kernel idle task
|
||||
m_processes.append(create_process(1, &taskAstack0[0], &taskAstack3[0], &taskA));
|
||||
m_processes.append(create_process(2, &taskBstack0[0], &taskBstack3[0], &taskB));
|
||||
m_processes.append({0, 0, page_manager::get_pml4()}); // The kernel idle task, also the thread we're in now
|
||||
m_processes.append(create_process(1, &taskA));
|
||||
m_processes.append(create_process(2, &taskB));
|
||||
}
|
||||
|
||||
addr_t
|
||||
@@ -85,6 +80,10 @@ scheduler::tick(addr_t rsp0)
|
||||
// Set rsp0 to after the end of the about-to-be-popped cpu state
|
||||
tss_set_stack(0, rsp0 + sizeof(cpu_state));
|
||||
|
||||
// Swap page tables
|
||||
page_table *pml4 = m_processes[m_current].pml4;
|
||||
page_manager::set_pml4(pml4);
|
||||
|
||||
m_apic->reset_timer(quantum);
|
||||
return rsp0;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
#include "kutil/vector.h"
|
||||
|
||||
class lapic;
|
||||
struct page_table;
|
||||
|
||||
|
||||
struct process
|
||||
{
|
||||
uint16_t pid;
|
||||
addr_t rsp;
|
||||
page_table *pml4;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user