Compare commits

..

No commits in common. "0ea9974512adb8cf58974646b0421669e2c6f96f" and "f0e14cf7e9380b222b220b2de3478b197481a888" have entirely different histories.

14 changed files with 77 additions and 236 deletions

View File

@ -1,4 +1,18 @@
section .text section .text
global _start global _start
_start: _start:
mov eax, ecx
push rdx
mov eax, 1
mov edi, hello_world
mov esi, 14
int 42h int 42h
nop
section .rodata
hello_world:
db 'Hello, world!', 0xa, 0
section .bss
array:
resb 10

View File

@ -23,11 +23,19 @@ static bool can_write_segment(u32 flags)
return can_write_segment(flags) && can_execute_segment(flags); return can_write_segment(flags) && can_execute_segment(flags);
}*/ }*/
ELFSegment::ELFSegment(u64 base, usize size) : m_base(base), m_size(size)
{
}
namespace ELFLoader namespace ELFLoader
{ {
// FIXME: Check that all calls to read_contents() read the proper amount of bytes. // FIXME: Check that all calls to read_contents() read the proper amount of bytes.
Result<ELFData> load(const TarStream::Entry& elf_entry, const TarStream& stream) Result<ELFData> load(const TarStream::Entry& elf_entry, const TarStream& stream)
{ {
LinkedList<ELFSegment> segments;
auto guard = make_scope_guard([&] { segments.consume([](ELFSegment* segment) { delete segment; }); });
Elf64_Ehdr elf_header; Elf64_Ehdr elf_header;
usize nread = stream.read_contents(elf_entry, &elf_header, 0, sizeof elf_header); usize nread = stream.read_contents(elf_entry, &elf_header, 0, sizeof elf_header);
if (nread < sizeof elf_header) if (nread < sizeof elf_header)
@ -93,6 +101,9 @@ namespace ELFLoader
/*expect(!can_write_and_execute_segment(program_header.p_flags), /*expect(!can_write_and_execute_segment(program_header.p_flags),
"Segment is both writable and executable");*/ "Segment is both writable and executable");*/
ELFSegment* segment = TRY(make<ELFSegment>(program_header.p_vaddr, program_header.p_memsz));
segments.append(segment);
int flags = MMU::User | MMU::NoExecute; int flags = MMU::User | MMU::NoExecute;
if (can_write_segment(program_header.p_flags)) flags |= MMU::ReadWrite; if (can_write_segment(program_header.p_flags)) flags |= MMU::ReadWrite;
else if (can_execute_segment(program_header.p_flags)) else if (can_execute_segment(program_header.p_flags))
@ -113,6 +124,14 @@ namespace ELFLoader
else { kdbgln("ELF: Encountered non-loadable program header, skipping"); } else { kdbgln("ELF: Encountered non-loadable program header, skipping"); }
} }
return ELFData { elf_header.e_entry }; if (segments.count() == 0)
{
kdbgln("Error while loading ELF: No loadable segments");
return err(ENOEXEC);
}
guard.deactivate();
return ELFData { segments, elf_header.e_entry };
} }
} }

View File

@ -47,8 +47,28 @@ typedef struct
u64 p_align; /* Segment alignment */ u64 p_align; /* Segment alignment */
} Elf64_Phdr; } Elf64_Phdr;
struct ELFSegment : public LinkedListNode<ELFSegment>
{
u64 base() const
{
return m_base;
}
usize size() const
{
return m_size;
}
ELFSegment(u64 base, usize size);
private:
u64 m_base;
usize m_size;
};
struct ELFData struct ELFData
{ {
LinkedList<ELFSegment> segments;
u64 entry; u64 entry;
}; };

View File

@ -31,7 +31,6 @@ namespace MMU
void flush_all(); void flush_all();
Result<PageDirectory*> create_page_directory_for_userspace(); Result<PageDirectory*> create_page_directory_for_userspace();
Result<void> delete_userspace_page_directory(PageDirectory* directory);
void setup_initial_page_directory(); void setup_initial_page_directory();
PageDirectory* kernel_page_directory(); PageDirectory* kernel_page_directory();

View File

@ -187,5 +187,4 @@ ISR 20 ; virtualization exception (#VE)
ISR_ERROR 21 ; control-protection exception (#CP) ISR_ERROR 21 ; control-protection exception (#CP)
; ISR 22-31 reserved ; ISR 22-31 reserved
IRQ 32, 0 ; timer interrupt IRQ 32, 0 ; timer interrupt
IRQ 33, 0 ; keyboard interrupt IRQ 33, 0 ; keyboard interrupt
ISR 66 ; user exit

View File

@ -107,12 +107,6 @@ extern "C" void arch_interrupt_entry(Registers* regs)
scancode_queue.try_push(scancode); scancode_queue.try_push(scancode);
pic_eoi(regs); pic_eoi(regs);
} }
else if (regs->isr == 66) // Exit!!
{
kdbgln("exiting from user task!!");
Scheduler::current()->state = ThreadState::Dying;
kernel_yield();
}
else else
{ {
kwarnln("IRQ catched! Halting."); kwarnln("IRQ catched! Halting.");

View File

@ -2,7 +2,6 @@
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include <luna/CString.h> #include <luna/CString.h>
#include <luna/Result.h> #include <luna/Result.h>
#include <luna/ScopeGuard.h>
#include <luna/SystemError.h> #include <luna/SystemError.h>
#pragma GCC push_options #pragma GCC push_options
@ -51,16 +50,11 @@ namespace MMU
return l4_table()->entries[l4_index(addr)]; return l4_table()->entries[l4_index(addr)];
} }
constexpr PageDirectory* raw_l3_table(u64 l4)
{
const u64 l3 = sign | (rindex << 39) | (rindex << 30) | (rindex << 21) | (l4 << 12);
return (PageDirectory*)l3;
}
constexpr PageDirectory* l3_table(u64 addr) constexpr PageDirectory* l3_table(u64 addr)
{ {
const u64 l4 = l4_index(addr); const u64 l4 = l4_index(addr);
return raw_l3_table(l4); const u64 l3 = sign | (rindex << 39) | (rindex << 30) | (rindex << 21) | (l4 << 12);
return (PageDirectory*)l3;
} }
constexpr u64 l3_index(u64 addr) constexpr u64 l3_index(u64 addr)
@ -73,17 +67,12 @@ namespace MMU
return l3_table(addr)->entries[l3_index(addr)]; return l3_table(addr)->entries[l3_index(addr)];
} }
constexpr PageDirectory* raw_l2_table(u64 l4, u64 l3)
{
const u64 l2 = sign | (rindex << 39) | (rindex << 30) | (l4 << 21) | (l3 << 12);
return (PageDirectory*)l2;
}
constexpr PageDirectory* l2_table(u64 addr) constexpr PageDirectory* l2_table(u64 addr)
{ {
const u64 l4 = l4_index(addr); const u64 l4 = l4_index(addr);
const u64 l3 = l3_index(addr); const u64 l3 = l3_index(addr);
return raw_l2_table(l4, l3); const u64 l2 = sign | (rindex << 39) | (rindex << 30) | (l4 << 21) | (l3 << 12);
return (PageDirectory*)l2;
} }
constexpr u64 l2_index(u64 addr) constexpr u64 l2_index(u64 addr)
@ -96,18 +85,13 @@ namespace MMU
return l2_table(addr)->entries[l2_index(addr)]; return l2_table(addr)->entries[l2_index(addr)];
} }
constexpr PageDirectory* raw_l1_table(u64 l4, u64 l3, u64 l2)
{
const u64 l1 = sign | (rindex << 39) | (l4 << 30) | (l3 << 21) | (l2 << 12);
return (PageDirectory*)l1;
}
constexpr PageDirectory* l1_table(u64 addr) constexpr PageDirectory* l1_table(u64 addr)
{ {
const u64 l4 = l4_index(addr); const u64 l4 = l4_index(addr);
const u64 l3 = l3_index(addr); const u64 l3 = l3_index(addr);
const u64 l2 = l2_index(addr); const u64 l2 = l2_index(addr);
return raw_l1_table(l4, l3, l2); const u64 l1 = sign | (rindex << 39) | (l4 << 30) | (l3 << 21) | (l2 << 12);
return (PageDirectory*)l1;
} }
constexpr u64 l1_index(u64 addr) constexpr u64 l1_index(u64 addr)
@ -286,96 +270,6 @@ namespace MMU
flush_all(); flush_all();
} }
Result<PageDirectory*> create_page_directory_for_userspace()
{
u64 directory_virt = TRY(MemoryManager::alloc_for_kernel(1, MMU::ReadWrite | MMU::NoExecute));
u64 directory_phys = MMU::get_physical(directory_virt).value();
PageDirectory* directory = (PageDirectory*)directory_virt;
memset(directory, 0, ARCH_PAGE_SIZE);
PageTableEntry& recursive_entry = directory->entries[rindex];
recursive_entry.read_write = true;
recursive_entry.present = true;
recursive_entry.set_address(directory_phys);
directory->entries[511] = g_kernel_directory->entries[511];
// From now on, we're only going to use the physical address, since accessing the PageDirectory will be dealt
// with using recursive mapping. So let's make sure we don't leak any VM.
MemoryManager::unmap_weak_and_free_vm(directory_virt, 1);
return (PageDirectory*)directory_phys;
}
Result<void> delete_userspace_page_directory(PageDirectory* directory)
{
check(directory);
// Needed in order to access page tables using the recursive mapping system.
switch_page_directory(directory);
auto guard = make_scope_guard([&] {
check(g_kernel_directory);
switch_page_directory(g_kernel_directory);
MemoryManager::free_frame((u64)directory);
});
PageDirectory* table = l4_table();
// Let's iterate over every top-level entry, skipping the last two entries (recursive mapping and kernel pages)
for (u64 i = 0; i < 510; i++)
{
PageTableEntry& l4 = table->entries[i];
if (!l4.present) continue;
PageDirectory* pdp = raw_l3_table(i);
for (u64 j = 0; j < 512; j++)
{
PageTableEntry& l3 = pdp->entries[j];
if (!l3.present) continue;
if (l3.larger_pages)
{
// FIXME: Maybe we shouldn't delete some pages in an address space, such as shared memory.
TRY(MemoryManager::free_frame(l3.get_address()));
}
PageDirectory* pd = raw_l2_table(i, j);
for (u64 k = 0; k < 512; k++)
{
PageTableEntry& l2 = pd->entries[k];
if (!l2.present) continue;
if (l2.larger_pages)
{
// FIXME: Maybe we shouldn't delete some pages in an address space, such as shared memory.
TRY(MemoryManager::free_frame(l2.get_address()));
}
PageDirectory* pt = raw_l1_table(i, j, k);
for (u64 l = 0; l < 512; l++)
{
PageTableEntry& l1 = pt->entries[l];
if (!l1.present) continue;
// FIXME: Maybe we shouldn't delete some pages in an address space, such as shared memory.
TRY(MemoryManager::free_frame(l1.get_address()));
}
TRY(MemoryManager::free_frame(l2.get_address()));
}
TRY(MemoryManager::free_frame(l3.get_address()));
}
TRY(MemoryManager::free_frame(l4.get_address()));
}
// No need to clean up manually, the ScopeGuard we set up earlier will do that for us.
return {};
}
PageDirectory* kernel_page_directory() PageDirectory* kernel_page_directory()
{ {
return g_kernel_directory; return g_kernel_directory;

View File

@ -36,7 +36,7 @@ u64 IDTEntry::get_offset() const
static IDTEntry idt[256]; static IDTEntry idt[256];
#define IDT_TA_InterruptGate 0b10001110 #define IDT_TA_InterruptGate 0b10001110
#define IDT_TA_UserCallableInterruptGate 0b11101110 #define IDT_TA_UserInterruptGate 0b11101110
#define IDT_TA_TrapGate 0b10001111 #define IDT_TA_TrapGate 0b10001111
struct [[gnu::packed]] IDTR struct [[gnu::packed]] IDTR
@ -60,7 +60,6 @@ static void idt_add_handler(short num, void* handler, u8 type_attr)
#define INT(x) extern "C" void _isr##x() #define INT(x) extern "C" void _isr##x()
#define TRAP(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_TrapGate) #define TRAP(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_TrapGate)
#define IRQ(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_InterruptGate) #define IRQ(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_InterruptGate)
#define SYS(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_UserCallableInterruptGate)
INT(0); INT(0);
INT(1); INT(1);
@ -84,7 +83,6 @@ INT(20);
INT(21); INT(21);
INT(32); INT(32);
INT(33); INT(33);
INT(66);
void setup_idt() void setup_idt()
{ {
@ -112,7 +110,6 @@ void setup_idt()
TRAP(21); TRAP(21);
IRQ(32); IRQ(32);
IRQ(33); IRQ(33);
SYS(66);
static IDTR idtr; static IDTR idtr;
idtr.limit = 0x0FFF; idtr.limit = 0x0FFF;

View File

@ -51,8 +51,7 @@ Result<void> init()
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars()); kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars()); kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
Thread::init(); MMU::unmap(0x400000);
Scheduler::init();
TarStream::Entry entry; TarStream::Entry entry;
while (TRY(g_initrd.read_next_entry().try_set_value_with_specific_error(entry, 0))) while (TRY(g_initrd.read_next_entry().try_set_value_with_specific_error(entry, 0)))
@ -62,10 +61,18 @@ Result<void> init()
kinfoln("Found file %s in initial ramdisk, of size %s", entry.name, kinfoln("Found file %s in initial ramdisk, of size %s", entry.name,
to_dynamic_unit(entry.size).release_value().chars()); to_dynamic_unit(entry.size).release_value().chars());
if (!strcmp(entry.name, "bin/app")) { TRY(Scheduler::new_userspace_thread(entry, g_initrd)); } if (!strcmp(entry.name, "bin/app"))
{
auto data = TRY(ELFLoader::load(entry, g_initrd));
data.segments.consume([](ELFSegment* segment) { delete segment; });
kinfoln("Loaded ELF with entry=%#.16lx", data.entry);
}
} }
} }
Thread::init();
Scheduler::init();
TRY(Scheduler::new_kernel_thread(heap_thread)); TRY(Scheduler::new_kernel_thread(heap_thread));
TRY(Scheduler::new_kernel_thread(reap_thread)); TRY(Scheduler::new_kernel_thread(reap_thread));

View File

@ -309,15 +309,6 @@ namespace MemoryManager
return {}; return {};
} }
Result<void> unmap_weak_and_free_vm(u64 virt, usize count)
{
CHECK_PAGE_ALIGNED(virt);
KernelVM::free_several_pages(virt, count);
return unmap_weak(virt, count);
}
Result<void> remap_unaligned(u64 address, usize count, int flags) Result<void> remap_unaligned(u64 address, usize count, int flags)
{ {
if (!is_aligned<ARCH_PAGE_SIZE>(address)) count++; if (!is_aligned<ARCH_PAGE_SIZE>(address)) count++;

View File

@ -30,7 +30,6 @@ namespace MemoryManager
Result<void> unmap_owned(u64 virt, usize count); Result<void> unmap_owned(u64 virt, usize count);
Result<void> unmap_owned_and_free_vm(u64 virt, usize count); Result<void> unmap_owned_and_free_vm(u64 virt, usize count);
Result<void> unmap_weak(u64 virt, usize count); Result<void> unmap_weak(u64 virt, usize count);
Result<void> unmap_weak_and_free_vm(u64 virt, usize count);
usize free(); usize free();
usize used(); usize used();

View File

@ -1,5 +1,4 @@
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include "ELF.h"
#include "Log.h" #include "Log.h"
#include "arch/CPU.h" #include "arch/CPU.h"
#include "arch/MMU.h" #include "arch/MMU.h"
@ -20,7 +19,6 @@ namespace Scheduler
g_idle.init_regs_kernel(); g_idle.init_regs_kernel();
g_idle.set_ip((u64)CPU::idle_loop); g_idle.set_ip((u64)CPU::idle_loop);
g_idle.state = ThreadState::Idle; g_idle.state = ThreadState::Idle;
g_idle.is_kernel = true;
g_idle.ticks_left = 1; g_idle.ticks_left = 1;
@ -62,8 +60,6 @@ namespace Scheduler
thread->stack = thread_stack; thread->stack = thread_stack;
thread->is_kernel = true;
g_threads.append(thread); g_threads.append(thread);
kinfoln("CREATED THREAD: id %lu with ip %lx and sp %lx", thread->id, thread->ip(), thread->sp()); kinfoln("CREATED THREAD: id %lu with ip %lx and sp %lx", thread->id, thread->ip(), thread->sp());
@ -99,84 +95,13 @@ namespace Scheduler
return new_kernel_thread_impl(thread); return new_kernel_thread_impl(thread);
} }
static Result<void> create_stacks(Stack& user_stack, Stack& kernel_stack)
{
const u64 THREAD_STACK_BASE = 0x10000;
TRY(MemoryManager::alloc_at(THREAD_STACK_BASE, 4, MMU::ReadWrite | MMU::NoExecute | MMU::User));
auto guard = make_scope_guard([&] { MemoryManager::unmap_owned(THREAD_STACK_BASE, 4); });
u64 kernel_stack_base = TRY(MemoryManager::alloc_for_kernel(4, MMU::ReadWrite | MMU::NoExecute));
guard.deactivate();
user_stack = { THREAD_STACK_BASE, 4 * ARCH_PAGE_SIZE };
kernel_stack = { kernel_stack_base, 4 * ARCH_PAGE_SIZE };
return {};
}
Result<void> new_userspace_thread(const TarStream::Entry& entry, const TarStream& stream)
{
Thread* thread = TRY(new_thread());
thread->is_kernel = false;
auto guard = make_scope_guard([&] { delete thread; });
auto directory = TRY(MMU::create_page_directory_for_userspace());
auto directory_guard = make_scope_guard([&] {
MMU::switch_page_directory(MMU::kernel_page_directory());
MemoryManager::free_frame((u64)directory);
});
MMU::switch_page_directory(directory);
thread->init_regs_user();
auto data = TRY(ELFLoader::load(entry, stream));
thread->set_ip(data.entry);
TRY(create_stacks(thread->stack, thread->kernel_stack));
thread->set_sp(thread->stack.top());
thread->directory = directory;
guard.deactivate();
directory_guard.deactivate();
kinfoln("CREATED USERSPACE THREAD: id %lu with ip %lx and sp %lx (ksp %lx)", thread->id, thread->ip(),
thread->sp(), thread->kernel_stack.top());
g_threads.append(thread);
return {};
}
void reap_thread(Thread* thread) void reap_thread(Thread* thread)
{ {
kinfoln("reap: reaping thread with id %zu", thread->id); kinfoln("reap: reaping thread with id %zu", thread->id);
auto stack = thread->stack;
if (thread->is_kernel) kinfoln("deleting thread stack @ %#lx, has %zu bytes of stack", stack.bottom(), stack.bytes());
{ // FIXME: Propagate errors I guess?
auto stack = thread->stack; MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
// FIXME: Propagate errors I guess?
kinfoln("deleting stack @ %#lx", stack.bottom());
MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
}
else
{
auto stack = thread->kernel_stack;
kinfoln("deleting kstack @ %#lx", stack.bottom());
// FIXME: Propagate errors I guess?
MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
}
if (!thread->is_kernel) MMU::delete_userspace_page_directory(thread->directory);
delete thread; delete thread;
} }
@ -213,15 +138,7 @@ namespace Scheduler
void generic_switch_context(Thread* old_thread, Thread* new_thread, Registers* regs) void generic_switch_context(Thread* old_thread, Thread* new_thread, Registers* regs)
{ {
if (old_thread != new_thread) if (old_thread != new_thread) switch_context(old_thread, new_thread, regs);
{
switch_context(old_thread, new_thread, regs);
if (!new_thread->is_kernel)
{
MMU::switch_page_directory(new_thread->directory);
CPU::switch_kernel_stack(new_thread->kernel_stack.top());
}
}
if (new_thread->is_idle()) if (new_thread->is_idle())
{ {

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "thread/Thread.h" #include "thread/Thread.h"
#include <luna/TarStream.h>
namespace Scheduler namespace Scheduler
{ {
@ -13,8 +12,6 @@ namespace Scheduler
Result<void> new_kernel_thread(void (*func)(void)); Result<void> new_kernel_thread(void (*func)(void));
Result<void> new_kernel_thread(void (*func)(void*), void* arg); Result<void> new_kernel_thread(void (*func)(void*), void* arg);
Result<void> new_userspace_thread(const TarStream::Entry& entry, const TarStream& stream);
Thread* pick_task(); Thread* pick_task();
void reap_thread(Thread* thread); void reap_thread(Thread* thread);

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "arch/MMU.h"
#include <luna/LinkedList.h> #include <luna/LinkedList.h>
#include <luna/Result.h> #include <luna/Result.h>
#include <luna/Stack.h> #include <luna/Stack.h>
@ -33,14 +32,9 @@ struct Thread : public LinkedListNode<Thread>
u64 sleep_ticks_left; u64 sleep_ticks_left;
Stack stack; Stack stack;
Stack kernel_stack;
ThreadState state = ThreadState::Runnable; ThreadState state = ThreadState::Runnable;
bool is_kernel { true };
PageDirectory* directory;
bool is_idle() bool is_idle()
{ {
return state == ThreadState::Idle; return state == ThreadState::Idle;