[boot] Build, load, and pass initrd from boot to init

The initrd image is now created by the build system, loaded by the
bootloader, and passed to srv.init, which loads it (but doesn't do
anything with it yet, so this is actually a functional regression).

This simplifies a lot of the modules code between boot and init as well:
Gone are the many subclasses of module and all the data being inline
with the module structs, except for any loaded files. Now the only
modules loaded and passed will be the initrd, and any devices only the
bootloader has knowledge of, like the UEFI framebuffer.
This commit is contained in:
Justin C. Miller
2023-01-17 19:12:40 -07:00
parent 6ef15a2721
commit 66abcc57a2
26 changed files with 399 additions and 296 deletions

View File

@@ -1,8 +1,12 @@
--- ---
location: jsix
init: srv.init init: srv.init
programs: initrd:
- name: panic.serial name: initrd.dat
target: kernel format: zstd
flags: panic panic:
- name: srv.logger - panic.serial
- name: drv.uart services:
- srv.logger
drivers:
- drv.uart

View File

@@ -1,9 +1,13 @@
--- ---
location: jsix
init: srv.init init: srv.init
initrd:
name: initrd.dat
format: zstd
flags: ["test"] flags: ["test"]
programs: panic:
- name: panic.serial - panic.serial
target: kernel services:
flags: panic - test_runner
- name: drv.uart drivers:
- name: test_runner - drv.uart

View File

@@ -2,12 +2,16 @@ from . import BonnibelError
class Manifest: class Manifest:
from collections import namedtuple from collections import namedtuple
Entry = namedtuple("Entry", ("module", "target", "output", "location", "description", "flags")) Entry = namedtuple("Entry", ("module", "target", "output", "flags"))
formats = {
"none": 0x00,
"zstd": 0x01,
}
flags = { flags = {
"graphical": 0x01, "graphical": 0x01,
"panic": 0x02, "symbols": 0x80,
"symbols": 0x04,
} }
boot_flags = { boot_flags = {
@@ -20,6 +24,8 @@ class Manifest:
config = load_config(path) config = load_config(path)
self.location = config.get("location", "jsix")
self.kernel = self.__build_entry(modules, self.kernel = self.__build_entry(modules,
config.get("kernel", dict()), config.get("kernel", dict()),
name="kernel", target="kernel") name="kernel", target="kernel")
@@ -27,11 +33,24 @@ class Manifest:
self.init = self.__build_entry(modules, self.init = self.__build_entry(modules,
config.get("init", None)) config.get("init", None))
self.programs = [self.__build_entry(modules, i) self.panics = [self.__build_entry(modules, i, target="kernel")
for i in config.get("programs", tuple())] for i in config.get("panic", tuple())]
self.services = [self.__build_entry(modules, i)
for i in config.get("services", tuple())]
self.drivers = [self.__build_entry(modules, i)
for i in config.get("drivers", tuple())]
self.flags = config.get("flags", tuple()) self.flags = config.get("flags", tuple())
initrd = config.get("initrd", dict())
self.initrd = initrd.get("name", "initrd.dat")
self.initrd_format = initrd.get("format", "none")
if not self.initrd_format in Manifest.formats:
raise BonnibelError(f"Manifest specifies unknown initrd format '{self.initrd_format}'")
self.data = [] self.data = []
for d in config.get("data", tuple()): for d in config.get("data", tuple()):
self.add_data(**d) self.add_data(**d)
@@ -56,10 +75,10 @@ class Manifest:
if not f in Manifest.flags: if not f in Manifest.flags:
raise BonnibelError(f"Manifest specifies unknown flag '{f}'") raise BonnibelError(f"Manifest specifies unknown flag '{f}'")
return Manifest.Entry(name, target, mod.output, mod.location, mod.description, flags) return Manifest.Entry(name, target, mod.output, flags)
def add_data(self, output, location, desc, flags=tuple()): def add_data(self, output, desc, flags=tuple()):
e = Manifest.Entry(None, None, output, location, desc, flags) e = Manifest.Entry(None, None, output, flags)
self.data.append(e) self.data.append(e)
return e return e
@@ -69,35 +88,83 @@ class Manifest:
with open(path, 'wb') as outfile: with open(path, 'wb') as outfile:
magic = "jsixboot".encode("utf-8") # magic string magic = "jsixboot".encode("utf-8") # magic string
version = 0 version = 1
reserved = 0 reserved = 0
initrd_format = Manifest.formats.get(self.initrd_format, 0)
bootflags = sum([Manifest.boot_flags.get(s, 0) for s in self.flags]) bootflags = sum([Manifest.boot_flags.get(s, 0) for s in self.flags])
outfile.write(struct.pack("<8sBBHHH", outfile.write(struct.pack("<8s BBH HH",
magic, version, reserved, magic,
len(self.programs), len(self.data), version, reserved, len(self.panics),
bootflags)) initrd_format, bootflags))
def write_str(s): def write_str(s):
outfile.write(struct.pack("<H", (len(s)+1)*2)) data = s.encode("utf-16le")
outfile.write(s.encode("utf-16le")) outfile.write(struct.pack("<H", len(data)+2))
outfile.write(data)
outfile.write(b"\0\0") outfile.write(b"\0\0")
def write_path(name):
write_str(join(self.location, name).replace('/','\\'))
def write_ent(ent): def write_ent(ent):
flags = 0 flags = 0
for f in ent.flags: for f in ent.flags:
flags |= Manifest.flags[f] flags |= Manifest.flags[f]
outfile.write(struct.pack("<H", flags)) outfile.write(struct.pack("<H", flags))
write_str(join(ent.location, ent.output).replace('/','\\')) write_path(ent.output)
write_str(ent.description)
write_ent(self.kernel) write_ent(self.kernel)
write_ent(self.init) write_ent(self.init)
write_path(self.initrd)
for p in self.programs: for p in self.panics:
write_ent(p) write_ent(p)
for d in self.data: def write_init_config(self, path, modules):
write_ent(d) from os.path import join
import struct
with open(path, 'wb') as outfile:
magic = "jsixinit".encode("utf-8") # magic string
version = 1
reserved = 0
string_data = bytearray()
outfile.write(struct.pack("<8s BBH HH",
magic,
version, reserved, len(self.services),
len(self.data), len(self.drivers)))
offset_ptr = outfile.tell()
outfile.write(struct.pack("<H", 0)) # filled in later
def write_str(s):
pos = len(string_data)
string_data.extend(s.encode("utf-8") + b"\0")
outfile.write(struct.pack("<H", pos))
def write_driver(ent):
write_str(ent.output)
drivers = modules[ent.module].drivers
outfile.write(struct.pack("<H", len(drivers)))
for driver in drivers:
write_str(driver)
for p in self.services:
write_str(p.output)
for p in self.data:
write_str(p.output)
for p in self.drivers:
write_driver(p)
offset = outfile.tell()
outfile.write(string_data)
outfile.seek(offset_ptr)
outfile.write(struct.pack("<H", offset))

View File

@@ -23,9 +23,9 @@ class Module:
"public_headers": (set, ()), "public_headers": (set, ()),
"includes": (tuple, ()), "includes": (tuple, ()),
"sources": (tuple, ()), "sources": (tuple, ()),
"drivers": (tuple, ()),
"variables": (dict, ()), "variables": (dict, ()),
"default": (bool, False), "default": (bool, False),
"location": (str, "jsix"),
"description": (str, None), "description": (str, None),
"no_libc": (bool, False), "no_libc": (bool, False),
} }

View File

@@ -71,9 +71,13 @@ class Project:
fatroot.mkdir(exist_ok=True) fatroot.mkdir(exist_ok=True)
fatroot_content = [] fatroot_content = []
initrd_content = []
from .manifest import Manifest
manifest = Manifest(manifest_file, modules)
def add_fatroot(source, entry): def add_fatroot(source, entry):
output = join(entry.location, entry.output) output = join(manifest.location, entry.output)
fatroot_output = f"${{build_root}}/fatroot/{output}" fatroot_output = f"${{build_root}}/fatroot/{output}"
build.build( build.build(
@@ -103,16 +107,30 @@ class Project:
add_fatroot(intermediary, entry) add_fatroot(intermediary, entry)
from .manifest import Manifest def add_initrd_exe(entry):
manifest = Manifest(manifest_file, modules) input_path = f"${{build_root}}/{entry.target}/{entry.output}"
intermediary = f"${{build_root}}/{entry.output}"
build.build(
rule = "strip",
outputs = [intermediary],
inputs = [input_path],
implicit = [f"{input_path}.dump"],
variables = {
"name": f"Stripping {entry.module}",
"debug": f"${{build_root}}/.debug/{entry.output}.debug",
})
initrd_content.append(intermediary)
add_fatroot_exe(manifest.kernel) add_fatroot_exe(manifest.kernel)
add_fatroot_exe(manifest.init) add_fatroot_exe(manifest.init)
for program in manifest.programs: for program in manifest.panics: add_fatroot_exe(program)
add_fatroot_exe(program) for program in manifest.services: add_initrd_exe(program)
for program in manifest.drivers: add_initrd_exe(program)
syms = manifest.add_data("symbol_table.dat", syms = manifest.add_data("symbol_table.dat",
manifest.kernel.location, "Symbol table", ("symbols",)) "Symbol table", ("symbols",))
sym_out = f"${{build_root}}/symbol_table.dat" sym_out = f"${{build_root}}/symbol_table.dat"
build.build( build.build(
@@ -120,7 +138,7 @@ class Project:
outputs = [sym_out], outputs = [sym_out],
inputs = [f"${{build_root}}/{modules['kernel'].output}"], inputs = [f"${{build_root}}/{modules['kernel'].output}"],
) )
add_fatroot(sym_out, syms) initrd_content.append(sym_out)
bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi" bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi"
build.build( build.build(
@@ -132,8 +150,20 @@ class Project:
}) })
build.newline() build.newline()
boot_config = join(fatroot, "jsix_boot.dat") boot_config = str(fatroot / "jsix_boot.dat")
init_config = str(output / "init.manifest")
manifest.write_boot_config(boot_config) manifest.write_boot_config(boot_config)
manifest.write_init_config(init_config, modules)
initrd_content.append(init_config)
initrd = join(fatroot, manifest.location, "initrd.dat")
build.build(
rule = "mkinitrd",
outputs = [initrd],
inputs = initrd_content,
)
build.newline()
fatroot_content.append(initrd)
build.build( build.build(
rule = "makefat", rule = "makefat",

View File

@@ -73,7 +73,6 @@ allocator::add_modules()
if (m_modules) if (m_modules)
m_modules->next = reinterpret_cast<uintptr_t>(mods); m_modules->next = reinterpret_cast<uintptr_t>(mods);
mods->modules = reinterpret_cast<module*>(mods + 1);
m_modules = mods; m_modules = mods;
m_next_mod = mods->modules; m_next_mod = mods->modules;
return; return;
@@ -110,11 +109,13 @@ allocator::allocate_pages(size_t count, alloc_type type, bool zero)
} }
module * module *
allocator::allocate_module_untyped(size_t size) allocator::allocate_module()
{ {
static constexpr size_t size = sizeof(module);
size_t remaining = size_t remaining =
reinterpret_cast<uintptr_t>(m_modules) + page_size reinterpret_cast<uintptr_t>(m_modules) + page_size
- reinterpret_cast<uintptr_t>(m_next_mod); - reinterpret_cast<uintptr_t>(m_next_mod) - sizeof(module);
if (size > remaining) if (size > remaining)
add_modules(); add_modules();
@@ -122,8 +123,6 @@ allocator::allocate_module_untyped(size_t size)
++m_modules->count; ++m_modules->count;
module *m = m_next_mod; module *m = m_next_mod;
m_next_mod = util::offset_pointer(m_next_mod, size); m_next_mod = util::offset_pointer(m_next_mod, size);
m->mod_length = size;
return m; return m;
} }

View File

@@ -32,10 +32,7 @@ public:
void * allocate_pages(size_t count, alloc_type type, bool zero = false); void * allocate_pages(size_t count, alloc_type type, bool zero = false);
template <typename M> module * allocate_module();
M * allocate_module(size_t extra = 0) {
return static_cast<M*>(allocate_module_untyped(sizeof(M) + extra));
}
void memset(void *start, size_t size, uint8_t value); void memset(void *start, size_t size, uint8_t value);
void copy(void *to, void *from, size_t size); void copy(void *to, void *from, size_t size);
@@ -54,7 +51,6 @@ public:
private: private:
void add_register(); void add_register();
void add_modules(); void add_modules();
module * allocate_module_untyped(size_t size);
uefi::boot_services &m_bs; uefi::boot_services &m_bs;

View File

@@ -24,7 +24,6 @@ read_descriptor(descriptor &e, util::buffer &data)
{ {
e.flags = static_cast<desc_flags>(*util::read<uint16_t>(data)); e.flags = static_cast<desc_flags>(*util::read<uint16_t>(data));
e.path = read_string(data); e.path = read_string(data);
e.desc = read_string(data);
} }
bootconfig::bootconfig(util::buffer data, uefi::boot_services *bs) bootconfig::bootconfig(util::buffer data, uefi::boot_services *bs)
@@ -35,29 +34,23 @@ bootconfig::bootconfig(util::buffer data, uefi::boot_services *bs)
error::raise(uefi::status::load_error, L"Bad header in jsix_boot.dat"); error::raise(uefi::status::load_error, L"Bad header in jsix_boot.dat");
const uint8_t version = *util::read<uint8_t>(data); const uint8_t version = *util::read<uint8_t>(data);
if (version != 0) if (version != 1)
error::raise(uefi::status::incompatible_version, L"Bad version in jsix_boot.dat"); error::raise(uefi::status::incompatible_version, L"Bad version in jsix_boot.dat");
data += 1; // reserved byte data += 1; // reserved byte
uint16_t num_programs = *util::read<uint16_t>(data); uint16_t num_panics = *util::read<uint16_t>(data);
uint16_t num_data = *util::read<uint16_t>(data); m_initrd_format = *util::read<uint16_t>(data);
m_flags = *util::read<uint16_t>(data); m_flags = *util::read<uint16_t>(data);
read_descriptor(m_kernel, data); read_descriptor(m_kernel, data);
read_descriptor(m_init, data); read_descriptor(m_init, data);
m_initrd = read_string(data);
m_programs.count = num_programs; m_panics.count = num_panics;
m_programs.pointer = new descriptor [num_programs]; m_panics.pointer = new descriptor [num_panics];
for (unsigned i = 0; i < num_programs; ++i) for (unsigned i = 0; i < num_panics; ++i)
read_descriptor(m_programs[i], data); read_descriptor(m_panics[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 } // namespace boot

View File

@@ -16,7 +16,6 @@ using desc_flags = bootproto::desc_flags;
struct descriptor { struct descriptor {
desc_flags flags; desc_flags flags;
wchar_t const *path; wchar_t const *path;
wchar_t const *desc;
}; };
/// A bootconfig is a manifest of potential files. /// A bootconfig is a manifest of potential files.
@@ -31,15 +30,17 @@ public:
inline uint16_t flags() const { return m_flags; } inline uint16_t flags() const { return m_flags; }
inline const descriptor & kernel() const { return m_kernel; } inline const descriptor & kernel() const { return m_kernel; }
inline const descriptor & init() const { return m_init; } inline const descriptor & init() const { return m_init; }
descriptors programs() { return m_programs; } inline const wchar_t * initrd() const { return m_initrd; }
descriptors data() { return m_data; } inline uint16_t initrd_format() const { return m_initrd_format; }
inline const descriptors & panics() { return m_panics; }
private: private:
uint16_t m_flags; uint16_t m_flags;
uint16_t m_initrd_format;
descriptor m_kernel; descriptor m_kernel;
descriptor m_init; descriptor m_init;
descriptors m_programs; descriptors m_panics;
descriptors m_data; wchar_t const *m_initrd;
}; };
} // namespace boot } // namespace boot

View File

@@ -24,52 +24,26 @@ using memory::alloc_type;
util::buffer util::buffer
load_file( load_file(
fs::file &disk, fs::file &disk,
const descriptor &desc) const wchar_t *path)
{ {
status_line status(L"Loading file", desc.path); status_line status(L"Loading file", path);
fs::file file = disk.open(desc.path); fs::file file = disk.open(path);
util::buffer b = file.load(); util::buffer b = file.load();
//console::print(L" Loaded at: 0x%lx, %d bytes\r\n", b.data, b.size); //console::print(L" Loaded at: 0x%lx, %d bytes\r\n", b.data, b.size);
return b; return b;
} }
static void
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<bootproto::module_program>(path_len);
mod->mod_type = bootproto::module_type::program;
mod->base_address = reinterpret_cast<uintptr_t>(data.pointer);
mod->size = data.count;
if (loaded)
mod->mod_flags = static_cast<bootproto::module_flags>(
static_cast<uint8_t>(mod->mod_flags) |
static_cast<uint8_t>(bootproto::module_flags::no_load));
// TODO: support non-ascii path characters and do real utf-16 to utf-8
// conversion
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 * bootproto::program *
load_program( load_program(
fs::file &disk, fs::file &disk,
const descriptor &desc, const wchar_t *name,
bool add_module) const descriptor &desc)
{ {
status_line status(L"Loading program", desc.desc); status_line status(L"Loading program", name);
util::buffer data = load_file(disk, desc); util::buffer data = load_file(disk, desc.path);
if (add_module)
create_module(data, desc, true);
elf::file program(data.pointer, data.count); elf::file program(data.pointer, data.count);
if (!program.valid()) { if (!program.valid()) {
@@ -129,12 +103,17 @@ load_program(
void void
load_module( load_module(
fs::file &disk, fs::file &disk,
const descriptor &desc) const wchar_t *name,
const wchar_t *path,
bootproto::module_type type,
uint16_t subtype)
{ {
status_line status(L"Loading module", desc.desc); status_line status(L"Loading module", name);
util::buffer data = load_file(disk, desc); bootproto::module *mod = g_alloc.allocate_module();
create_module(data, desc, false); mod->type = type;
mod->subtype = subtype;
mod->data = load_file(disk, path);
} }
void void

View File

@@ -2,11 +2,11 @@
/// Definitions for loading the kernel into memory /// Definitions for loading the kernel into memory
#pragma once #pragma once
#include <bootproto/init.h>
#include <util/counted.h> #include <util/counted.h>
namespace bootproto { namespace bootproto {
struct program; struct program;
struct module;
} }
namespace boot { namespace boot {
@@ -21,29 +21,35 @@ namespace loader {
/// Load a file from disk into memory. /// Load a file from disk into memory.
/// \arg disk The opened UEFI filesystem to load from /// \arg disk The opened UEFI filesystem to load from
/// \arg desc The program descriptor identifying the file /// \arg path The path of the file to load
util::buffer util::buffer
load_file( load_file(
fs::file &disk, fs::file &disk,
const descriptor &desc); const wchar_t *path);
/// Parse and load an ELF file in memory into a loaded image. /// Parse and load an ELF file in memory into a loaded image.
/// \arg disk The opened UEFI filesystem to load from /// \arg disk The opened UEFI filesystem to load from
/// \arg desc The descriptor identifying the program /// \arg desc The descriptor identifying the program
/// \arg add_module Also create a module for this loaded program /// \arg name The human-readable name of the program to load
bootproto::program * bootproto::program *
load_program( load_program(
fs::file &disk, fs::file &disk,
const descriptor &desc, const wchar_t *name,
bool add_module = false); const descriptor &desc);
/// Load a file from disk into memory, creating an init args module /// Load a file from disk into memory, creating an init args module
/// \arg disk The opened UEFI filesystem to load from /// \arg disk The opened UEFI filesystem to load from
/// \arg desc The program descriptor identifying the file /// \arg name The human-readable name of the module
/// \arg path The path of the file to load the module from
/// \arg type The major type to set on the module
/// \arg subtype The subtype to set on the module
void void
load_module( load_module(
fs::file &disk, fs::file &disk,
const descriptor &desc); const wchar_t *name,
const wchar_t *path,
bootproto::module_type type,
uint16_t subtype);
/// Verify that a loaded ELF has the j6 kernel header /// Verify that a loaded ELF has the j6 kernel header
/// \arg program The program to check for a header /// \arg program The program to check for a header

View File

@@ -75,48 +75,36 @@ load_resources(bootproto::args *args, video::screen *screen, uefi::handle image,
fs::file bc_data = disk.open(L"jsix_boot.dat"); fs::file bc_data = disk.open(L"jsix_boot.dat");
bootconfig bc {bc_data.load(), bs}; bootconfig bc {bc_data.load(), bs};
args->kernel = loader::load_program(disk, bc.kernel(), true); args->kernel = loader::load_program(disk, L"kernel", bc.kernel());
args->init = loader::load_program(disk, bc.init()); args->init = loader::load_program(disk, L"init server", bc.init());
args->flags = static_cast<bootproto::boot_flags>(bc.flags()); args->flags = static_cast<bootproto::boot_flags>(bc.flags());
loader::load_module(disk, L"initrd", bc.initrd(),
bootproto::module_type::initrd, bc.initrd_format());
namespace bits = util::bits; namespace bits = util::bits;
using bootproto::desc_flags; using bootproto::desc_flags;
if (screen) { if (screen) {
video::make_module(screen); video::make_module(screen);
// Go through the screen-specific descriptors first to // Find the screen-specific panic handler first to
// give them priority // give it priority
for (const descriptor &d : bc.programs()) { for (const descriptor &d : bc.panics()) {
if (!bits::has(d.flags, desc_flags::graphical)) if (bits::has(d.flags, desc_flags::graphical)) {
continue; args->panic = loader::load_program(disk, L"panic handler", d);
break;
if (bits::has(d.flags, desc_flags::panic)) }
args->panic = loader::load_program(disk, d);
else
loader::load_module(disk, d);
} }
} }
// Load the non-graphical descriptors if (!args->panic) {
for (const descriptor &d : bc.programs()) { for (const descriptor &d : bc.panics()) {
if (bits::has(d.flags, desc_flags::graphical)) if (!bits::has(d.flags, desc_flags::graphical)) {
continue; args->panic = loader::load_program(disk, L"panic handler", d);
break;
if (bits::has(d.flags, desc_flags::panic) && !args->panic) }
args->panic = loader::load_program(disk, d); }
else
loader::load_module(disk, d);
}
// 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 = symbol_table.pointer;
break;
} }
loader::verify_kernel_header(*args->kernel); loader::verify_kernel_header(*args->kernel);

View File

@@ -4,6 +4,7 @@
#include <stdint.h> #include <stdint.h>
#include <uefi/types.h> #include <uefi/types.h>
#include "video.h"
namespace uefi { namespace uefi {
struct boot_services; struct boot_services;
@@ -11,10 +12,6 @@ namespace uefi {
namespace boot { namespace boot {
namespace video {
struct screen;
}
// Abstract base class for status reporters. // Abstract base class for status reporters.
class status class status
{ {

View File

@@ -2,6 +2,8 @@
#include <uefi/graphics.h> #include <uefi/graphics.h>
#include <uefi/protos/graphics_output.h> #include <uefi/protos/graphics_output.h>
#include <bootproto/init.h>
#include "allocator.h" #include "allocator.h"
#include "console.h" #include "console.h"
#include "error.h" #include "error.h"
@@ -10,10 +12,7 @@
namespace boot { namespace boot {
namespace video { namespace video {
using bootproto::fb_layout; using bootproto::devices::fb_layout;
using bootproto::fb_type;
using bootproto::module_flags;
using bootproto::module_framebuffer;
using bootproto::module_type; using bootproto::module_type;
static uefi::protos::graphics_output * static uefi::protos::graphics_output *
@@ -70,7 +69,7 @@ pick_mode(uefi::boot_services *bs)
.vertical = gop->mode->info->vertical_resolution, .vertical = gop->mode->info->vertical_resolution,
.horizontal = gop->mode->info->horizontal_resolution, .horizontal = gop->mode->info->horizontal_resolution,
.scanline = gop->mode->info->pixels_per_scanline, .scanline = gop->mode->info->pixels_per_scanline,
.layout = layout::unknown, .layout = fb_layout::unknown,
}; };
s->framebuffer = { s->framebuffer = {
@@ -82,12 +81,12 @@ pick_mode(uefi::boot_services *bs)
switch (info->pixel_format) { switch (info->pixel_format) {
case uefi::pixel_format::rgb8: case uefi::pixel_format::rgb8:
type = L"rgb8"; type = L"rgb8";
s->mode.layout = layout::rgb8; s->mode.layout = fb_layout::rgb8;
break; break;
case uefi::pixel_format::bgr8: case uefi::pixel_format::bgr8:
type = L"bgr8"; type = L"bgr8";
s->mode.layout = layout::bgr8; s->mode.layout = fb_layout::bgr8;
break; break;
default: default:
@@ -108,13 +107,22 @@ pick_mode(uefi::boot_services *bs)
void void
make_module(screen *s) make_module(screen *s)
{ {
using bootproto::module_framebuffer; using bootproto::module;
module_framebuffer *modfb = g_alloc.allocate_module<module_framebuffer>(); using bootproto::module_type;
modfb->mod_type = module_type::framebuffer; using bootproto::device_type;
modfb->type = fb_type::uefi; using bootproto::devices::uefi_fb;
modfb->framebuffer = s->framebuffer; uefi_fb *fb = new uefi_fb;
modfb->mode = s->mode; fb->framebuffer = s->framebuffer;
fb->mode = s->mode;
module *mod = g_alloc.allocate_module();
mod->type = module_type::device;
mod->subtype = static_cast<uint16_t>(device_type::uefi_fb);
mod->data = {
.pointer = fb,
.count = sizeof(uefi_fb),
};
} }
} // namespace video } // namespace video

View File

@@ -5,7 +5,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
#include <bootproto/init.h> #include <bootproto/devices/framebuffer.h>
#include <util/counted.h> #include <util/counted.h>
namespace uefi { namespace uefi {
@@ -15,13 +15,8 @@ namespace uefi {
namespace boot { namespace boot {
namespace video { namespace video {
using bootproto::video_mode; using layout = bootproto::devices::fb_layout;
using layout = bootproto::fb_layout; using screen = bootproto::devices::uefi_fb;
struct screen {
util::buffer framebuffer;
video_mode mode;
};
/// Pick the best video mode and set up the screen /// Pick the best video mode and set up the screen
screen * pick_mode(uefi::boot_services *bs); screen * pick_mode(uefi::boot_services *bs);

View File

@@ -4,6 +4,7 @@ bp = module("bootproto",
kind = "lib", kind = "lib",
public_headers = [ public_headers = [
"bootproto/bootconfig.h", "bootproto/bootconfig.h",
"bootproto/devices/framebuffer.h",
"bootproto/init.h", "bootproto/init.h",
"bootproto/kernel.h", "bootproto/kernel.h",
"bootproto/memory.h.cog", "bootproto/memory.h.cog",

View File

@@ -8,9 +8,9 @@
namespace bootproto { namespace bootproto {
enum class desc_flags : uint16_t { enum class desc_flags : uint16_t {
graphical = 0x01, graphical = 0x0001,
panic = 0x02, panic = 0x0002,
symbols = 0x04, symbols = 0x0004,
}; };
is_bitfield(desc_flags); is_bitfield(desc_flags);

View File

@@ -0,0 +1,28 @@
#pragma once
/// \file bootproto/devices/uefi_fb.h
/// Data structures describing bootloader-passed framebuffer
#include <util/counted.h>
namespace bootproto {
namespace devices {
enum class fb_layout : uint8_t { rgb8, bgr8, unknown = 0xff };
struct video_mode
{
uint32_t vertical;
uint32_t horizontal;
uint32_t scanline;
fb_layout layout;
};
struct uefi_fb
{
util::buffer framebuffer;
video_mode mode;
};
} // namespace devices
} // namespace bootproto

View File

@@ -10,61 +10,30 @@
namespace bootproto { namespace bootproto {
enum class module_type : uint8_t { enum class module_type : uint8_t { none, initrd, device, };
none, enum class initrd_format : uint8_t { none, zstd, };
program, enum class device_type : uint16_t { none, uefi_fb, };
framebuffer,
};
enum class module_flags : uint8_t {
none = 0x00,
/// This module was already handled by the bootloader,
/// no action is needed. The module is included for
/// informational purposes only.
no_load = 0x01,
};
is_bitfield(module_flags);
struct module struct module
{ {
module_type mod_type; module_type type;
module_flags mod_flags; // 1 byte padding
uint32_t mod_length; uint16_t subtype;
// 4 bytes padding
util::buffer data;
}; };
struct module_program : struct module_page_header
public module
{
uintptr_t base_address;
size_t size;
char filename[];
};
enum class fb_layout : uint8_t { rgb8, bgr8, unknown = 0xff };
enum class fb_type : uint8_t { uefi };
struct video_mode
{
uint32_t vertical;
uint32_t horizontal;
uint32_t scanline;
fb_layout layout;
};
struct module_framebuffer :
public module
{
util::buffer framebuffer;
video_mode mode;
fb_type type;
};
struct modules_page
{ {
uint8_t count; uint8_t count;
module *modules;
uintptr_t next; uintptr_t next;
}; };
struct modules_page :
public module_page_header
{
static constexpr unsigned per_page = (0x1000 - sizeof(module_page_header)) / sizeof(module);
module modules[per_page];
};
} // namespace bootproto } // namespace bootproto

View File

@@ -4,6 +4,7 @@ module("drv.uart",
targets = [ "user" ], targets = [ "user" ],
deps = [ "libc", "util" ], deps = [ "libc", "util" ],
description = "UART driver", description = "UART driver",
drivers = [ "pc.uart" ],
sources = [ sources = [
"io.cpp", "io.cpp",
"main.cpp", "main.cpp",

View File

@@ -4,6 +4,7 @@ module("drv.uefi_fb",
targets = [ "user" ], targets = [ "user" ],
deps = [ "libc" ], deps = [ "libc" ],
description = "UEFI framebuffer driver", description = "UEFI framebuffer driver",
drivers = [ "uefi.fb" ],
sources = [ sources = [
"font.cpp", "font.cpp",
"main.cpp", "main.cpp",

View File

@@ -9,8 +9,9 @@
#include <j6/syscalls.h> #include <j6/syscalls.h>
#include <util/enum_bitfields.h> #include <util/enum_bitfields.h>
using bootproto::module_flags; #include "loader.h"
using bootproto::module_program;
using bootproto::module;
extern j6_handle_t __handle_self; extern j6_handle_t __handle_self;
@@ -18,54 +19,62 @@ constexpr uintptr_t load_addr = 0xf8000000;
constexpr size_t stack_size = 0x10000; constexpr size_t stack_size = 0x10000;
constexpr uintptr_t stack_top = 0x80000000000; constexpr uintptr_t stack_top = 0x80000000000;
j6_handle_t
map_phys(j6_handle_t sys, uintptr_t phys, size_t len, uintptr_t addr)
{
j6_handle_t vma = j6_handle_invalid;
j6_status_t res = j6_system_map_phys(sys, &vma, phys, len, 0);
if (res != j6_status_ok)
return j6_handle_invalid;
if (!addr)
addr = phys;
res = j6_vma_map(vma, __handle_self, addr);
if (res != j6_status_ok)
return j6_handle_invalid;
return vma;
}
bool bool
load_program( load_program(
const module_program &prog, const char *name,
uintptr_t base_address,
size_t size,
j6_handle_t sys, j6_handle_t slp, j6_handle_t sys, j6_handle_t slp,
char *err_msg) char *err_msg)
{ {
if (prog.mod_flags && module_flags::no_load) { j6_handle_t elf_vma = map_phys(sys, base_address, size);
sprintf(err_msg, " skipping pre-loaded program module '%s' at %lx", prog.filename, prog.base_address); if (elf_vma == j6_handle_invalid) {
return true; sprintf(err_msg, " ** error loading program '%s': creating physical vma", name);
}
j6_handle_t elf_vma = j6_handle_invalid;
j6_status_t res = j6_system_map_phys(sys, &elf_vma, prog.base_address, prog.size, 0);
if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': creating physical vma: %lx", prog.filename, res);
return false; return false;
} }
res = j6_vma_map(elf_vma, __handle_self, prog.base_address); const void *addr = reinterpret_cast<const void *>(base_address);
if (res != j6_status_ok) { elf::file progelf {addr, size};
sprintf(err_msg, " ** error loading program '%s': mapping vma: %lx", prog.filename, res);
return false;
}
const void *addr = reinterpret_cast<const void *>(prog.base_address);
elf::file progelf {addr, prog.size};
if (!progelf.valid()) { if (!progelf.valid()) {
sprintf(err_msg, " ** error loading program '%s': ELF is invalid", prog.filename); sprintf(err_msg, " ** error loading program '%s': ELF is invalid", name);
return false; return false;
} }
j6_handle_t proc = j6_handle_invalid; j6_handle_t proc = j6_handle_invalid;
res = j6_process_create(&proc); j6_status_t res = j6_process_create(&proc);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': creating process: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': creating process: %lx", name, res);
return false; return false;
} }
res = j6_process_give_handle(proc, sys); res = j6_process_give_handle(proc, sys);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': giving system handle: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': giving system handle: %lx", name, res);
return false; return false;
} }
res = j6_process_give_handle(proc, slp); res = j6_process_give_handle(proc, slp);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': giving SLP handle: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': giving SLP handle: %lx", name, res);
return false; return false;
} }
@@ -79,14 +88,14 @@ load_program(
if (seg.flags && elf::segment_flags::exec) if (seg.flags && elf::segment_flags::exec)
flags |= j6_vm_flag_exec; flags |= j6_vm_flag_exec;
uintptr_t start = prog.base_address + seg.offset; uintptr_t start = base_address + seg.offset;
size_t prologue = start & 0xfff; size_t prologue = start & 0xfff;
size_t epilogue = seg.mem_size - (prologue+seg.file_size); size_t epilogue = seg.mem_size - (prologue+seg.file_size);
j6_handle_t sub_vma = j6_handle_invalid; j6_handle_t sub_vma = j6_handle_invalid;
res = j6_vma_create_map(&sub_vma, seg.mem_size+prologue, load_addr, flags); res = j6_vma_create_map(&sub_vma, seg.mem_size+prologue, load_addr, flags);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': creating sub vma: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': creating sub vma: %lx", name, res);
return false; return false;
} }
@@ -98,13 +107,13 @@ load_program(
res = j6_vma_map(sub_vma, proc, seg.vaddr & ~0xfffull); res = j6_vma_map(sub_vma, proc, seg.vaddr & ~0xfffull);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': mapping sub vma to child: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': mapping sub vma to child: %lx", name, res);
return false; return false;
} }
res = j6_vma_unmap(sub_vma, __handle_self); res = j6_vma_unmap(sub_vma, __handle_self);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': unmapping sub vma: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': unmapping sub vma: %lx", name, res);
return false; return false;
} }
} }
@@ -112,7 +121,7 @@ load_program(
j6_handle_t stack_vma = j6_handle_invalid; j6_handle_t stack_vma = j6_handle_invalid;
res = j6_vma_create_map(&stack_vma, stack_size, load_addr, j6_vm_flag_write); res = j6_vma_create_map(&stack_vma, stack_size, load_addr, j6_vm_flag_write);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': creating stack vma: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': creating stack vma: %lx", name, res);
return false; return false;
} }
@@ -121,26 +130,26 @@ load_program(
res = j6_vma_map(stack_vma, proc, stack_top-stack_size); res = j6_vma_map(stack_vma, proc, stack_top-stack_size);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': mapping stack vma: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': mapping stack vma: %lx", name, res);
return false; return false;
} }
res = j6_vma_unmap(stack_vma, __handle_self); res = j6_vma_unmap(stack_vma, __handle_self);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': unmapping stack vma: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': unmapping stack vma: %lx", name, res);
return false; return false;
} }
j6_handle_t thread = j6_handle_invalid; j6_handle_t thread = j6_handle_invalid;
res = j6_thread_create(&thread, proc, stack_top - 6*sizeof(uint64_t), progelf.entrypoint()); res = j6_thread_create(&thread, proc, stack_top - 6*sizeof(uint64_t), progelf.entrypoint());
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': creating thread: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': creating thread: %lx", name, res);
return false; return false;
} }
res = j6_vma_unmap(elf_vma, __handle_self); res = j6_vma_unmap(elf_vma, __handle_self);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': unmapping elf vma: %lx", prog.filename, res); sprintf(err_msg, " ** error loading program '%s': unmapping elf vma: %lx", name, res);
return false; return false;
} }

View File

@@ -12,3 +12,9 @@ bool load_program(
const bootproto::module_program &prog, const bootproto::module_program &prog,
j6_handle_t sys, j6_handle_t slp, j6_handle_t sys, j6_handle_t slp,
char *err_msg); char *err_msg);
j6_handle_t map_phys(j6_handle_t sys, uintptr_t phys, size_t len, uintptr_t addr = 0);
inline j6_handle_t map_phys(j6_handle_t sys, void *phys, size_t len, uintptr_t addr = 0) {
return map_phys(sys, reinterpret_cast<uintptr_t>(phys), len, addr);
}

View File

@@ -1,5 +1,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <vector>
#include <j6/cap_flags.h> #include <j6/cap_flags.h>
#include <j6/errors.h> #include <j6/errors.h>
@@ -10,11 +11,11 @@
#include "loader.h" #include "loader.h"
#include "modules.h" #include "modules.h"
#include "ramdisk.h"
#include "service_locator.h" #include "service_locator.h"
using bootproto::module; using bootproto::module;
using bootproto::module_type; using bootproto::module_type;
using bootproto::module_program;
extern "C" { extern "C" {
int main(int, const char **); int main(int, const char **);
@@ -61,19 +62,38 @@ main(int argc, const char **argv)
modules mods = modules::load_modules(_arg_modules_phys, sys, __handle_self); modules mods = modules::load_modules(_arg_modules_phys, sys, __handle_self);
for (auto &mod : mods.of_type(module_type::program)) { module const *initrd_module;
auto &prog = static_cast<const module_program&>(mod); std::vector<module const*> devices;
char message[100]; for (auto &mod : mods) {
sprintf(message, " loading program module '%s' at %lx", prog.filename, prog.base_address); switch (mod.type) {
j6_log(message); case module_type::initrd:
initrd_module = &mod;
break;
if (!load_program(prog, sys_child, slp_mb_child, message)) { case module_type::device:
j6_log(message); devices.push_back(&mod);
return 2; break;
default:
// Unknown module??
break;
} }
} }
if (!initrd_module)
return 1;
j6_handle_t initrd_vma =
map_phys(sys, initrd_module->data.pointer, initrd_module->data.count);
if (initrd_vma == j6_handle_invalid) {
j6_log(" ** error loading ramdisk: mapping physical vma");
return 1;
}
ramdisk initrd {initrd_module->data};
util::buffer manifest = initrd.load_file("init.manifest");
service_locator_start(slp_mb); service_locator_start(slp_mb);
return 0; return 0;
} }

View File

@@ -20,28 +20,24 @@ const module *
module_iterator::operator++() module_iterator::operator++()
{ {
do { do {
m_mod = util::offset_pointer(m_mod, m_mod->mod_length); if (++m_idx >= modules_page::per_page) {
if (m_mod->mod_type == type::none) {
// We've reached the end of a page, see if there's another // We've reached the end of a page, see if there's another
const modules_page *page = get_page(m_mod); if (!m_page->next) {
if (!page->next) { m_page = nullptr;
m_mod = nullptr;
break; break;
} }
m_idx = 0;
m_mod = page->modules; m_page = reinterpret_cast<modules_page*>(m_page->next);
} }
} }
while (m_type != type::none && m_type != m_mod->mod_type); while (m_type != type::none && m_type != deref()->type);
return deref();
return m_mod;
} }
const module * const module *
module_iterator::operator++(int) module_iterator::operator++(int)
{ {
const module *tmp = m_mod; const module *tmp = deref();
operator++(); operator++();
return tmp; return tmp;
} }
@@ -64,7 +60,7 @@ load_page(uintptr_t address, j6_handle_t system, j6_handle_t self)
modules modules
modules::load_modules(uintptr_t address, j6_handle_t system, j6_handle_t self) modules::load_modules(uintptr_t address, j6_handle_t system, j6_handle_t self)
{ {
const module *first = nullptr; const modules_page *first = nullptr;
while (address) { while (address) {
const modules_page *page = load_page(address, system, self); const modules_page *page = load_page(address, system, self);
@@ -73,7 +69,7 @@ modules::load_modules(uintptr_t address, j6_handle_t system, j6_handle_t self)
j6_log(message); j6_log(message);
if (!first) if (!first)
first = page->modules; first = page;
address = page->next; address = page->next;
} }

View File

@@ -13,27 +13,32 @@ public:
using type = bootproto::module_type; using type = bootproto::module_type;
using module = bootproto::module; using module = bootproto::module;
module_iterator(const module *m, type t = type::none) : module_iterator(const bootproto::modules_page *p, unsigned i, type t = type::none) :
m_mod {m}, m_type {t} {} m_page {p}, m_idx {i}, m_type {t} {
if ( t != type::none && p && deref()->type != t ) operator++();
}
const module * operator++(); const module * operator++();
const module * operator++(int); const module * operator++(int);
bool operator==(const module* m) const { return m == m_mod; } bool operator==(const module* m) const { return m == deref(); }
bool operator!=(const module* m) const { return m != m_mod; } bool operator!=(const module* m) const { return m != deref(); }
bool operator==(const module_iterator &i) const { return i.m_mod == m_mod; } bool operator==(const module_iterator &i) const { return i.deref() == deref(); }
bool operator!=(const module_iterator &i) const { return i.m_mod != m_mod; } bool operator!=(const module_iterator &i) const { return i.deref() != deref(); }
const module & operator*() const { return *m_mod; } const module & operator*() const { return *deref(); }
operator const module & () const { return *m_mod; } operator const module & () const { return *deref(); }
const module * operator->() const { return m_mod; } const module * operator->() const { return deref(); }
// Allow iterators to be used in for(:) statments // Allow iterators to be used in for(:) statments
module_iterator & begin() { return *this; } module_iterator & begin() { return *this; }
module_iterator end() const { return nullptr; } module_iterator end() const { return {nullptr, 0}; }
private: private:
module const * m_mod; inline const module * deref() const { return m_page ? &m_page->modules[m_idx] : nullptr; }
unsigned m_idx;
bootproto::modules_page const *m_page;
type m_type; type m_type;
}; };
@@ -48,15 +53,15 @@ public:
j6_handle_t system, j6_handle_t system,
j6_handle_t self); j6_handle_t self);
iterator of_type(type t) const { return iterator {m_root, t}; } iterator of_type(type t) const { return iterator {m_root, 0, t}; }
iterator begin() const { return iterator {m_root}; } iterator begin() const { return iterator {m_root, 0}; }
iterator end() const { return nullptr; } iterator end() const { return {nullptr, 0}; }
private: private:
using module = bootproto::module; using module = bootproto::module;
modules(const module* root) : m_root {root} {} modules(const bootproto::modules_page* root) : m_root {root} {}
const module *m_root; const bootproto::modules_page *m_root;
}; };