Merge branch 'address-spaces'

This commit is contained in:
apio 2022-10-14 18:20:42 +02:00
commit e11280ad3f
18 changed files with 379 additions and 60 deletions

View File

@ -1,8 +1,11 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
int main() int main()
{ {
sleep(6);
FILE* syms = fopen("/sys/moon.sym", "r"); FILE* syms = fopen("/sys/moon.sym", "r");
if (!syms) if (!syms)
{ {

View File

@ -0,0 +1,27 @@
#pragma once
#include "memory/Paging.h"
struct AddressSpace
{
static AddressSpace create();
void destroy();
void detach();
AddressSpace clone();
PageTable* get_pml4()
{
return m_pml4;
}
bool is_cloned()
{
return m_cloned;
}
private:
PageTable* m_pml4;
bool m_cloned;
};

View File

@ -29,4 +29,6 @@ namespace MemoryManager
void release_page(void* page); void release_page(void* page);
void release_pages(void* pages, uint64_t count); void release_pages(void* pages, uint64_t count);
void protect(void* page, uint64_t count, int flags);
} }

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "memory/AddressSpace.h"
#include "memory/Paging.h" #include "memory/Paging.h"
enum Flags enum Flags
@ -11,6 +12,17 @@ namespace VMM
{ {
void init(); // Fetch page table from cr3 void init(); // Fetch page table from cr3
void switch_to_user_address_space(AddressSpace& space);
void switch_to_previous_user_address_space();
void switch_back_to_kernel_address_space();
void enter_syscall_context();
void exit_syscall_context();
void apply_address_space();
bool is_using_kernel_address_space();
void map(uint64_t vaddr, uint64_t paddr, int flags); void map(uint64_t vaddr, uint64_t paddr, int flags);
void remap(uint64_t vaddr, int flags); void remap(uint64_t vaddr, int flags);
void unmap(uint64_t vaddr); void unmap(uint64_t vaddr);
@ -27,4 +39,7 @@ namespace VMM
void decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index, void decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
uint64_t& pdp_index); uint64_t& pdp_index);
uint64_t recompose_vaddr(uint64_t page_index, uint64_t pt_index, uint64_t pd_index, uint64_t pdp_index);
void install_kernel_page_directory_into_address_space(AddressSpace& space);
}; };

View File

@ -20,6 +20,8 @@
namespace Syscall namespace Syscall
{ {
void entry(Context* context); void entry(Context* context);
char* strdup_from_user(const char* user_string);
} }
void sys_exit(Context* context, int status); void sys_exit(Context* context, int status);

View File

@ -10,7 +10,8 @@ namespace Scheduler
void exit(int status); void exit(int status);
void sleep(unsigned long ms); void sleep(unsigned long ms);
void add_kernel_task(void (*task)(void)); void add_kernel_task(void (*task)(void));
void add_user_task(void* task);
Task* create_user_task();
void load_user_task(const char* filename); void load_user_task(const char* filename);

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "fs/FileDescriptor.h" #include "fs/FileDescriptor.h"
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "memory/AddressSpace.h"
#include "sys/elf/Image.h" #include "sys/elf/Image.h"
#define TASK_MAX_FDS 8 #define TASK_MAX_FDS 8
@ -43,6 +44,8 @@ struct Task
ELFImage* image = nullptr; ELFImage* image = nullptr;
Descriptor files[TASK_MAX_FDS]; Descriptor files[TASK_MAX_FDS];
AddressSpace address_space;
}; };
void set_context_from_task(Task& task, Context* ctx); void set_context_from_task(Task& task, Context* ctx);

View File

@ -51,6 +51,8 @@ extern "C" void common_handler(Context* context)
StackTracer tracer(context->rbp); StackTracer tracer(context->rbp);
tracer.trace_with_ip(context->rip); tracer.trace_with_ip(context->rip);
hang(); // FIXME: Remove this when multiple address spaces are working.
Scheduler::task_misbehave(context, -3); Scheduler::task_misbehave(context, -3);
} }
} }

View File

@ -16,6 +16,7 @@
#include "io/PIC.h" #include "io/PIC.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/AddressSpace.h"
#include "memory/Memory.h" #include "memory/Memory.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/MemoryMap.h" #include "memory/MemoryMap.h"
@ -56,12 +57,6 @@ extern "C" void _start()
kinfoln("Loaded IDT"); kinfoln("Loaded IDT");
PIC::remap();
PIC::enable_master(0b11111100); // enable keyboard and PIT
PIC::enable_slave(0b11111111);
kinfoln("Prepared PIC");
PIT::initialize(1000); // 1000 times per second PIT::initialize(1000); // 1000 times per second
kinfoln("Prepared PIT"); kinfoln("Prepared PIT");
@ -79,6 +74,7 @@ extern "C" void _start()
}); });
Scheduler::load_user_task("/bin/init"); Scheduler::load_user_task("/bin/init");
Scheduler::load_user_task("/bin/sym");
kinfoln("Prepared scheduler tasks"); kinfoln("Prepared scheduler tasks");
@ -87,7 +83,9 @@ extern "C" void _start()
Init::finish_kernel_boot(); Init::finish_kernel_boot();
Interrupts::enable(); // Task switching commences here PIC::remap();
PIC::enable_master(0b11111100); // enable keyboard and PIT
PIC::enable_slave(0b11111111);
kinfoln("Interrupts enabled"); kinfoln("Interrupts enabled");

View File

@ -0,0 +1,93 @@
#define MODULE "vmm"
#include "memory/AddressSpace.h"
#include "log/Log.h"
#include "memory/PMM.h"
#include "memory/VMM.h"
AddressSpace AddressSpace::create()
{
AddressSpace result;
result.m_pml4 = (PageTable*)PMM::request_page();
VMM::install_kernel_page_directory_into_address_space(result);
return result;
}
void AddressSpace::destroy()
{
if (m_cloned)
{
kdbgln("Will not destroy a cloned address space, I don't own it");
return;
}
uint64_t pages_freed = 0;
for (int i = 0; i < 512; i++)
{
PageDirectoryEntry& pdp_pde = m_pml4->entries[i];
if (!pdp_pde.present) continue;
if (pdp_pde.larger_pages)
{
pages_freed++;
PMM::free_page((void*)pdp_pde.get_address());
continue;
}
PageTable* pdp = (PageTable*)pdp_pde.get_address();
for (int j = 0; j < 511; j++) // skip the last page directory, it's the kernel one
{
PageDirectoryEntry& pd_pde = pdp->entries[j];
if (!pd_pde.present) continue;
if (pd_pde.larger_pages)
{
pages_freed++;
PMM::free_page((void*)pd_pde.get_address());
continue;
}
PageTable* pd = (PageTable*)pd_pde.get_address();
for (int k = 0; k < 512; k++)
{
PageDirectoryEntry& pt_pde = pd->entries[k];
if (!pt_pde.present) continue;
if (pt_pde.larger_pages)
{
pages_freed++;
PMM::free_page((void*)pt_pde.get_address());
continue;
}
PageTable* pt = (PageTable*)pt_pde.get_address();
for (int l = 0; l < 512; l++)
{
PageDirectoryEntry& pde = pt->entries[l];
if (!pde.present) continue;
pages_freed++;
PMM::free_page((void*)pde.get_address());
}
pages_freed++;
PMM::free_page(pt);
}
pages_freed++;
PMM::free_page(pd);
}
pages_freed++;
PMM::free_page(pdp);
}
pages_freed++;
PMM::free_page(m_pml4);
kdbgln("Reclaimed %ld pages from address space!", pages_freed);
}
void AddressSpace::detach()
{
if (!m_cloned) return;
m_pml4 = (PageTable*)PMM::request_page();
VMM::install_kernel_page_directory_into_address_space(*this);
m_cloned = false;
}
AddressSpace AddressSpace::clone()
{
AddressSpace result;
result.m_pml4 = m_pml4;
result.m_cloned = true;
return result;
}

View File

@ -190,3 +190,8 @@ void MemoryManager::release_pages(void* pages, uint64_t count)
} }
KernelHeap::free_virtual_pages((uint64_t)pages, count); KernelHeap::free_virtual_pages((uint64_t)pages, count);
} }
void MemoryManager::protect(void* page, uint64_t count, int flags)
{
for (uint64_t i = 0; i < count; i++) { VMM::remap((uint64_t)page + (i * PAGE_SIZE), flags); }
}

View File

@ -9,29 +9,88 @@
#include "utils/Addresses.h" #include "utils/Addresses.h"
#include "utils/Registers.h" #include "utils/Registers.h"
// FIXME: There is a lot of duplicate code in this file. This should probably be refactored. static PageTable* kernel_pml4;
static PageTable* current_pml4;
static AddressSpace* user_address_space;
static PageTable* PML4; void VMM::switch_back_to_kernel_address_space()
{
if (current_pml4 != kernel_pml4) { current_pml4 = kernel_pml4; }
}
void VMM::switch_to_user_address_space(AddressSpace& space)
{
user_address_space = &space;
current_pml4 = user_address_space->get_pml4();
}
void VMM::switch_to_previous_user_address_space()
{
current_pml4 = user_address_space->get_pml4();
}
void VMM::enter_syscall_context()
{
if (current_pml4 != kernel_pml4)
{
current_pml4 = kernel_pml4;
apply_address_space();
switch_to_previous_user_address_space();
}
}
void VMM::exit_syscall_context()
{
if (current_pml4 != user_address_space->get_pml4()) { switch_to_previous_user_address_space(); }
apply_address_space();
}
void VMM::apply_address_space()
{
write_cr3(current_pml4);
}
bool VMM::is_using_kernel_address_space()
{
return current_pml4 == kernel_pml4;
}
void VMM::init() void VMM::init()
{ {
PML4 = (PageTable*)read_cr3(); kernel_pml4 = (PageTable*)read_cr3();
current_pml4 = kernel_pml4;
} }
void VMM::unmap(uint64_t vaddr) void VMM::unmap(uint64_t vaddr)
{ {
vaddr = round_down_to_nearest_page(vaddr); vaddr = round_down_to_nearest_page(vaddr);
PageDirectoryEntry* pde = find_pde(PML4, vaddr); PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
if (!pde) return; // Already unmapped if (!pde) return; // Already unmapped
memset(pde, 0, sizeof(PageDirectoryEntry)); memset(pde, 0, sizeof(PageDirectoryEntry));
flush_tlb(vaddr); flush_tlb(vaddr);
} }
void VMM::remap(uint64_t vaddr, int flags)
{
vaddr = round_down_to_nearest_page(vaddr);
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
if (!pde) return; // Not mapped
if (flags & User) propagate_user(current_pml4, vaddr);
else
pde->user = false;
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
else
pde->read_write = false;
flush_tlb(vaddr);
}
uint64_t VMM::get_physical(uint64_t vaddr) uint64_t VMM::get_physical(uint64_t vaddr)
{ {
PageDirectoryEntry* pde = find_pde(PML4, round_down_to_nearest_page(vaddr)); PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(vaddr));
if (!pde) return UINT64_MAX; // Not mapped if (!pde) return UINT64_MAX; // Not mapped
return pde->get_address() | (vaddr % PAGE_SIZE); return pde->get_address() | (vaddr % PAGE_SIZE);
@ -39,7 +98,7 @@ uint64_t VMM::get_physical(uint64_t vaddr)
uint64_t VMM::get_flags(uint64_t vaddr) uint64_t VMM::get_flags(uint64_t vaddr)
{ {
PageDirectoryEntry* pde = find_pde(PML4, round_down_to_nearest_page(vaddr)); PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(vaddr));
if (!pde) return 0; // Not mapped if (!pde) return 0; // Not mapped
uint64_t flags = 0; uint64_t flags = 0;
@ -51,35 +110,38 @@ uint64_t VMM::get_flags(uint64_t vaddr)
void VMM::map(uint64_t vaddr, uint64_t paddr, int flags) void VMM::map(uint64_t vaddr, uint64_t paddr, int flags)
{ {
vaddr = round_down_to_nearest_page(vaddr); vaddr = round_down_to_nearest_page(vaddr);
PageDirectoryEntry* pde = find_pde(PML4, vaddr); PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
bool will_flush_tlb = true; bool will_flush_tlb = true;
if (!pde) if (!pde)
{ {
pde = create_pde_if_not_exists(PML4, vaddr); pde = create_pde_if_not_exists(current_pml4, vaddr);
will_flush_tlb = false; will_flush_tlb = false;
} }
else if (pde->larger_pages) else if (pde->larger_pages)
{ {
unmap(vaddr); unmap(vaddr);
pde = create_pde_if_not_exists(PML4, vaddr); pde = create_pde_if_not_exists(current_pml4, vaddr);
will_flush_tlb = false;
} }
pde->set_address(round_down_to_nearest_page(paddr)); pde->set_address(round_down_to_nearest_page(paddr));
if (flags & User) propagate_user(PML4, vaddr); if (flags & User) propagate_user(current_pml4, vaddr);
if (flags & ReadWrite) propagate_read_write(PML4, vaddr); else
pde->user = false;
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
else
pde->read_write = false;
if (will_flush_tlb) flush_tlb(vaddr); if (will_flush_tlb) flush_tlb(vaddr);
} }
PageDirectoryEntry* VMM::find_pde(PageTable* root, uint64_t vaddr) PageDirectoryEntry* VMM::find_pde(PageTable* root, uint64_t vaddr)
{ {
uint64_t page_index, pt_index, pd_index, pdp_index; uint64_t page_index;
PageDirectoryEntry* pde; PageDirectoryEntry* pde;
PageTable* pt = root; PageTable* pt = root;
decompose_vaddr(vaddr, page_index, pt_index, pd_index, pdp_index); uint64_t indexes[3];
uint64_t indexes[3] = {pdp_index, pd_index, pt_index}; decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
for (int i = 0; i < 3; for (int i = 0; i < 3;
i++) // Walk through the page map level 4, page directory pointer, and page directory to find the page table. i++) // Walk through the page map level 4, page directory pointer, and page directory to find the page table.
@ -98,11 +160,13 @@ PageDirectoryEntry* VMM::find_pde(PageTable* root, uint64_t vaddr)
PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t vaddr) PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t vaddr)
{ {
uint64_t page_index, pt_index, pd_index, pdp_index; uint64_t page_index;
PageDirectoryEntry* pde; PageDirectoryEntry* pde;
PageTable* pt = root; PageTable* pt = root;
decompose_vaddr(vaddr, page_index, pt_index, pd_index, pdp_index); uint64_t indexes[3];
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
auto pde_create_if_not_present = [&]() { auto pde_create_if_not_present = [&]() {
pt = (PageTable*)PMM::request_page(); pt = (PageTable*)PMM::request_page();
@ -112,8 +176,6 @@ PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t vadd
pde->present = true; pde->present = true;
}; };
uint64_t indexes[3] = {pdp_index, pd_index, pt_index};
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
pde = &pt->entries[indexes[i]]; pde = &pt->entries[indexes[i]];
@ -130,13 +192,13 @@ PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t vadd
void VMM::propagate_read_write(PageTable* root, uint64_t vaddr) void VMM::propagate_read_write(PageTable* root, uint64_t vaddr)
{ {
uint64_t page_index, pt_index, pd_index, pdp_index; uint64_t page_index;
PageDirectoryEntry* pde; PageDirectoryEntry* pde;
PageTable* pt = root; PageTable* pt = root;
decompose_vaddr(vaddr, page_index, pt_index, pd_index, pdp_index); uint64_t indexes[3];
uint64_t indexes[3] = {pdp_index, pd_index, pt_index}; decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
@ -158,13 +220,13 @@ void VMM::propagate_read_write(PageTable* root, uint64_t vaddr)
void VMM::propagate_user(PageTable* root, uint64_t vaddr) void VMM::propagate_user(PageTable* root, uint64_t vaddr)
{ {
uint64_t page_index, pt_index, pd_index, pdp_index; uint64_t page_index;
PageDirectoryEntry* pde; PageDirectoryEntry* pde;
PageTable* pt = root; PageTable* pt = root;
decompose_vaddr(vaddr, page_index, pt_index, pd_index, pdp_index); uint64_t indexes[3];
uint64_t indexes[3] = {pdp_index, pd_index, pt_index}; decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
@ -201,3 +263,29 @@ void VMM::decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_ind
vaddr >>= 9; vaddr >>= 9;
pdp_index = vaddr & 0x1ff; pdp_index = vaddr & 0x1ff;
} }
uint64_t VMM::recompose_vaddr(uint64_t page_index, uint64_t pt_index, uint64_t pd_index, uint64_t pdp_index)
{
return pdp_index << 39 | pd_index << 30 | pt_index << 21 | page_index << 12;
}
void VMM::install_kernel_page_directory_into_address_space(AddressSpace& space)
{
PageTable* space_pml4 = space.get_pml4();
PageTable* kernel_last_pdp = (PageTable*)kernel_pml4->entries[511].get_address();
PageTable* kernel_last_pd = (PageTable*)kernel_last_pdp->entries[511].get_address();
PageTable* space_last_pdp = (PageTable*)PMM::request_page();
PageDirectoryEntry& space_last_pdp_pde = space_pml4->entries[511];
space_last_pdp_pde.present = true;
space_last_pdp_pde.read_write = true;
space_last_pdp_pde.set_address((uint64_t)space_last_pdp);
PageDirectoryEntry& space_last_pd_pde = space_last_pdp->entries[511];
space_last_pd_pde.present = true;
space_last_pd_pde.read_write = true;
space_last_pd_pde.set_address((uint64_t)kernel_last_pd);
}

View File

@ -8,6 +8,7 @@
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "io/IO.h" #include "io/IO.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/VMM.h"
#include "misc/hang.h" #include "misc/hang.h"
#include "std/string.h" #include "std/string.h"
@ -85,6 +86,11 @@ static void try_idt_triple_fault()
[[noreturn]] void reboot() [[noreturn]] void reboot()
{ {
Interrupts::disable(); Interrupts::disable();
if (!VMM::is_using_kernel_address_space())
{
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
}
kinfoln("Attempting reboot using ACPI"); kinfoln("Attempting reboot using ACPI");
try_acpi_reboot(); try_acpi_reboot();
kinfoln("Attempting reboot using keyboard RESET pulsing"); kinfoln("Attempting reboot using keyboard RESET pulsing");

View File

@ -1,11 +1,14 @@
#include "sys/Syscall.h" #include "sys/Syscall.h"
#include "errno.h" #include "errno.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "memory/VMM.h"
#include "std/string.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
void Syscall::entry(Context* context) void Syscall::entry(Context* context)
{ {
asm volatile("cli"); asm volatile("cli");
VMM::enter_syscall_context();
switch (context->rax) switch (context->rax)
{ {
case SYS_exit: sys_exit(context, (int)context->rdi); break; case SYS_exit: sys_exit(context, (int)context->rdi); break;
@ -24,4 +27,12 @@ void Syscall::entry(Context* context)
case SYS_exec: sys_exec(context, (const char*)context->rdi); break; case SYS_exec: sys_exec(context, (const char*)context->rdi); break;
default: context->rax = -ENOSYS; break; default: context->rax = -ENOSYS; break;
} }
VMM::exit_syscall_context();
}
char* Syscall::strdup_from_user(const char* user_string)
{
uint64_t phys = VMM::get_physical((uint64_t)user_string);
if (phys == (uint64_t)-1) { return nullptr; }
return strdup((const char*)phys);
} }

View File

@ -8,6 +8,7 @@
#include "log/Log.h" #include "log/Log.h"
#include "memory/Memory.h" #include "memory/Memory.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/VMM.h"
#include "misc/utils.h" #include "misc/utils.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
@ -67,7 +68,9 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
{ {
Elf64_Ehdr elf_ehdr; Elf64_Ehdr elf_ehdr;
ASSERT(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0); ASSERT(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0);
ASSERT(strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) == 0); ASSERT(strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) ==
0); // If you haven't checked the ELF executable with check_elf_image() first, then an assertion fail is your
// fault =D
ASSERT(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64); ASSERT(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64);
ASSERT(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB); ASSERT(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
ASSERT(elf_ehdr.e_type == ET_EXEC); ASSERT(elf_ehdr.e_type == ET_EXEC);
@ -86,13 +89,24 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
kdbgln("Loading loadable segment at address %lx, file size %ld, mem size %ld, permissions %s", phdr.p_vaddr, kdbgln("Loading loadable segment at address %lx, file size %ld, mem size %ld, permissions %s", phdr.p_vaddr,
phdr.p_filesz, phdr.p_memsz, format_permissions(phdr.p_flags)); phdr.p_filesz, phdr.p_memsz, format_permissions(phdr.p_flags));
ASSERT(phdr.p_vaddr); ASSERT(phdr.p_vaddr);
uint64_t pages = Utilities::get_blocks_from_size(PAGE_SIZE, (phdr.p_vaddr % PAGE_SIZE) + phdr.p_memsz); uint64_t pages = Utilities::get_blocks_from_size(PAGE_SIZE, (phdr.p_vaddr % PAGE_SIZE) + phdr.p_memsz);
void* buffer = void* buffer = (void*)((uint64_t)MemoryManager::get_pages_at(round_down_to_nearest_page(phdr.p_vaddr),
(void*)((uint64_t)MemoryManager::get_pages_at(round_down_to_nearest_page(phdr.p_vaddr), pages, pages, MAP_READ_WRITE) +
phdr.p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER) +
(phdr.p_vaddr % PAGE_SIZE)); (phdr.p_vaddr % PAGE_SIZE));
if (VMM::is_using_kernel_address_space()) { VMM::switch_to_previous_user_address_space(); }
VMM::apply_address_space();
VFS::read(node, phdr.p_offset, phdr.p_filesz, (char*)buffer); VFS::read(node, phdr.p_offset, phdr.p_filesz, (char*)buffer);
memset((void*)((uint64_t)buffer + phdr.p_filesz), 0, phdr.p_memsz - phdr.p_filesz); memset((void*)((uint64_t)buffer + phdr.p_filesz), 0, phdr.p_memsz - phdr.p_filesz);
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
VMM::switch_to_previous_user_address_space();
MemoryManager::protect(buffer, pages, phdr.p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER);
image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) + image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) +
(image->section_count + 1) * sizeof(ELFSection)); (image->section_count + 1) * sizeof(ELFSection));
ELFSection& section = image->sections[image->section_count]; ELFSection& section = image->sections[image->section_count];

View File

@ -5,30 +5,38 @@
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "memory/VMM.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
#include "sys/Syscall.h"
#include "sys/elf/ELFLoader.h" #include "sys/elf/ELFLoader.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
void sys_exec(Context* context, const char* pathname) void sys_exec(Context* context, const char* pathname)
{ {
if (!pathname) /*context->rax = -ENOSYS; // FIXME: Make exec() work under separate address spaces.
return;*/
char* kpathname = Syscall::strdup_from_user(pathname);
if (!kpathname)
{ {
context->rax = -EFAULT; context->rax = -EFAULT;
return; return;
} }
kinfoln("exec(): executing %s", pathname); kinfoln("exec(): executing %s", kpathname);
VFS::Node* program = VFS::resolve_path(pathname); VFS::Node* program = VFS::resolve_path(kpathname);
if (!program) if (!program)
{ {
kfree(kpathname);
context->rax = -ENOENT; context->rax = -ENOENT;
return; return;
} }
if (program->type == VFS_DIRECTORY) if (program->type == VFS_DIRECTORY)
{ {
kfree(kpathname);
context->rax = -EISDIR; context->rax = -EISDIR;
return; return;
} }
@ -36,6 +44,7 @@ void sys_exec(Context* context, const char* pathname)
long memusage; long memusage;
if ((memusage = ELFLoader::check_elf_image(program)) < 0) if ((memusage = ELFLoader::check_elf_image(program)) < 0)
{ {
kfree(kpathname);
context->rax = -ENOEXEC; context->rax = -ENOEXEC;
return; return;
} }
@ -43,12 +52,16 @@ void sys_exec(Context* context, const char* pathname)
uint64_t allocated_stack = (uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); uint64_t allocated_stack = (uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER);
if (!allocated_stack) if (!allocated_stack)
{ {
kfree(kpathname);
context->rax = -ENOMEM; context->rax = -ENOMEM;
return; return;
} }
uint64_t allocated_stack_phys = VMM::get_physical(allocated_stack);
if ((uint64_t)memusage > PMM::get_free()) if ((uint64_t)memusage > PMM::get_free())
{ {
kfree(kpathname);
MemoryManager::release_pages((void*)allocated_stack, TASK_PAGES_IN_STACK); MemoryManager::release_pages((void*)allocated_stack, TASK_PAGES_IN_STACK);
context->rax = -ENOMEM; context->rax = -ENOMEM;
return; return;
@ -62,19 +75,22 @@ void sys_exec(Context* context, const char* pathname)
// At this point, pretty much nothing can fail. // At this point, pretty much nothing can fail.
ELFLoader::release_elf_image(Scheduler::current_task()->image);
ELFImage* image = ELFLoader::load_elf_from_vfs(program); ELFImage* image = ELFLoader::load_elf_from_vfs(program);
ASSERT(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly ASSERT(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly
// wrong. // wrong.
MemoryManager::release_pages((void*)task->allocated_stack, TASK_PAGES_IN_STACK);
task->allocated_stack = allocated_stack; task->allocated_stack = allocated_stack;
for (uint64_t i = 0; i < TASK_PAGES_IN_STACK; i++)
{
VMM::map(allocated_stack + (i * PAGE_SIZE), allocated_stack_phys + (i * PAGE_SIZE), MAP_READ_WRITE | MAP_USER);
}
Scheduler::reset_task(task, image); Scheduler::reset_task(task, image);
set_context_from_task(*task, context); set_context_from_task(*task, context);
kfree(kpathname);
return; return;
} }

View File

@ -4,7 +4,10 @@
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/VMM.h"
#include "render/TextRenderer.h" #include "render/TextRenderer.h"
#include "std/stdlib.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include "thread/Task.h" #include "thread/Task.h"
@ -79,7 +82,7 @@ void sys_write(Context* context, int fd, size_t size, const char* addr)
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
ssize_t result = current_task->files[fd].write(size, addr); ssize_t result = current_task->files[fd].write(size, (const char*)VMM::get_physical((uint64_t)addr));
context->rax = (size_t)result; context->rax = (size_t)result;
return; return;
} }
@ -99,9 +102,17 @@ void sys_open(Context* context, const char* filename, int flags)
return; return;
} }
VFS::Node* node = VFS::resolve_path(filename); char* kernel_filename = Syscall::strdup_from_user(filename);
if (!kernel_filename)
{
context->rax = -EFAULT;
return;
}
VFS::Node* node = VFS::resolve_path(kernel_filename);
if (!node) if (!node)
{ {
kfree(kernel_filename);
context->rax = -ENOENT; context->rax = -ENOENT;
return; return;
} }
@ -110,16 +121,18 @@ void sys_open(Context* context, const char* filename, int flags)
bool can_write = (flags & OPEN_WRITE) > 0; bool can_write = (flags & OPEN_WRITE) > 0;
if (!can_read && !can_write) if (!can_read && !can_write)
{ {
kfree(kernel_filename);
context->rax = -EINVAL; context->rax = -EINVAL;
return; return;
} }
kdbgln("open(): opening %s %s, allocated file descriptor %d", filename, kdbgln("open(): opening %s %s, allocated file descriptor %d", kernel_filename,
(can_read && can_write) ? "rw" (can_read && can_write) ? "rw"
: can_read ? "r-" : can_read ? "r-"
: "-w", : "-w",
fd); fd);
kfree(kernel_filename);
current_task->files[fd].open(node, can_read, can_write); current_task->files[fd].open(node, can_read, can_write);
context->rax = fd; context->rax = fd;
return; return;
@ -143,7 +156,7 @@ void sys_read(Context* context, int fd, size_t size, char* buffer)
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
ssize_t result = current_task->files[fd].read(size, buffer); ssize_t result = current_task->files[fd].read(size, (char*)VMM::get_physical((uint64_t)buffer));
context->rax = (size_t)result; context->rax = (size_t)result;
return; return;
} }

View File

@ -87,21 +87,16 @@ void Scheduler::add_kernel_task(void (*task)(void))
new_task->id, new_task->regs.rsp, task_num); new_task->id, new_task->regs.rsp, task_num);
} }
void Scheduler::add_user_task(void* task) Task* Scheduler::create_user_task()
{ {
Task* new_task = new Task; Task* new_task = new Task;
ASSERT(new_task); ASSERT(new_task);
memset(&new_task->regs, 0, sizeof(Context)); memset(&new_task->regs, 0, sizeof(Context));
new_task->user_task = true; new_task->user_task = true;
new_task->id = free_tid++; new_task->id = free_tid++;
new_task->regs.rip = (uint64_t)task;
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages( new_task->allocated_stack = (uint64_t)MemoryManager::get_pages(
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right? TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK); new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
new_task->regs.cs = 0x18 | 0x03;
new_task->regs.ss = 0x20 | 0x03;
new_task->regs.ds = 0x20 | 0x03;
new_task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
new_task->task_sleep = 0; new_task->task_sleep = 0;
new_task->task_time = 0; new_task->task_time = 0;
new_task->cpu_time = 0; new_task->cpu_time = 0;
@ -110,10 +105,7 @@ void Scheduler::add_user_task(void* task)
base_task->prev_task = new_task; base_task->prev_task = new_task;
new_task->next_task = base_task; new_task->next_task = base_task;
end_task = new_task; end_task = new_task;
new_task->state = new_task->Running;
task_num++; task_num++;
kinfoln("Adding user task: starts at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip,
new_task->id, new_task->regs.rsp, task_num);
} }
void Scheduler::load_user_task(const char* filename) void Scheduler::load_user_task(const char* filename)
@ -130,6 +122,8 @@ void Scheduler::load_user_task(const char* filename)
ASSERT(new_task); ASSERT(new_task);
memset(&new_task->regs, 0, sizeof(Context)); memset(&new_task->regs, 0, sizeof(Context));
new_task->id = free_tid++; new_task->id = free_tid++;
new_task->address_space = AddressSpace::create();
VMM::switch_to_user_address_space(new_task->address_space);
ELFImage* image = ELFLoader::load_elf_from_filesystem( ELFImage* image = ELFLoader::load_elf_from_filesystem(
filename); // FIXME: TOCTOU? Right now, impossible, since interrupts are disabled and SMP is not a thing. But in filename); // FIXME: TOCTOU? Right now, impossible, since interrupts are disabled and SMP is not a thing. But in
// the future, it might be possible. // the future, it might be possible.
@ -156,6 +150,7 @@ void Scheduler::load_user_task(const char* filename)
task_num++; task_num++;
kinfoln("Adding user task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip, kinfoln("Adding user task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip,
new_task->id, new_task->regs.rsp, task_num); new_task->id, new_task->regs.rsp, task_num);
VMM::switch_back_to_kernel_address_space();
Interrupts::pop(); Interrupts::pop();
} }
@ -182,12 +177,27 @@ void Scheduler::reap_task(Task* task)
task_num--; task_num--;
Task* exiting_task = task; Task* exiting_task = task;
ASSERT(task->id != 0); // WHY IN THE WORLD WOULD WE BE REAPING THE IDLE TASK? ASSERT(task->id != 0); // WHY IN THE WORLD WOULD WE BE REAPING THE IDLE TASK?
if (exiting_task->is_user_task())
{
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
VMM::switch_to_user_address_space(exiting_task->address_space);
}
kinfoln("reaping task %ld, exited with code %ld", exiting_task->id, exiting_task->exit_status); kinfoln("reaping task %ld, exited with code %ld", exiting_task->id, exiting_task->exit_status);
if (exiting_task->allocated_stack) if (exiting_task->allocated_stack)
MemoryManager::release_pages((void*)exiting_task->allocated_stack, TASK_PAGES_IN_STACK); MemoryManager::release_pages((void*)exiting_task->allocated_stack, TASK_PAGES_IN_STACK);
if (exiting_task->image) // FIXME: Also free pages the task has mmap-ed but not munmap-ed. if (exiting_task->image) // FIXME: Also free pages the task has mmap-ed but not munmap-ed.
{ {
ELFLoader::release_elf_image(exiting_task->image); // ELFLoader::release_elf_image(exiting_task->image);
kfree(exiting_task->image);
}
if (exiting_task->is_user_task())
{
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
Interrupts::push_and_enable();
exiting_task->address_space.destroy();
Interrupts::pop();
} }
for (int i = 0; i < TASK_MAX_FDS; i++) { exiting_task->files[i].close(); } for (int i = 0; i < TASK_MAX_FDS; i++) { exiting_task->files[i].close(); }
delete exiting_task; delete exiting_task;
@ -316,7 +326,17 @@ void Scheduler::task_yield(Context* context)
{ {
task_save_floating(*original_task); task_save_floating(*original_task);
} }
if (sched_current_task->is_user_task()) { task_restore_floating(*sched_current_task); } if (sched_current_task->is_user_task())
{
VMM::switch_to_user_address_space(sched_current_task->address_space);
VMM::apply_address_space();
task_restore_floating(*sched_current_task);
}
else if (!was_idle && original_task->is_user_task() && !sched_current_task->is_user_task())
{
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
}
} }
sched_current_task->task_time = 20; sched_current_task->task_time = 20;
set_context_from_task(*sched_current_task, context); set_context_from_task(*sched_current_task, context);