Kernel: Add basic fixed width allocator for userspace
We have to move process stacks to the general heap and maybe map kernel to higher half.
This commit is contained in:
parent
b65cd1d09b
commit
ff5bcd4416
|
@ -25,6 +25,7 @@ set(KERNEL_SOURCES
|
||||||
kernel/Input/PS2Keymap.cpp
|
kernel/Input/PS2Keymap.cpp
|
||||||
kernel/InterruptController.cpp
|
kernel/InterruptController.cpp
|
||||||
kernel/kernel.cpp
|
kernel/kernel.cpp
|
||||||
|
kernel/Memory/FixedWidthAllocator.cpp
|
||||||
kernel/Memory/Heap.cpp
|
kernel/Memory/Heap.cpp
|
||||||
kernel/Memory/kmalloc.cpp
|
kernel/Memory/kmalloc.cpp
|
||||||
kernel/Panic.cpp
|
kernel/Panic.cpp
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kernel/Memory/Heap.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
class Process;
|
||||||
|
|
||||||
|
class FixedWidthAllocator
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(FixedWidthAllocator);
|
||||||
|
|
||||||
|
public:
|
||||||
|
FixedWidthAllocator(Process*, uint32_t);
|
||||||
|
FixedWidthAllocator(FixedWidthAllocator&&);
|
||||||
|
~FixedWidthAllocator();
|
||||||
|
|
||||||
|
vaddr_t allocate();
|
||||||
|
void deallocate(vaddr_t);
|
||||||
|
|
||||||
|
uint32_t allocation_size() const { return m_allocation_size; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct node
|
||||||
|
{
|
||||||
|
node* prev { nullptr };
|
||||||
|
node* next { nullptr };
|
||||||
|
};
|
||||||
|
vaddr_t address_of(const node*) const;
|
||||||
|
void allocate_page_for_node_if_needed(const node*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr uint32_t m_min_allocation_size = 16;
|
||||||
|
|
||||||
|
Process* m_process;
|
||||||
|
const uint32_t m_allocation_size;
|
||||||
|
|
||||||
|
vaddr_t m_nodes_page { 0 };
|
||||||
|
vaddr_t m_allocated_pages { 0 };
|
||||||
|
|
||||||
|
node* m_free_list { nullptr };
|
||||||
|
node* m_used_list { nullptr };
|
||||||
|
|
||||||
|
uint32_t m_allocated { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/Vector.h>
|
||||||
#include <kernel/FS/Inode.h>
|
#include <kernel/FS/Inode.h>
|
||||||
|
#include <kernel/Memory/FixedWidthAllocator.h>
|
||||||
#include <kernel/Memory/Heap.h>
|
#include <kernel/Memory/Heap.h>
|
||||||
#include <kernel/Memory/MMU.h>
|
#include <kernel/Memory/MMU.h>
|
||||||
#include <kernel/SpinLock.h>
|
#include <kernel/SpinLock.h>
|
||||||
|
@ -55,6 +56,8 @@ namespace Kernel
|
||||||
BAN::ErrorOr<BAN::String> working_directory() const;
|
BAN::ErrorOr<BAN::String> working_directory() const;
|
||||||
BAN::ErrorOr<void> set_working_directory(BAN::StringView);
|
BAN::ErrorOr<void> set_working_directory(BAN::StringView);
|
||||||
|
|
||||||
|
BAN::ErrorOr<void*> allocate(size_t);
|
||||||
|
|
||||||
void termid(char*) const;
|
void termid(char*) const;
|
||||||
|
|
||||||
static Process& current() { return Thread::current().process(); }
|
static Process& current() { return Thread::current().process(); }
|
||||||
|
@ -90,6 +93,8 @@ namespace Kernel
|
||||||
BAN::String m_working_directory;
|
BAN::String m_working_directory;
|
||||||
BAN::Vector<Thread*> m_threads;
|
BAN::Vector<Thread*> m_threads;
|
||||||
|
|
||||||
|
BAN::Vector<FixedWidthAllocator> m_fixed_width_allocators;
|
||||||
|
|
||||||
MMU* m_mmu { nullptr };
|
MMU* m_mmu { nullptr };
|
||||||
TTY* m_tty { nullptr };
|
TTY* m_tty { nullptr };
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#define SYS_CLOSE 5
|
#define SYS_CLOSE 5
|
||||||
#define SYS_SEEK 6
|
#define SYS_SEEK 6
|
||||||
#define SYS_OPEN 7
|
#define SYS_OPEN 7
|
||||||
|
#define SYS_ALLOC 8
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include <kernel/CriticalScope.h>
|
||||||
|
#include <kernel/Memory/FixedWidthAllocator.h>
|
||||||
|
#include <kernel/Memory/MMU.h>
|
||||||
|
#include <kernel/Process.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
FixedWidthAllocator::FixedWidthAllocator(Process* process, uint32_t allocation_size)
|
||||||
|
: m_process(process)
|
||||||
|
, m_allocation_size(BAN::Math::max(allocation_size, m_min_allocation_size))
|
||||||
|
{
|
||||||
|
ASSERT(BAN::Math::is_power_of_two(allocation_size));
|
||||||
|
|
||||||
|
paddr_t nodes_paddr = Heap::get().take_free_page();
|
||||||
|
m_nodes_page = m_process->mmu().get_free_page();
|
||||||
|
m_process->mmu().map_page_at(nodes_paddr, m_nodes_page, MMU::Flags::ReadWrite | MMU::Flags::Present);
|
||||||
|
|
||||||
|
paddr_t allocated_pages_paddr = Heap::get().take_free_page();
|
||||||
|
m_allocated_pages = m_process->mmu().get_free_page();
|
||||||
|
m_process->mmu().map_page_at(allocated_pages_paddr, m_allocated_pages, MMU::Flags::ReadWrite | MMU::Flags::Present);
|
||||||
|
|
||||||
|
CriticalScope _;
|
||||||
|
|
||||||
|
m_process->mmu().load();
|
||||||
|
|
||||||
|
memset((void*)m_nodes_page, 0, PAGE_SIZE);
|
||||||
|
memset((void*)m_allocated_pages, 0, PAGE_SIZE);
|
||||||
|
|
||||||
|
node* node_table = (node*)m_nodes_page;
|
||||||
|
for (uint32_t i = 0; i < PAGE_SIZE / sizeof(node); i++)
|
||||||
|
{
|
||||||
|
node_table[i].next = &node_table[i + 1];
|
||||||
|
node_table[i].prev = &node_table[i - 1];
|
||||||
|
}
|
||||||
|
node_table[0].prev = nullptr;
|
||||||
|
node_table[PAGE_SIZE / sizeof(node) - 1].next = nullptr;
|
||||||
|
|
||||||
|
m_free_list = node_table;
|
||||||
|
m_used_list = nullptr;
|
||||||
|
|
||||||
|
Process::current().mmu().load();
|
||||||
|
}
|
||||||
|
|
||||||
|
FixedWidthAllocator::FixedWidthAllocator(FixedWidthAllocator&& other)
|
||||||
|
: m_process(other.m_process)
|
||||||
|
, m_allocation_size(other.m_allocation_size)
|
||||||
|
, m_nodes_page(other.m_nodes_page)
|
||||||
|
, m_allocated_pages(other.m_allocated_pages)
|
||||||
|
, m_free_list(other.m_free_list)
|
||||||
|
, m_used_list(other.m_used_list)
|
||||||
|
, m_allocated(other.m_allocated)
|
||||||
|
{
|
||||||
|
other.m_process = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FixedWidthAllocator::~FixedWidthAllocator()
|
||||||
|
{
|
||||||
|
if (m_process == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Heap::get().release_page(m_process->mmu().physical_address_of(m_nodes_page));
|
||||||
|
m_process->mmu().unmap_page(m_nodes_page);
|
||||||
|
|
||||||
|
for (uint32_t page_index = 0; page_index < PAGE_SIZE / sizeof(vaddr_t); page_index++)
|
||||||
|
{
|
||||||
|
vaddr_t page_vaddr = ((vaddr_t*)m_allocated_pages)[page_index];
|
||||||
|
if (page_vaddr == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ASSERT(!m_process->mmu().is_page_free(page_vaddr));
|
||||||
|
paddr_t page_paddr = m_process->mmu().physical_address_of(page_vaddr);
|
||||||
|
|
||||||
|
Heap::get().release_page(page_paddr);
|
||||||
|
m_process->mmu().unmap_page(page_vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Heap::get().release_page(m_process->mmu().physical_address_of(m_allocated_pages));
|
||||||
|
m_process->mmu().unmap_page(m_allocated_pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
paddr_t FixedWidthAllocator::allocate()
|
||||||
|
{
|
||||||
|
// FIXME: We should get allocate more memory if we run out of
|
||||||
|
// nodes in free list.
|
||||||
|
ASSERT(m_free_list);
|
||||||
|
|
||||||
|
node* node = m_free_list;
|
||||||
|
|
||||||
|
m_free_list = node->next;
|
||||||
|
if (m_free_list)
|
||||||
|
m_free_list->prev = nullptr;
|
||||||
|
|
||||||
|
node->next = m_used_list;
|
||||||
|
node->prev = nullptr;
|
||||||
|
|
||||||
|
if (m_used_list)
|
||||||
|
m_used_list->prev = node;
|
||||||
|
m_used_list = node;
|
||||||
|
|
||||||
|
m_allocated++;
|
||||||
|
allocate_page_for_node_if_needed(node);
|
||||||
|
return address_of(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedWidthAllocator::deallocate(paddr_t addr)
|
||||||
|
{
|
||||||
|
(void)addr;
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
vaddr_t FixedWidthAllocator::address_of(const node* node) const
|
||||||
|
{
|
||||||
|
uint32_t index = node - (struct node*)m_nodes_page;
|
||||||
|
|
||||||
|
uint32_t page_index = index / (PAGE_SIZE / sizeof(struct node));
|
||||||
|
ASSERT(page_index < PAGE_SIZE / sizeof(vaddr_t));
|
||||||
|
|
||||||
|
uint32_t offset = index % (PAGE_SIZE / sizeof(struct node));
|
||||||
|
|
||||||
|
vaddr_t page_begin = ((vaddr_t*)m_allocated_pages)[page_index];
|
||||||
|
ASSERT(page_begin);
|
||||||
|
|
||||||
|
return page_begin + offset * m_allocation_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedWidthAllocator::allocate_page_for_node_if_needed(const node* node)
|
||||||
|
{
|
||||||
|
uint32_t index = node - (struct node*)m_nodes_page;
|
||||||
|
|
||||||
|
uint32_t page_index = index / (PAGE_SIZE / sizeof(struct node));
|
||||||
|
ASSERT(page_index < PAGE_SIZE / sizeof(vaddr_t));
|
||||||
|
|
||||||
|
vaddr_t& page_vaddr = ((vaddr_t*)m_allocated_pages)[page_index];
|
||||||
|
if (page_vaddr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
paddr_t page_paddr = Heap::get().take_free_page();
|
||||||
|
ASSERT(page_paddr);
|
||||||
|
|
||||||
|
page_vaddr = m_process->mmu().get_free_page();
|
||||||
|
m_process->mmu().map_page_at(page_paddr, page_vaddr, MMU::Flags::UserSupervisor | MMU::Flags::ReadWrite | MMU::Flags::Present);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
|
#include <kernel/CriticalScope.h>
|
||||||
#include <kernel/FS/VirtualFileSystem.h>
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
#include <kernel/LockGuard.h>
|
#include <kernel/LockGuard.h>
|
||||||
#include <kernel/Memory/Heap.h>
|
#include <kernel/Memory/Heap.h>
|
||||||
|
@ -69,10 +70,10 @@ namespace Kernel
|
||||||
break;
|
break;
|
||||||
case LibELF::PT_LOAD:
|
case LibELF::PT_LOAD:
|
||||||
{
|
{
|
||||||
// TODO: Do some relocations?
|
// TODO: Do some relocations or map kernel to higher half?
|
||||||
ASSERT(process->mmu().is_range_free(elf_program_header.p_vaddr, elf_program_header.p_memsz));
|
ASSERT(process->mmu().is_range_free(elf_program_header.p_vaddr, elf_program_header.p_memsz));
|
||||||
|
|
||||||
uint8_t flags = MMU::Flags::UserSupervisor | MMU::Flags::Present;
|
MMU::flags_t flags = MMU::Flags::UserSupervisor | MMU::Flags::Present;
|
||||||
if (elf_program_header.p_flags & LibELF::PF_W)
|
if (elf_program_header.p_flags & LibELF::PF_W)
|
||||||
flags |= MMU::Flags::ReadWrite;
|
flags |= MMU::Flags::ReadWrite;
|
||||||
size_t page_start = elf_program_header.p_vaddr / PAGE_SIZE;
|
size_t page_start = elf_program_header.p_vaddr / PAGE_SIZE;
|
||||||
|
@ -82,12 +83,16 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
auto paddr = Heap::get().take_free_page();
|
auto paddr = Heap::get().take_free_page();
|
||||||
MUST(process->m_allocated_pages.push_back(paddr));
|
MUST(process->m_allocated_pages.push_back(paddr));
|
||||||
process->m_mmu->map_page_at(paddr, page * PAGE_SIZE, flags);
|
process->mmu().map_page_at(paddr, page * PAGE_SIZE, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CriticalScope _;
|
||||||
|
process->mmu().load();
|
||||||
|
memcpy((void*)elf_program_header.p_vaddr, elf->data() + elf_program_header.p_offset, elf_program_header.p_filesz);
|
||||||
|
memset((void*)(elf_program_header.p_vaddr + elf_program_header.p_filesz), 0, elf_program_header.p_memsz - elf_program_header.p_filesz);
|
||||||
|
Process::current().mmu().load();
|
||||||
}
|
}
|
||||||
process->m_mmu->load();
|
|
||||||
memcpy((void*)elf_program_header.p_vaddr, elf->data() + elf_program_header.p_offset, elf_program_header.p_filesz);
|
|
||||||
memset((void*)(elf_program_header.p_vaddr + elf_program_header.p_filesz), 0, elf_program_header.p_memsz - elf_program_header.p_filesz);
|
|
||||||
Process::current().mmu().load();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -100,6 +105,11 @@ namespace Kernel
|
||||||
|
|
||||||
delete elf;
|
delete elf;
|
||||||
|
|
||||||
|
MUST(process->m_fixed_width_allocators.emplace_back(process, 64));
|
||||||
|
MUST(process->m_fixed_width_allocators.emplace_back(process, 256));
|
||||||
|
MUST(process->m_fixed_width_allocators.emplace_back(process, 1024));
|
||||||
|
MUST(process->m_fixed_width_allocators.emplace_back(process, 4096));
|
||||||
|
|
||||||
register_process(process);
|
register_process(process);
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
@ -112,6 +122,7 @@ namespace Kernel
|
||||||
Process::~Process()
|
Process::~Process()
|
||||||
{
|
{
|
||||||
ASSERT(m_threads.empty());
|
ASSERT(m_threads.empty());
|
||||||
|
ASSERT(m_fixed_width_allocators.empty());
|
||||||
if (m_mmu)
|
if (m_mmu)
|
||||||
{
|
{
|
||||||
MMU::get().load();
|
MMU::get().load();
|
||||||
|
@ -144,6 +155,9 @@ namespace Kernel
|
||||||
for (auto& open_fd : m_open_files)
|
for (auto& open_fd : m_open_files)
|
||||||
open_fd.inode = nullptr;
|
open_fd.inode = nullptr;
|
||||||
|
|
||||||
|
// NOTE: We must clear allocators while the mmu is still alive
|
||||||
|
m_fixed_width_allocators.clear();
|
||||||
|
|
||||||
dprintln("process {} exit", pid());
|
dprintln("process {} exit", pid());
|
||||||
s_process_lock.lock();
|
s_process_lock.lock();
|
||||||
for (size_t i = 0; i < s_processes.size(); i++)
|
for (size_t i = 0; i < s_processes.size(); i++)
|
||||||
|
@ -364,6 +378,14 @@ namespace Kernel
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void*> Process::allocate(size_t bytes)
|
||||||
|
{
|
||||||
|
for (auto& allocator : m_fixed_width_allocators)
|
||||||
|
if (bytes <= allocator.allocation_size())
|
||||||
|
return (void*)allocator.allocate();
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
void Process::termid(char* buffer) const
|
void Process::termid(char* buffer) const
|
||||||
{
|
{
|
||||||
if (m_tty == nullptr)
|
if (m_tty == nullptr)
|
||||||
|
|
|
@ -55,6 +55,14 @@ namespace Kernel
|
||||||
return res.value();
|
return res.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long sys_alloc(size_t bytes)
|
||||||
|
{
|
||||||
|
auto res = Process::current().allocate(bytes);
|
||||||
|
if (res.is_error())
|
||||||
|
return -res.error().get_error_code();
|
||||||
|
return (long)res.value();
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" long cpp_syscall_handler(int syscall, void* arg1, void* arg2, void* arg3)
|
extern "C" long cpp_syscall_handler(int syscall, void* arg1, void* arg2, void* arg3)
|
||||||
{
|
{
|
||||||
Thread::current().set_in_syscall(true);
|
Thread::current().set_in_syscall(true);
|
||||||
|
@ -85,6 +93,9 @@ namespace Kernel
|
||||||
case SYS_OPEN:
|
case SYS_OPEN:
|
||||||
ret = sys_open((const char*)arg1, (int)(uintptr_t)arg2);
|
ret = sys_open((const char*)arg1, (int)(uintptr_t)arg2);
|
||||||
break;
|
break;
|
||||||
|
case SYS_ALLOC:
|
||||||
|
ret = sys_alloc((size_t)arg1);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Kernel::panic("Unknown syscall {}", syscall);
|
Kernel::panic("Unknown syscall {}", syscall);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#include <BAN/Assert.h>
|
#include <BAN/Assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
extern "C" void _fini();
|
extern "C" void _fini();
|
||||||
|
@ -55,9 +57,15 @@ char* getenv(const char*)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* malloc(size_t)
|
void* malloc(size_t bytes)
|
||||||
{
|
{
|
||||||
return nullptr;
|
long ret = syscall(SYS_ALLOC, bytes);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
errno = -ret;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return (void*)ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* calloc(size_t nmemb, size_t size)
|
void* calloc(size_t nmemb, size_t size)
|
||||||
|
|
|
@ -68,6 +68,12 @@ long syscall(long syscall, ...)
|
||||||
ret = Kernel::syscall(SYS_OPEN, path, oflags);
|
ret = Kernel::syscall(SYS_OPEN, path, oflags);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SYS_ALLOC:
|
||||||
|
{
|
||||||
|
size_t bytes = va_arg(args, size_t);
|
||||||
|
ret = Kernel::syscall(SYS_ALLOC, bytes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
puts("LibC: Unhandeled syscall");
|
puts("LibC: Unhandeled syscall");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
void* ptr = malloc(10);
|
||||||
|
if (ptr == NULL)
|
||||||
|
{
|
||||||
|
perror("malloc");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
*(int*)ptr = 5;
|
||||||
|
putc('0' + *(int*)ptr, stdout);
|
||||||
|
return 0;
|
||||||
|
|
||||||
FILE* fp = fopen("/boot/grub/grub.cfg", "r");
|
FILE* fp = fopen("/boot/grub/grub.cfg", "r");
|
||||||
if (fp == NULL)
|
if (fp == NULL)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue