mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[boot] Create bootconfig to tell boot what to load
While bonnibel already had the concept of a manifest, which controls what goes into the built disk image, the bootloader still had filenames hard-coded. Now bonnibel creates a 'jsix_boot.dat' file that tells the bootloader what it should load. Changes include: - Modules have two new fields: location and description. location is their intended directory on the EFI boot volume. description is self-explanatory, and is used in log messages. - New class, boot::bootconfig, implements reading of jsix_boot.dat - New header, bootproto/bootconfig.h, specifies flags used in the manifest and jsix_boot.dat - New python module, bonnibel/manifest.py, encapsulates reading of the manifest and writing jsix_boot.dat - Syntax of the manifest changed slightly, including adding flags - Boot and Kernel target ccflags unified a bit (this was partly due to trying to get enum_bitfields to work in boot) - util::counted gained operator+= and new free function util::read<T>
This commit is contained in:
94
scripts/bonnibel/manifest.py
Normal file
94
scripts/bonnibel/manifest.py
Normal file
@@ -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("<H", (len(s)+1)*2))
|
||||
outfile.write(s.encode("utf-16le"))
|
||||
outfile.write(b"\0\0")
|
||||
|
||||
def write_ent(ent):
|
||||
flags = 0
|
||||
for f in ent.flags:
|
||||
flags |= Manifest.flags[f]
|
||||
|
||||
outfile.write(struct.pack("<H", flags))
|
||||
write_str(join(ent.location, ent.output).replace('/','\\'))
|
||||
write_str(ent.description)
|
||||
|
||||
write_ent(self.kernel)
|
||||
write_ent(self.init)
|
||||
|
||||
for p in self.programs:
|
||||
write_ent(p)
|
||||
|
||||
for d in self.data:
|
||||
write_ent(d)
|
||||
@@ -15,6 +15,8 @@ class Module:
|
||||
"sources": (tuple, ()),
|
||||
"variables": (dict, ()),
|
||||
"default": (bool, False),
|
||||
"location": (str, "jsix"),
|
||||
"description": (str, None),
|
||||
}
|
||||
|
||||
def __init__(self, name, modfile, root, **kwargs):
|
||||
|
||||
@@ -51,56 +51,57 @@ class Project:
|
||||
fatroot.mkdir(exist_ok=True)
|
||||
|
||||
fatroot_content = []
|
||||
def add_fatroot(name, target):
|
||||
if not name in modules:
|
||||
raise BonnibelError(f"Manifest item '{name}' is not a known module")
|
||||
|
||||
mod = modules[name]
|
||||
intermediary = f"${{build_root}}/{mod.output}"
|
||||
fatroot_output = f"${{build_root}}/fatroot/{mod.output}"
|
||||
|
||||
build.build(
|
||||
rule = "strip",
|
||||
outputs = [intermediary],
|
||||
inputs = [f"${{build_root}}/{target}/{mod.output}"],
|
||||
implicit = [f"${{build_root}}/{target}/{mod.output}.dump"],
|
||||
variables = {
|
||||
"name": f"Stripping {name}",
|
||||
"debug": f"${{build_root}}/.debug/{mod.output}.debug",
|
||||
})
|
||||
def add_fatroot(source, entry):
|
||||
output = join(entry.location, entry.output)
|
||||
fatroot_output = f"${{build_root}}/fatroot/{output}"
|
||||
|
||||
build.build(
|
||||
rule = "cp",
|
||||
outputs = [fatroot_output],
|
||||
inputs = [intermediary],
|
||||
inputs = [source],
|
||||
variables = {
|
||||
"name": f"Installing {name}",
|
||||
"name": f"Installing {output}",
|
||||
})
|
||||
|
||||
fatroot_content.append(fatroot_output)
|
||||
build.newline()
|
||||
|
||||
manifest = load_config(manifest_file)
|
||||
programs = manifest.get("programs", tuple())
|
||||
def add_fatroot_exe(entry):
|
||||
input_path = f"${{build_root}}/{entry.target}/{entry.output}"
|
||||
intermediary = f"${{build_root}}/{entry.output}"
|
||||
|
||||
kernel = manifest.get("kernel", dict())
|
||||
add_fatroot(
|
||||
kernel.get("name", "kernel"),
|
||||
kernel.get("target", "kernel"))
|
||||
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",
|
||||
})
|
||||
|
||||
for program in programs:
|
||||
name = program["name"]
|
||||
target = program.get("target", "user")
|
||||
add_fatroot(name, target)
|
||||
add_fatroot(intermediary, entry)
|
||||
return mod.location
|
||||
|
||||
symbol_table = "${build_root}/fatroot/symbol_table.dat"
|
||||
from .manifest import Manifest
|
||||
manifest = Manifest(manifest_file, modules)
|
||||
|
||||
add_fatroot_exe(manifest.kernel)
|
||||
add_fatroot_exe(manifest.init)
|
||||
for program in manifest.programs:
|
||||
add_fatroot_exe(program)
|
||||
|
||||
syms = manifest.add_data("symbol_table.dat",
|
||||
manifest.kernel.location, "Symbol table", ("symbols",))
|
||||
|
||||
sym_out = f"${{build_root}}/symbol_table.dat"
|
||||
build.build(
|
||||
rule = "makest",
|
||||
outputs = [symbol_table],
|
||||
inputs = ["${build_root}/fatroot/jsix.elf"],
|
||||
outputs = [sym_out],
|
||||
inputs = [f"${{build_root}}/{modules['kernel'].output}"],
|
||||
)
|
||||
fatroot_content.append(symbol_table)
|
||||
build.newline()
|
||||
add_fatroot(sym_out, syms)
|
||||
|
||||
bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi"
|
||||
build.build(
|
||||
@@ -110,14 +111,16 @@ class Project:
|
||||
variables = {
|
||||
"name": "Installing bootloader",
|
||||
})
|
||||
fatroot_content.append(bootloader)
|
||||
build.newline()
|
||||
|
||||
boot_config = join(fatroot, "jsix_boot.dat")
|
||||
manifest.write_boot_config(boot_config)
|
||||
|
||||
build.build(
|
||||
rule = "makefat",
|
||||
outputs = ["${build_root}/jsix.img"],
|
||||
inputs = ["${source_root}/assets/diskbase.img"],
|
||||
implicit = fatroot_content,
|
||||
implicit = fatroot_content + [bootloader],
|
||||
variables = {"name": "jsix.img"},
|
||||
)
|
||||
build.newline()
|
||||
@@ -159,7 +162,8 @@ class Project:
|
||||
implicit = regen_implicits,
|
||||
implicit_outputs =
|
||||
[f"{mod.name}.ninja" for mod in modules.values()] +
|
||||
[f"{target.name}/target.ninja" for target in targets],
|
||||
[f"{target.name}/target.ninja" for target in targets] +
|
||||
[boot_config],
|
||||
)
|
||||
|
||||
build.newline()
|
||||
|
||||
Reference in New Issue
Block a user