diff --git a/.gitignore b/.gitignore index 616233bd..34f5764f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ toolchain/ .vscode/ build/ initrd/boot/moon -env-local.sh \ No newline at end of file +env-local.sh +initrd/bin/** \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f522402..a43fbb8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ set(CMAKE_CXX_COMPILER x86_64-luna-g++) set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64) +set(CMAKE_ASM_NASM_LINK_EXECUTABLE "x86_64-luna-ld -o ") + set(CMAKE_FIND_ROOT_PATH ${LUNA_ROOT}/toolchain/x86-64-luna) set(ARCH $ENV{ARCH}) @@ -25,4 +27,5 @@ endif() message(STATUS "Configuring Luna for ${ARCH}") add_subdirectory(luna) -add_subdirectory(kernel) \ No newline at end of file +add_subdirectory(kernel) +add_subdirectory(apps) \ No newline at end of file diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt new file mode 100644 index 00000000..cc7a0218 --- /dev/null +++ b/apps/CMakeLists.txt @@ -0,0 +1,6 @@ +function(luna_app SOURCE_FILE APP_NAME) + add_executable(${APP_NAME} ${SOURCE_FILE}) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${APP_NAME}" DESTINATION ${LUNA_ROOT}/initrd/bin) +endfunction() + +luna_app(app.asm app) \ No newline at end of file diff --git a/apps/app.asm b/apps/app.asm new file mode 100644 index 00000000..78dc6066 --- /dev/null +++ b/apps/app.asm @@ -0,0 +1,18 @@ +section .text +global _start +_start: + mov eax, ecx + push rdx + mov eax, 1 + mov edi, hello_world + mov esi, 14 + int 42h + nop + +section .rodata +hello_world: + db 'Hello, world!', 0xa, 0 + +section .bss +array: + resb 10 \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 66637a77..7e9e5db4 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -16,6 +16,7 @@ set(SOURCES src/thread/Thread.cpp src/thread/Scheduler.cpp src/InitRD.cpp + src/ELF.cpp ) if("${ARCH}" MATCHES "x86_64") diff --git a/kernel/src/ELF.cpp b/kernel/src/ELF.cpp new file mode 100644 index 00000000..fe4a1357 --- /dev/null +++ b/kernel/src/ELF.cpp @@ -0,0 +1,137 @@ +#include "ELF.h" +#include "Log.h" +#include "arch/CPU.h" +#include "arch/MMU.h" +#include "memory/MemoryManager.h" +#include +#include +#include +#include + +static bool can_execute_segment(u32 flags) +{ + return flags & 1; +} + +static bool can_write_segment(u32 flags) +{ + return flags & 2; +} + +/*static bool can_write_and_execute_segment(u32 flags) +{ + return can_write_segment(flags) && can_execute_segment(flags); +}*/ + +ELFSegment::ELFSegment(u64 base, usize size) : m_base(base), m_size(size) +{ +} + +namespace ELFLoader +{ + // FIXME: Check that all calls to read_contents() read the proper amount of bytes. + Result load(const TarStream::Entry& elf_entry, const TarStream& stream) + { + LinkedList segments; + + auto guard = make_scope_guard([&] { segments.consume([](ELFSegment* segment) { delete segment; }); }); + + Elf64_Ehdr elf_header; + usize nread = stream.read_contents(elf_entry, &elf_header, 0, sizeof elf_header); + if (nread < sizeof elf_header) + { + kdbgln("Error while loading ELF: ELF header does not fit in entry"); + return err(ENOEXEC); + } + + if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) + { + kdbgln("Error while loading ELF: ELF header has no valid magic"); + return err(ENOEXEC); + } + + if (elf_header.e_ident[EI_CLASS] != ELFCLASS64) + { + kdbgln("Error while loading ELF: ELF object is not 64-bit"); + return err(ENOEXEC); + } + + if (elf_header.e_ident[EI_DATA] != ELFDATA2LSB) + { + kdbgln("Error while loading ELF: ELF object is not 2's complement little-endian"); + return err(ENOEXEC); + } + + if (elf_header.e_type != ET_EXEC) + { + kdbgln("Error while loading ELF: ELF object is not an executable"); + return err(ENOEXEC); + } + + if (elf_header.e_machine != EM_MACH) + { + kdbgln("Error while loading ELF: ELF object's target architecture does not match the current one (%s)", + CPU::platform_string()); + return err(ENOEXEC); + } + + if (elf_header.e_phnum == 0) + { + kdbgln("Error while loading ELF: ELF object has no program headers"); + return err(ENOEXEC); + } + + kdbgln("ELF: Loading ELF with entry=%#.16lx", elf_header.e_entry); + + usize i; + Elf64_Phdr program_header; + + for (stream.read_contents(elf_entry, &program_header, elf_header.e_phoff, sizeof program_header), i = 0; + i < elf_header.e_phnum; + i++, stream.read_contents(elf_entry, &program_header, elf_header.e_phoff + (i * elf_header.e_phentsize), + sizeof program_header)) + { + if (program_header.p_type == PT_LOAD) + { + kdbgln("ELF: Loading segment (offset=%zu, base=%#.16lx, filesize=%zu, memsize=%zu)", + program_header.p_offset, program_header.p_vaddr, program_header.p_filesz, + program_header.p_memsz); + + check(is_aligned(program_header.p_vaddr)); + /*expect(!can_write_and_execute_segment(program_header.p_flags), + "Segment is both writable and executable");*/ + + ELFSegment* segment = TRY(make(program_header.p_vaddr, program_header.p_memsz)); + segments.append(segment); + + int flags = MMU::User | MMU::NoExecute; + if (can_write_segment(program_header.p_flags)) flags |= MMU::ReadWrite; + else if (can_execute_segment(program_header.p_flags)) + flags &= ~MMU::NoExecute; + + // Allocate physical memory for the segment + TRY(MemoryManager::alloc_at(program_header.p_vaddr, + get_blocks_from_size(program_header.p_memsz, ARCH_PAGE_SIZE), flags)); + + // Load the file section of the segment + stream.read_contents(elf_entry, (void*)program_header.p_vaddr, program_header.p_offset, + program_header.p_filesz); + + // Fill out the rest of the segment with 0s + memset((void*)(program_header.p_vaddr + program_header.p_filesz), 0, + program_header.p_memsz - program_header.p_filesz); + } + else { kdbgln("ELF: Encountered non-loadable program header, skipping"); } + } + + if (segments.count() == 0) + { + kdbgln("Error while loading ELF: No loadable segments"); + return err(ENOEXEC); + } + + guard.deactivate(); + + return ELFData { segments, elf_header.e_entry }; + } +} \ No newline at end of file diff --git a/kernel/src/ELF.h b/kernel/src/ELF.h new file mode 100644 index 00000000..1a3dce72 --- /dev/null +++ b/kernel/src/ELF.h @@ -0,0 +1,78 @@ +#pragma once +#include +#include +#include + +#define ELFMAG "\177ELF" +#define SELFMAG 4 +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ET_EXEC 2 /* Executable file */ +#define PT_LOAD 1 /* Loadable program segment */ +#ifdef ARCH_X86_64 +#define EM_MACH 62 /* AMD x86-64 architecture */ +#else +#error "Unknown architecture." +#endif + +typedef struct +{ + u8 e_ident[16]; /* Magic number and other info */ + u16 e_type; /* Object file type */ + u16 e_machine; /* Architecture */ + u32 e_version; /* Object file version */ + u64 e_entry; /* Entry point virtual address */ + u64 e_phoff; /* Program header table file offset */ + u64 e_shoff; /* Section header table file offset */ + u32 e_flags; /* Processor-specific flags */ + u16 e_ehsize; /* ELF header size in bytes */ + u16 e_phentsize; /* Program header table entry size */ + u16 e_phnum; /* Program header table entry count */ + u16 e_shentsize; /* Section header table entry size */ + u16 e_shnum; /* Section header table entry count */ + u16 e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +typedef struct +{ + u32 p_type; /* Segment type */ + u32 p_flags; /* Segment flags */ + u64 p_offset; /* Segment file offset */ + u64 p_vaddr; /* Segment virtual address */ + u64 p_paddr; /* Segment physical address */ + u64 p_filesz; /* Segment size in file */ + u64 p_memsz; /* Segment size in memory */ + u64 p_align; /* Segment alignment */ +} Elf64_Phdr; + +struct ELFSegment : public LinkedListNode +{ + 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 +{ + LinkedList segments; + u64 entry; +}; + +namespace ELFLoader +{ + Result load(const TarStream::Entry& elf_entry, const TarStream& stream); +}; \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index cfd37243..83519890 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,3 +1,4 @@ +#include "ELF.h" #include "InitRD.h" #include "Log.h" #include "arch/CPU.h" @@ -67,6 +68,8 @@ Result init() kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars()); kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars()); + MMU::unmap(0x400000); + TarStream::Entry entry; while (TRY(g_initrd.read_next_entry().try_set_value_with_specific_error(entry, 0))) { @@ -75,11 +78,11 @@ Result init() kinfoln("Found file %s in initial ramdisk, of size %s", entry.name, to_dynamic_unit(entry.size).release_value().chars()); - if (!strcmp(entry.name, "sys/config")) + if (!strcmp(entry.name, "bin/app")) { - auto contents = TRY(g_initrd.read_contents_as_string(entry, 0, entry.size)); - - kinfoln("%s", contents.chars()); + auto data = TRY(ELFLoader::load(entry, g_initrd)); + data.segments.consume([](ELFSegment* segment) { delete segment; }); + kinfoln("Loaded ELF with entry=%#.16lx", data.entry); } } }