Compare commits

...

2 Commits

Author SHA1 Message Date
Drew Galbraith aefb4f082b Add a kernel ELF module and load it in a new process.
Don't yet jump to userspace.
2023-05-29 00:32:54 -07:00
Drew Galbraith f86bbe6ea9 Wireframe for syscalls in place 2023-05-18 16:03:09 -07:00
25 changed files with 353 additions and 16 deletions

View File

@ -1,3 +1,4 @@
target remote localhost:1234
source ~/.gdbinit-gef.py
gef-remote --qemu-user --qemu-binary=builddbg/zion/zion localhost 1234
file builddbg/zion/zion
break zion

View File

@ -10,6 +10,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_EXPORT_COMPILE_COMMANDS True)
add_subdirectory(zion)
add_subdirectory(sys)
add_custom_command(
OUTPUT disk.img

View File

@ -18,6 +18,7 @@ echo "Loopback device: ${dev}"
cleanup() {
umount efi
rm -rf efi
losetup -d $dev
}
trap cleanup EXIT
@ -35,5 +36,7 @@ cp /usr/share/limine/BOOTX64.EFI efi/EFI/BOOT
cp /usr/share/limine/limine.sys efi/
cp ../zion/boot/limine.cfg efi/
cp zion/zion efi/
mkdir -p efi/sys
cp sys/test efi/sys/test
chown drew:drew $1

17
sys/CMakeLists.txt Normal file
View File

@ -0,0 +1,17 @@
set(_COMPILE_FLAGS "-ffreestanding -mgeneral-regs-only")
set(_LINK_FLAGS "-nostdlib")
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
add_executable(test
test.cpp
)
target_link_libraries(test
zion_lib)
set_target_properties(test
PROPERTIES
COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${_COMPILE_FLAGS}"
LINK_FLAGS "${CMAKE_EXE_LINK_FLAGS} ${_LINK_FLAGS}"
)

7
sys/test.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "zcall.h"
int main() {
ZDebug("Testing");
return 0;
}

View File

@ -5,6 +5,8 @@ add_executable(zion
debug/debug.cpp
interrupt/interrupt.cpp
interrupt/interrupt_enter.s
loader/elf_loader.cpp
loader/init_loader.cpp
memory/kernel_heap.cpp
memory/paging_util.cpp
memory/physical_memory.cpp
@ -12,6 +14,8 @@ add_executable(zion
scheduler/process.cpp
scheduler/scheduler.cpp
scheduler/thread.cpp
syscall/syscall.cpp
syscall/syscall_enter.s
zion.cpp)
target_include_directories(zion
@ -48,3 +52,17 @@ set_target_properties(zion
COMPILE_FLAGS "${_Z_COMPILE_FLAGS}"
LINK_FLAGS "${_Z_LINK_FLAGS}"
)
add_library(zion_lib STATIC
usr/crt0.s
usr/zcall.cpp)
target_include_directories(zion_lib
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
set_target_properties(zion_lib
PROPERTIES
COMPILE_FLAGS "${_Z_COMPILE_FLAGS}")

View File

@ -25,4 +25,15 @@ uint64_t GetHigherHalfDirectMap() {
return gHhdmRequest.response->offset;
}
static volatile struct limine_module_request gModuleRequest {
.id = LIMINE_MODULE_REQUEST, .revision = 0, .response = 0,
};
const limine_module_response& GetModules() {
if (!gModuleRequest.response) {
panic("No module response from limine");
}
return *gModuleRequest.response;
}
} // namespace boot

View File

@ -7,4 +7,6 @@ namespace boot {
const limine_memmap_response& GetMemoryMap();
uint64_t GetHigherHalfDirectMap();
const limine_module_response& GetModules();
} // namespace boot

View File

@ -3,7 +3,7 @@
TIMEOUT=0
:AcadiaOS
PROTOCOL=limine
PROTOCOL=limine
KERNEL_PATH=boot:///zion
KERNEL_PATH=boot:///zion
MODULE_PATH=boot:///sys/test

8
zion/include/zcall.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdint.h>
#define Z_DEBUG_PRINT 100
uint64_t ZDebug(const char* message);

View File

@ -0,0 +1,74 @@
#include "loader/elf_loader.h"
#include "debug/debug.h"
#include "memory/paging_util.h"
namespace {
typedef struct {
char ident[16];
uint16_t type;
uint16_t machine;
uint32_t version;
uint64_t entry;
uint64_t phoff;
uint64_t shoff;
uint32_t flags;
uint16_t ehsize;
uint16_t phentsize;
uint16_t phnum;
uint16_t shentsize;
uint16_t shnum;
uint16_t shstrndx;
} Elf64Header;
typedef struct {
uint32_t name;
uint32_t type;
uint64_t flags;
uint64_t addr;
uint64_t offset;
uint64_t size;
uint32_t link;
uint32_t info;
uint64_t addralign;
uint64_t entsize;
} Elf64SectionHeader;
typedef struct {
uint32_t type;
uint32_t flags;
uint64_t offset;
uint64_t vaddr;
uint64_t paddr;
uint64_t filesz;
uint64_t memsz;
uint64_t align;
} Elf64ProgramHeader;
void badmemcpy(uint64_t base, uint64_t offset, uint64_t dest) {
uint8_t* ptr = reinterpret_cast<uint8_t*>(base);
uint8_t* dest_ptr = reinterpret_cast<uint8_t*>(dest);
for (uint64_t i = 0; i < offset; i++) {
dest_ptr[i] = ptr[i];
}
}
} // namespace
void LoadElfProgram(uint64_t base, uint64_t offset) {
Elf64Header* header = reinterpret_cast<Elf64Header*>(base);
dbgln("phoff: %u phnum: %u", header->phoff, header->phnum);
Elf64ProgramHeader* programs =
reinterpret_cast<Elf64ProgramHeader*>(base + header->phoff);
for (uint64_t i = 0; i < header->phnum; i++) {
Elf64ProgramHeader& program = programs[i];
dbgln(
"prog: type: %u, flags: %u, offset: %u\n vaddr: %m, paddr: %m\n "
"filesz: %u, memsz: %u, align: %u",
program.type, program.flags, program.offset, program.vaddr,
program.paddr, program.filesz, program.memsz, program.align);
EnsureResident(program.vaddr, program.memsz);
badmemcpy(base + program.offset, program.filesz, program.vaddr);
}
}

5
zion/loader/elf_loader.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
void LoadElfProgram(uint64_t base, uint64_t length);

View File

@ -0,0 +1,30 @@
#include "loader/init_loader.h"
#include "boot/boot_info.h"
#include "debug/debug.h"
#include "loader/elf_loader.h"
#include "scheduler/process.h"
#include "scheduler/scheduler.h"
namespace {
const limine_file& GetInitProgram() {
const limine_module_response& resp = boot::GetModules();
dbgln("Dumping modules");
for (uint64_t i = 0; i < resp.module_count; i++) {
const limine_file& file = *resp.modules[i];
dbgln("%s,%m,%x", file.path, file.address, file.size);
// TODO eventually compare this file path.
return file;
}
panic("No init program found");
}
} // namespace
void LoadInitProgram() {
const limine_file& init_prog = GetInitProgram();
sched::InsertProcess(
new Process(reinterpret_cast<uint64_t>(init_prog.address)));
}

View File

@ -0,0 +1,3 @@
#pragma once
void LoadInitProgram();

View File

@ -25,14 +25,14 @@ Process* Process::RootProcess() {
return proc;
}
Process::Process() : id_(gNextId++) {
Process::Process(uint64_t elf_ptr) : id_(gNextId++) {
cr3_ = phys_mem::AllocatePage();
InitializePml4(cr3_);
CreateThread();
CreateThread(elf_ptr);
}
void Process::CreateThread() {
Thread* thread = new Thread(this, next_thread_id_++);
void Process::CreateThread(uint64_t elf_ptr) {
Thread* thread = new Thread(this, next_thread_id_++, elf_ptr);
ThreadEntry* tentry = new ThreadEntry{
.thread = thread,
.next = nullptr,

View File

@ -9,12 +9,12 @@ class Process {
public:
// Caller takes ownership of returned process.
static Process* RootProcess();
Process();
Process(uint64_t elf_ptr);
uint64_t id() { return id_; }
uint64_t cr3() { return cr3_; }
void CreateThread();
void CreateThread(uint64_t elf_ptr);
Thread* GetThread(uint64_t tid);
private:

View File

@ -55,6 +55,7 @@ class Scheduler {
Process& CurrentProcess() { return current_thread_->process(); }
Thread& CurrentThread() { return *current_thread_; }
void InsertProcess(Process* process) { proc_list_.InsertProcess(process); }
void Enqueue(Thread* thread) {
Thread* back = current_thread_;
while (back->next_thread_ != nullptr) {
@ -108,6 +109,7 @@ void EnableScheduler() { GetScheduler().Enable(); }
void Yield() { GetScheduler().Yield(); }
void InsertProcess(Process* process) { GetScheduler().InsertProcess(process); }
void EnqueueThread(Thread* thread) { GetScheduler().Enqueue(thread); }
Process& CurrentProcess() { return GetScheduler().CurrentProcess(); }

View File

@ -1,6 +1,7 @@
#include "scheduler/thread.h"
#include "debug/debug.h"
#include "loader/elf_loader.h"
#include "scheduler/process.h"
#include "scheduler/scheduler.h"
@ -16,7 +17,8 @@ extern "C" void thread_init() {
Thread* Thread::RootThread(Process* root_proc) { return new Thread(root_proc); }
Thread::Thread(Process* proc, uint64_t tid) : process_(proc), id_(tid) {
Thread::Thread(Process* proc, uint64_t tid, uint64_t elf_ptr)
: process_(proc), id_(tid), elf_ptr_(elf_ptr) {
uint64_t* stack = new uint64_t[512];
uint64_t* stack_ptr = stack + 511;
// 0: rip
@ -28,11 +30,13 @@ Thread::Thread(Process* proc, uint64_t tid) : process_(proc), id_(tid) {
// 16: cr3
*(stack_ptr - 16) = proc->cr3();
rsp0_ = reinterpret_cast<uint64_t>(stack_ptr - 16);
rsp0_start_ = reinterpret_cast<uint64_t>(stack_ptr);
}
uint64_t Thread::pid() { return process_->id(); }
void Thread::Init() {
LoadElfProgram(elf_ptr_, 0);
while (true) {
dbgln("[%u.%u]", pid(), id_);
sched::Yield();

View File

@ -9,7 +9,7 @@ class Thread {
public:
static Thread* RootThread(Process* root_proc);
explicit Thread(Process* proc, uint64_t tid);
explicit Thread(Process* proc, uint64_t tid, uint64_t elf_ptr);
uint64_t tid() { return id_; };
uint64_t pid();
@ -17,6 +17,7 @@ class Thread {
Process& process() { return *process_; }
uint64_t* Rsp0Ptr() { return &rsp0_; }
uint64_t Rsp0Start() { return rsp0_start_; }
// Called the first time the thread starts up.
void Init();
@ -30,7 +31,12 @@ class Thread {
Process* process_;
uint64_t id_;
uint64_t elf_ptr_;
// Stack pointer to take on resume.
// Stack will contain the full thread context.
uint64_t rsp0_;
// Stack pointer to take when returning from userspace.
// I don't think me mind clobbering the stack here.
uint64_t rsp0_start_;
};

57
zion/syscall/syscall.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "syscall/syscall.h"
#include <stdint.h>
#include "debug/debug.h"
#include "scheduler/scheduler.h"
#define EFER 0xC0000080
#define STAR 0xC0000081
#define LSTAR 0xC0000082
namespace {
uint64_t GetMSR(uint32_t msr) {
uint32_t lo, hi;
asm("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return (static_cast<uint64_t>(hi) << 32) | lo;
}
void SetMSR(uint32_t msr, uint64_t val) {
uint32_t lo = static_cast<uint32_t>(val);
uint32_t hi = val >> 32;
asm("wrmsr" ::"a"(lo), "d"(hi), "c"(msr));
}
extern "C" void syscall_enter();
} // namespace
// Used by syscall_enter.s
extern "C" uint64_t GetKernelRsp() {
return sched::CurrentThread().Rsp0Start();
}
void InitSyscall() {
uint64_t efer_val = GetMSR(EFER);
efer_val |= 1;
SetMSR(EFER, efer_val);
if (GetMSR(EFER) != efer_val) {
panic("Failed to set EFER MSR");
}
uint64_t star_val = GetMSR(STAR);
// FIXME: Fix GDT such that we can properly set the user CS.
// Due to the ability to jump from a 64 bit kernel into compatibility mode,
// we set the user_cs to the kernel_cs because it adds 16 to jump to 64-bit
// mode. See AMD Manual 3.4 instruction SYSRET for more info.
uint64_t kernel_cs = 0x8;
uint64_t user_cs = kernel_cs;
star_val |= (kernel_cs << 32) | (user_cs << 48);
SetMSR(STAR, star_val);
SetMSR(LSTAR, reinterpret_cast<uint64_t>(syscall_enter));
}
extern "C" void SyscallHandler(uint64_t call_id, char* message) {
dbgln(message);
}

3
zion/syscall/syscall.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void InitSyscall();

View File

@ -0,0 +1,60 @@
.global syscall_enter
syscall_enter:
# Technically don't need to save all of these as
# the SYS V ABI will preserve some of them by
# default but I doubt that this costs us much.
push %rbx
push %rcx # Special! This is the return address
push %rdx
push %rsi
push %rdi
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
call GetKernelRsp
# RAX holds the kernel RSP now.
mov %rsp, %rbx
mov %rax, %rsp
push %rbx
# Now that we are on the kernel stack we can re-enable interrupts.
sti
# Restore caller registers using the userspace rsp in rbx
mov 0x40(%rbx), %rdi
mov 0x48(%rbx), %rsi
mov 0x50(%rbx), %rdx
mov 0x58(%rbx), %rcx
# Don't push the rbp and rsp as the callee will do so.
call SyscallHandler
# Clear interrupts since we are moving back to the user stack here.
# The sysret call will re-enable them for us.
cli
# Pop the userspace rsp off the stack
pop %rsp
pop %r15
pop %r14
pop %r13
pop %r12
pop %r11 # Contains rflags.
pop %r10
pop %r9
pop %r8
pop %rdi
pop %rsi
pop %rdx
pop %rcx # Contains return address.
pop %rbx
# Because we haven't touched rax since calling syscall_handler it should still have the return value.
sysretq

10
zion/usr/crt0.s Normal file
View File

@ -0,0 +1,10 @@
.text
.global _start
_start:
call main
call _exit
_exit:
mov $1, %rdi
syscall

13
zion/usr/zcall.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "include/zcall.h"
#include <stdint.h>
uint64_t SysCall1(uint64_t number, const void* first) {
uint64_t return_code;
asm("syscall" : "=a"(return_code) : "D"(number), "S"(first) : "rcx", "r11");
return return_code;
}
uint64_t ZDebug(const char* message) {
return SysCall1(Z_DEBUG_PRINT, message);
}

View File

@ -3,10 +3,12 @@
#include "common/gdt.h"
#include "debug/debug.h"
#include "interrupt/interrupt.h"
#include "loader/init_loader.h"
#include "memory/kernel_heap.h"
#include "memory/paging_util.h"
#include "memory/physical_memory.h"
#include "scheduler/scheduler.h"
#include "syscall/syscall.h"
extern "C" void zion() {
InitGdt();
@ -17,12 +19,12 @@ extern "C" void zion() {
KernelHeap heap(0xFFFFFFFF'40000000, 0xFFFFFFFF'80000000);
phys_mem::InitPhysicalMemoryManager();
InitSyscall();
sched::InitScheduler();
Process p1;
p1.CreateThread();
Process p2;
p2.CreateThread();
sched::EnableScheduler();
LoadInitProgram();
sched::Yield();
dbgln("Sleeping!");