[kernel] Add test mode, controlled by manifest

The manifest can now supply a list of boot flags, including "test".
Those get turned into the bootproto::args::flags field by the
bootloader. The kernel takes those and uses the test flag to control
enabling syscalls with the new "test" attribute, like the new
test_finish syscall, which lets automated tests call back to the kernel
to shut down the system.
This commit is contained in:
Justin C. Miller
2022-02-03 19:45:46 -08:00
parent 401e662f0b
commit 0e80c19d3d
12 changed files with 59 additions and 11 deletions

View File

@@ -0,0 +1,8 @@
---
init: srv.init
flags: ["test"]
programs:
- name: panic.serial
target: kernel
flags: panic
- name: drv.uart

View File

@@ -42,4 +42,9 @@ interface syscalls [syscall] {
param clone ref object [out] # The new handle param clone ref object [out] # The new handle
param mask uint32 # The capability bitmask param mask uint32 # The capability bitmask
} }
# Testing mode only: Have the kernel finish and exit QEMU with the given exit code
function test_finish [test] {
param exit_code uint32
}
} }

View File

@@ -10,6 +10,11 @@ class Manifest:
"symbols": 0x04, "symbols": 0x04,
} }
boot_flags = {
"debug": 0x01,
"test": 0x02,
}
def __init__(self, path, modules): def __init__(self, path, modules):
from . import load_config from . import load_config
@@ -25,6 +30,8 @@ class Manifest:
self.programs = [self.__build_entry(modules, i) self.programs = [self.__build_entry(modules, i)
for i in config.get("programs", tuple())] for i in config.get("programs", tuple())]
self.flags = config.get("flags", tuple())
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)
@@ -65,10 +72,12 @@ class Manifest:
version = 0 version = 0
reserved = 0 reserved = 0
bootflags = sum([Manifest.boot_flags.get(s, 0) for s in self.flags])
outfile.write(struct.pack("<8sBBHHH", outfile.write(struct.pack("<8sBBHHH",
magic, version, reserved, magic, version, reserved,
len(self.programs), len(self.data), len(self.programs), len(self.data),
reserved)) bootflags))
def write_str(s): def write_str(s):
outfile.write(struct.pack("<H", (len(s)+1)*2)) outfile.write(struct.pack("<H", (len(s)+1)*2))

View File

@@ -41,7 +41,8 @@ bootconfig::bootconfig(util::buffer data, uefi::boot_services *bs)
data += 1; // reserved byte data += 1; // reserved byte
uint16_t num_programs = *util::read<uint16_t>(data); uint16_t num_programs = *util::read<uint16_t>(data);
uint16_t num_data = *util::read<uint16_t>(data); uint16_t num_data = *util::read<uint16_t>(data);
data += 2; // reserved short
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);

View File

@@ -28,12 +28,14 @@ public:
/// Constructor. Loads bootconfig from the given buffer. /// Constructor. Loads bootconfig from the given buffer.
bootconfig(util::buffer data, uefi::boot_services *bs); bootconfig(util::buffer data, uefi::boot_services *bs);
inline const descriptor & kernel() { return m_kernel; } inline uint16_t flags() const { return m_flags; }
inline const descriptor & init() { return m_init; } inline const descriptor & kernel() const { return m_kernel; }
inline const descriptor & init() const { return m_init; }
descriptors programs() { return m_programs; } descriptors programs() { return m_programs; }
descriptors data() { return m_data; } descriptors data() { return m_data; }
private: private:
uint16_t m_flags;
descriptor m_kernel; descriptor m_kernel;
descriptor m_init; descriptor m_init;
descriptors m_programs; descriptors m_programs;

View File

@@ -77,6 +77,7 @@ load_resources(bootproto::args *args, video::screen *screen, uefi::handle image,
args->kernel = loader::load_program(disk, bc.kernel(), true); args->kernel = loader::load_program(disk, bc.kernel(), true);
args->init = loader::load_program(disk, bc.init()); args->init = loader::load_program(disk, bc.init());
args->flags = static_cast<bootproto::boot_flags>(bc.flags());
namespace bits = util::bits; namespace bits = util::bits;
using bootproto::desc_flags; using bootproto::desc_flags;

View File

@@ -98,8 +98,6 @@ bsp_late_init()
asm ("mov %%cr4, %0" : "=r"(cr4)); asm ("mov %%cr4, %0" : "=r"(cr4));
uint64_t efer = rdmsr(msr::ia32_efer); uint64_t efer = rdmsr(msr::ia32_efer);
log::debug(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx", cr0, cr4, efer); log::debug(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx", cr0, cr4, efer);
syscall_initialize();
} }
cpu_data * cpu_data *

View File

@@ -18,6 +18,7 @@
#include "objects/vm_area.h" #include "objects/vm_area.h"
#include "scheduler.h" #include "scheduler.h"
#include "smp.h" #include "smp.h"
#include "syscall.h"
#include "sysconf.h" #include "sysconf.h"
extern "C" { extern "C" {
@@ -55,6 +56,10 @@ kernel_main(bootproto::args *args)
bsp_late_init(); bsp_late_init();
using bootproto::boot_flags;
bool enable_test = util::bits::has(args->flags, boot_flags::test);
syscall_initialize(enable_test);
device_manager &devices = device_manager::get(); device_manager &devices = device_manager::get();
devices.parse_acpi(args->acpi_table); devices.parse_acpi(args->acpi_table);

View File

@@ -66,7 +66,7 @@ syscall_invalid(uint64_t call)
} }
void void
syscall_initialize() syscall_initialize(bool enable_test)
{ {
memset(&syscall_registry, 0, sizeof(syscall_registry)); memset(&syscall_registry, 0, sizeof(syscall_registry));
@@ -77,8 +77,17 @@ syscall_initialize()
else: else:
name = method.name name = method.name
cog.outl(f"syscall_registry[{id}] = reinterpret_cast<uintptr_t>(syscalls::_syscall_verify_{name});") indent = ""
cog.outl(f"""log::debug(logs::syscall, "Enabling syscall {id:02x} as {name}");""") if "test" in method.options:
cog.outl("if (enable_test) {")
indent = " "
cog.outl(f"{indent}syscall_registry[{id}] = reinterpret_cast<uintptr_t>(syscalls::_syscall_verify_{name});")
cog.outl(f"""{indent}log::debug(logs::syscall, "Enabling syscall {id:02x} as {name}");""")
if "test" in method.options:
cog.outl("}")
cog.outl("") cog.outl("")
]]]*/ ]]]*/
//[[[end]]] //[[[end]]]

View File

@@ -17,5 +17,5 @@ cog.outl(f"constexpr size_t num_syscalls = {len(syscalls.methods)};")
]]]*/ ]]]*/
/// [[[end]]] /// [[[end]]]
void syscall_initialize(); void syscall_initialize(bool enable_test);
extern "C" void syscall_enable(); extern "C" void syscall_enable();

View File

@@ -36,6 +36,14 @@ noop()
return j6_status_ok; return j6_status_ok;
} }
[[ noreturn ]] j6_status_t
test_finish(uint32_t exit_code)
{
// Tell QEMU to exit
asm ( "out %0, %1" :: "a"(exit_code), "Nd"(0xf4) );
while (1) asm ("hlt");
}
j6_status_t j6_status_t
system_get_log(system *self, void *buffer, size_t *buffer_len) system_get_log(system *self, void *buffer, size_t *buffer_len)
{ {

View File

@@ -114,8 +114,10 @@ struct frame_block
enum class boot_flags : uint16_t { enum class boot_flags : uint16_t {
none = 0x0000, none = 0x0000,
debug = 0x0001 debug = 0x0001,
test = 0x0002,
}; };
is_bitfield(boot_flags);
struct args struct args
{ {