diff --git a/assets/manifests/default.yaml b/assets/manifests/default.yaml index 2399b73..d651360 100644 --- a/assets/manifests/default.yaml +++ b/assets/manifests/default.yaml @@ -1,8 +1,9 @@ --- +init: srv.init programs: - name: panic.serial target: kernel - + flags: panic - name: drv.uefi_fb + flags: graphical - name: testapp - - name: srv.init diff --git a/configs/boot-debug.yaml b/configs/boot-debug.yaml index 507abe0..c9c26e6 100644 --- a/configs/boot-debug.yaml +++ b/configs/boot-debug.yaml @@ -5,6 +5,10 @@ variables: ld: clang++ ccflags: [ + "-nostdlib", + "-nodefaultlibs", + "-fno-builtin", + "-I${source_root}/external", "--target=x86_64-unknown-windows", "-ffreestanding", @@ -12,8 +16,7 @@ variables: "-fshort-wchar", "-fno-omit-frame-pointer", "-ggdb", - "-g3", - '-DKERNEL_FILENAME=L"jsix.elf"' ] + "-g3" ] cxxflags: [ "-fno-exceptions", "-fno-rtti" ] diff --git a/configs/kernel-debug.yaml b/configs/kernel-debug.yaml index 38790ea..f769a48 100644 --- a/configs/kernel-debug.yaml +++ b/configs/kernel-debug.yaml @@ -8,6 +8,7 @@ variables: "--target=x86_64-unknown-elf", "-I${source_root}/external", + "-nostdinc", "-nostdlib", "-ffreestanding", "-nodefaultlibs", @@ -18,7 +19,8 @@ variables: "-mno-red-zone", "-mcmodel=large", - "-g", + "-g3", + "-ggdb", "-D__ELF__", "-D__JSIX__", diff --git a/scripts/bonnibel/manifest.py b/scripts/bonnibel/manifest.py new file mode 100644 index 0000000..70d4902 --- /dev/null +++ b/scripts/bonnibel/manifest.py @@ -0,0 +1,94 @@ +from . import BonnibelError + +class Manifest: + from collections import namedtuple + Entry = namedtuple("Entry", ("module", "target", "output", "location", "description", "flags")) + + flags = { + "graphical": 0x01, + "panic": 0x02, + "symbols": 0x04, + } + + def __init__(self, path, modules): + from . import load_config + + config = load_config(path) + + self.kernel = self.__build_entry(modules, + config.get("kernel", dict()), + name="kernel", target="kernel") + + self.init = self.__build_entry(modules, + config.get("init", None)) + + self.programs = [self.__build_entry(modules, i) + for i in config.get("programs", tuple())] + + self.data = [] + for d in config.get("data", tuple()): + self.add_data(**d) + + def __build_entry(self, modules, config, target="user", name=None): + flags = tuple() + + if isinstance(config, str): + name = config + elif isinstance(config, dict): + name = config.get("name", name) + target = config.get("target", target) + flags = config.get("flags", tuple()) + if isinstance(flags, str): + flags = flags.split() + + mod = modules.get(name) + if not mod: + raise BonnibelError(f"Manifest specifies unknown module '{name}'") + + for f in flags: + if not f in Manifest.flags: + raise BonnibelError(f"Manifest specifies unknown flag '{f}'") + + return Manifest.Entry(name, target, mod.output, mod.location, mod.description, flags) + + def add_data(self, output, location, desc, flags=tuple()): + e = Manifest.Entry(None, None, output, location, desc, flags) + self.data.append(e) + return e + + def write_boot_config(self, path): + from os.path import join + import struct + + with open(path, 'wb') as outfile: + magic = "jsixboot".encode("utf-8") # magic string + version = 0 + reserved = 0 + + outfile.write(struct.pack("<8sBBHHH", + magic, version, reserved, + len(self.programs), len(self.data), + reserved)) + + def write_str(s): + outfile.write(struct.pack(" +#include + +#include "bootconfig.h" +#include "error.h" +#include "fs.h" +#include "status.h" + +namespace boot { + +constexpr uint64_t jsixboot = 0x746f6f627869736a; // "jsixboot" + +static const wchar_t * +read_string(util::buffer &data) +{ + uint16_t size = *util::read(data); + const wchar_t *string = reinterpret_cast(data.pointer); + data += size; + return string; +} + +static void +read_descriptor(descriptor &e, util::buffer &data) +{ + e.flags = static_cast(*util::read(data)); + e.path = read_string(data); + e.desc = read_string(data); +} + +bootconfig::bootconfig(util::buffer data, uefi::boot_services *bs) +{ + status_line status {L"Loading boot config"}; + + if (*util::read(data) != jsixboot) + error::raise(uefi::status::load_error, L"Bad header in jsix_boot.dat"); + + const uint8_t version = *util::read(data); + if (version != 0) + error::raise(uefi::status::incompatible_version, L"Bad version in jsix_boot.dat"); + + data += 1; // reserved byte + uint16_t num_programs = *util::read(data); + uint16_t num_data = *util::read(data); + data += 2; // reserved short + + read_descriptor(m_kernel, data); + read_descriptor(m_init, data); + + m_programs.count = num_programs; + m_programs.pointer = new descriptor [num_programs]; + + for (unsigned i = 0; i < num_programs; ++i) + read_descriptor(m_programs[i], data); + + m_data.count = num_programs; + m_data.pointer = new descriptor [num_data]; + + for (unsigned i = 0; i < num_data; ++i) + read_descriptor(m_data[i], data); +} + +} // namespace boot diff --git a/src/boot/bootconfig.h b/src/boot/bootconfig.h new file mode 100644 index 0000000..588510d --- /dev/null +++ b/src/boot/bootconfig.h @@ -0,0 +1,43 @@ +/// \file bootconfig.h +/// Definitions for reading the jsix bootconfig file +#pragma once + +#include +#include + +namespace uefi { + struct boot_services; +} + +namespace boot { + +using desc_flags = bootproto::desc_flags; + +struct descriptor { + desc_flags flags; + wchar_t const *path; + wchar_t const *desc; +}; + +/// A bootconfig is a manifest of potential files. +class bootconfig +{ +public: + using descriptors = util::counted; + + /// Constructor. Loads bootconfig from the given buffer. + bootconfig(util::buffer data, uefi::boot_services *bs); + + inline const descriptor & kernel() { return m_kernel; } + inline const descriptor & init() { return m_init; } + descriptors programs() { return m_programs; } + descriptors data() { return m_data; } + +private: + descriptor m_kernel; + descriptor m_init; + descriptors m_programs; + descriptors m_data; +}; + +} // namespace boot diff --git a/src/boot/loader.cpp b/src/boot/loader.cpp index 0ae36b2..27b9995 100644 --- a/src/boot/loader.cpp +++ b/src/boot/loader.cpp @@ -7,6 +7,7 @@ #include #include "allocator.h" +#include "bootconfig.h" #include "console.h" #include "error.h" #include "fs.h" @@ -23,7 +24,7 @@ using memory::alloc_type; util::buffer load_file( fs::file &disk, - const program_desc &desc) + const descriptor &desc) { status_line status(L"Loading file", desc.path); @@ -36,7 +37,7 @@ load_file( static void -create_module(util::buffer data, const program_desc &desc, bool loaded) +create_module(util::buffer data, const descriptor &desc, bool loaded) { size_t path_len = wstrlen(desc.path); bootproto::module_program *mod = g_alloc.allocate_module(path_len); @@ -50,18 +51,20 @@ create_module(util::buffer data, const program_desc &desc, bool loaded) // TODO: support non-ascii path characters and do real utf-16 to utf-8 // conversion - for (int i = 0; i < path_len; ++i) - mod->filename[i] = desc.path[i]; + for (int i = 0; i < path_len; ++i) { + char c = desc.path[i]; + mod->filename[i] = c == '\\' ? '/' : c; + } mod->filename[path_len] = 0; } bootproto::program * load_program( fs::file &disk, - const program_desc &desc, + const descriptor &desc, bool add_module) { - status_line status(L"Loading program", desc.name); + status_line status(L"Loading program", desc.desc); util::buffer data = load_file(disk, desc); @@ -113,9 +116,9 @@ load_program( void load_module( fs::file &disk, - const program_desc &desc) + const descriptor &desc) { - status_line status(L"Loading module", desc.name); + status_line status(L"Loading module", desc.desc); util::buffer data = load_file(disk, desc); create_module(data, desc, false); diff --git a/src/boot/loader.h b/src/boot/loader.h index 3fc2966..b72f94c 100644 --- a/src/boot/loader.h +++ b/src/boot/loader.h @@ -11,34 +11,30 @@ namespace bootproto { namespace boot { +class descriptor; + namespace fs { class file; } namespace loader { -struct program_desc -{ - const wchar_t *name; - const wchar_t *path; -}; - /// Load a file from disk into memory. /// \arg disk The opened UEFI filesystem to load from /// \arg desc The program descriptor identifying the file util::buffer load_file( fs::file &disk, - const program_desc &desc); + const descriptor &desc); /// Parse and load an ELF file in memory into a loaded image. /// \arg disk The opened UEFI filesystem to load from -/// \arg desc The program descriptor identifying the program +/// \arg desc The descriptor identifying the program /// \arg add_module Also create a module for this loaded program bootproto::program * load_program( fs::file &disk, - const program_desc &desc, + const descriptor &desc, bool add_module = false); /// Load a file from disk into memory, creating an init args module @@ -47,7 +43,7 @@ load_program( void load_module( fs::file &disk, - const program_desc &desc); + const descriptor &desc); /// Verify that a loaded ELF has the j6 kernel header /// \arg program The program to check for a header diff --git a/src/boot/main.cpp b/src/boot/main.cpp index 2fae2af..9c2573a 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -7,12 +7,14 @@ #include #include +#include #include #include #include #include #include "allocator.h" +#include "bootconfig.h" #include "console.h" #include "error.h" #include "fs.h" @@ -27,17 +29,6 @@ namespace boot { -const loader::program_desc kern_desc = {L"kernel", L"jsix.elf"}; -const loader::program_desc init_desc = {L"init server", L"srv.init.elf"}; -const loader::program_desc fb_driver = {L"UEFI framebuffer driver", L"drv.uefi_fb.elf"}; -const loader::program_desc uart_driver = {L"Serial port driver", L"drv.uart.elf"}; - -const loader::program_desc panic_handler = {L"Serial panic handler", L"panic.serial.elf"}; - -const loader::program_desc extra_programs[] = { - {L"test application", L"testapp.elf"}, -}; - /// Change a pointer to point to the higher-half linear-offset version /// of the address it points to. template @@ -81,23 +72,51 @@ load_resources(bootproto::args *args, video::screen *screen, uefi::handle image, status_line status {L"Loading programs"}; fs::file disk = fs::get_boot_volume(image, bs); + fs::file bc_data = disk.open(L"jsix_boot.dat"); + bootconfig bc {bc_data.load(), bs}; + + args->kernel = loader::load_program(disk, bc.kernel(), true); + args->init = loader::load_program(disk, bc.init()); + + namespace bits = util::bits; + using bootproto::desc_flags; if (screen) { video::make_module(screen); - loader::load_module(disk, fb_driver); - } else { - loader::load_module(disk, uart_driver); + + // Go through the screen-specific descriptors first to + // give them priority + for (const descriptor &d : bc.programs()) { + if (!bits::has(d.flags, desc_flags::graphical)) + continue; + + if (bits::has(d.flags, desc_flags::panic)) + args->panic = loader::load_program(disk, d); + else + loader::load_module(disk, d); + } } - util::buffer symbol_table = loader::load_file(disk, {L"symbol table", L"symbol_table.dat"}); - args->symbol_table = reinterpret_cast(symbol_table.pointer); + // Load the non-graphical descriptors + for (const descriptor &d : bc.programs()) { + if (bits::has(d.flags, desc_flags::graphical)) + continue; - args->kernel = loader::load_program(disk, kern_desc, true); - args->init = loader::load_program(disk, init_desc); - args->panic = loader::load_program(disk, panic_handler); + if (bits::has(d.flags, desc_flags::panic) && !args->panic) + args->panic = loader::load_program(disk, d); + else + loader::load_module(disk, d); + } - for (auto &desc : extra_programs) - loader::load_module(disk, desc); + // For now the only data we load is the symbol table + for (const descriptor &d : bc.data()) { + if (!bits::has(d.flags, desc_flags::symbols)) + continue; + + util::buffer symbol_table = loader::load_file(disk, d); + args->symbol_table = reinterpret_cast(symbol_table.pointer); + break; + } loader::verify_kernel_header(*args->kernel); } diff --git a/src/kernel/kernel.module b/src/kernel/kernel.module index 1cfdaa6..3dd2c5d 100644 --- a/src/kernel/kernel.module +++ b/src/kernel/kernel.module @@ -5,6 +5,7 @@ kernel = module("kernel", default = True, output = "jsix.elf", targets = [ "kernel" ], + description = "jsix kernel", deps = [ "util", "cpu", "bootproto" ], includes = [ "." ], sources = [ diff --git a/src/kernel/panic.serial/serial.module b/src/kernel/panic.serial/serial.module index 4880e8f..665ca5a 100644 --- a/src/kernel/panic.serial/serial.module +++ b/src/kernel/panic.serial/serial.module @@ -2,9 +2,9 @@ panic = module("panic.serial", kind = "exe", - output = "panic.serial.elf", targets = [ "kernel" ], deps = [ "util", "elf", "kernel" ], + description = "Serial panic handler", sources = [ "display.cpp", "entry.s", diff --git a/src/libraries/bootproto/include/bootproto/bootconfig.h b/src/libraries/bootproto/include/bootproto/bootconfig.h new file mode 100644 index 0000000..89254fc --- /dev/null +++ b/src/libraries/bootproto/include/bootproto/bootconfig.h @@ -0,0 +1,17 @@ +#pragma once +/// \file bootproto/bootconfig.h +/// Data structures for reading jsix_boot.dat + +#include +#include + +namespace bootproto { + +enum class desc_flags : uint16_t { + graphical = 0x01, + panic = 0x02, + symbols = 0x04, +}; +is_bitfield(desc_flags); + +} // namespace bootproto diff --git a/src/libraries/libc/libc.module b/src/libraries/libc/libc.module index 469b2b4..815ac2e 100644 --- a/src/libraries/libc/libc.module +++ b/src/libraries/libc/libc.module @@ -2,7 +2,6 @@ libc = module("libc", kind = "lib", - output = "libc.a", includes = [ "include" ], deps = [ "j6" ], sources = [ diff --git a/src/libraries/util/include/util/counted.h b/src/libraries/util/include/util/counted.h index dd93d5e..e99be2b 100644 --- a/src/libraries/util/include/util/counted.h +++ b/src/libraries/util/include/util/counted.h @@ -35,6 +35,14 @@ struct counted /// Return an iterator to the end of the array inline const_iterator end() const { return offset_pointer(pointer, sizeof(T)*count); } + + /// Advance the pointer by N items + inline counted & operator+=(unsigned i) { + if (i > count) i = count; + pointer += i; + count -= i; + return *this; + } }; /// Specialize for `void` which cannot be indexed or iterated @@ -43,8 +51,24 @@ struct counted { void *pointer; size_t count; + + /// Advance the pointer by N bytes + inline counted & operator+=(unsigned i) { + if (i > count) i = count; + pointer = offset_pointer(pointer, i); + count -= i; + return *this; + } }; using buffer = counted; +template +const T * read(buffer &b) { + const T *p = reinterpret_cast(b.pointer); + b.pointer = offset_pointer(b.pointer, sizeof(T)); + b.count -= sizeof(T); + return p; +} + } // namespace util diff --git a/src/user/drv.uefi_fb/uefi_fb.module b/src/user/drv.uefi_fb/uefi_fb.module index 278e3fe..3b3e25b 100644 --- a/src/user/drv.uefi_fb/uefi_fb.module +++ b/src/user/drv.uefi_fb/uefi_fb.module @@ -3,6 +3,7 @@ module("drv.uefi_fb", targets = [ "user" ], deps = [ "libc" ], + description = "UEFI framebuffer driver", sources = [ "font.cpp", "main.cpp", diff --git a/src/user/srv.init/init.module b/src/user/srv.init/init.module index 29df789..7ac432e 100644 --- a/src/user/srv.init/init.module +++ b/src/user/srv.init/init.module @@ -3,6 +3,7 @@ init = module("srv.init", targets = [ "user" ], deps = [ "libc", "elf", "bootproto" ], + description = "Init server", sources = [ "loader.cpp", "main.cpp", diff --git a/src/user/testapp/testapp.module b/src/user/testapp/testapp.module index d47cecd..50b65e8 100644 --- a/src/user/testapp/testapp.module +++ b/src/user/testapp/testapp.module @@ -3,6 +3,7 @@ module("testapp", targets = [ "user" ], deps = [ "libc" ], + description = "Testbed app", sources = [ "io.cpp", "main.cpp",