[init] Load uart and logger from initrd

Load drv.uart.elf and srv.logger.elf from the initrd and start them.
It's extremely manual and hard-coded at the moment, but it works and
they run, getting us back to where we were pre-initrd branch.
This commit is contained in:
Justin C. Miller
2023-01-29 21:35:12 -08:00
parent 933e4e8040
commit 8b29680850
9 changed files with 235 additions and 114 deletions

View File

@@ -5,10 +5,10 @@ init = module("srv.init",
deps = [ "libc", "elf", "bootproto", "zstd" ], deps = [ "libc", "elf", "bootproto", "zstd" ],
description = "Init server", description = "Init server",
sources = [ sources = [
"j6romfs.cpp",
"loader.cpp", "loader.cpp",
"main.cpp", "main.cpp",
"modules.cpp", "modules.cpp",
"ramdisk.cpp",
"service_locator.cpp", "service_locator.cpp",
"start.s", "start.s",
]) ])

View File

@@ -0,0 +1,115 @@
#include <assert.h>
#include <string.h>
#include <util/hash.h>
#include <zstd.h>
#include "j6romfs.h"
namespace j6romfs {
namespace {
static constexpr unsigned buf_size = max_path + 1;
const char *
copy_path_element(const char *path, char buf[buf_size])
{
if (*path == '/') path++;
unsigned i = 0;
while (*path && *path != '/' && i < max_path)
buf[i++] = *path++;
buf[i] = 0;
return path;
}
} // anon namespace
fs::fs(util::const_buffer data) :
m_data {data}
{
const superblock *sb = reinterpret_cast<const superblock*>(data.pointer);
m_inodes = reinterpret_cast<const inode*>(
util::offset_pointer(data.pointer, sb->inode_offset));
m_root = &m_inodes[sb->root_inode];
}
util::const_buffer
fs::load_simple(char const *path) const
{
if (!path)
return {0, 0};
char element [buf_size];
inode const *in = m_root;
while (*path) {
path = copy_path_element(path, element);
in = lookup_inode_in_dir(in, element);
if (!in) {
// entry was not found
break;
} else if (*path && in->type == inode_type::file) {
// a directory was expected
break;
} else if (in->type == inode_type::file) {
// load the file
uint8_t *data = new uint8_t [in->size];
size_t total = load_inode_data(in, util::buffer::from(data, in->size));
return util::buffer::from(data, total);
}
}
return {0, 0};
}
size_t
fs::load_inode_data(const inode *in, util::buffer dest) const
{
util::const_buffer src = m_data + in->offset;
assert(dest.count >= in->size && "Dest buffer not big enough");
assert(src.count >= in->compressed && "Source buffer not big enough");
if (in->size == in->compressed) {
// If the sizes are equal, no compression happened
memcpy(dest.pointer, src.pointer, in->size);
return in->size;
}
size_t decom = ZSTD_decompress(dest.pointer, dest.count,
src.pointer, in->compressed);
assert(!ZSTD_isError(decom) && "Error decompressing");
return decom;
}
const inode *
fs::lookup_inode_in_dir(const inode *dir, const char *name) const
{
uint8_t *dirdata = new uint8_t [dir->size];
load_inode_data(dir, util::buffer::from(dirdata, dir->size));
const dirent *entries = reinterpret_cast<const dirent*>(dirdata);
inode const *found = nullptr;
uint64_t hash = util::fnv1a::hash64_string(name);
unsigned max = dir->size / sizeof(dirent);
for (unsigned i = 0; i < max; ++i) {
const dirent &e = entries[i];
if (e.name_hash == hash) {
found = &m_inodes[e.inode];
break;
}
unsigned new_max = e.name_offset / sizeof(dirent);
if (new_max < max)
max = new_max;
}
delete [] dirdata;
return found;
}
} // namespace j6romfs

View File

@@ -0,0 +1,63 @@
#pragma once
/// \file j6romfs.h
/// Data structure for dealing with j6romfs images
#include <util/counted.h>
#include <util/enum_bitfields.h>
namespace j6romfs
{
inline constexpr unsigned max_path = 0xff;
enum class compressor : uint8_t { none, zstd };
struct superblock
{
uint64_t magic;
uint64_t inode_offset;
uint32_t inode_count;
uint32_t root_inode;
compressor compressor;
uint8_t reserved[7];
};
enum class inode_type : uint8_t { none, directory, file, symlink, };
struct inode
{
uint32_t size;
uint32_t compressed : 24;
inode_type type : 8;
uint64_t offset;
};
struct dirent
{
uint32_t inode;
uint16_t name_offset;
inode_type type;
uint8_t name_len;
uint64_t name_hash;
};
class fs
{
public:
fs(util::const_buffer data);
util::const_buffer load_simple(char const *path) const;
private:
size_t load_inode_data(const inode *in, util::buffer dest) const;
const inode * lookup_inode_in_dir(const inode *in, const char *name) const;
util::const_buffer m_data;
inode const *m_inodes;
inode const *m_root;
};
} // namespace j6romfs

View File

@@ -40,19 +40,13 @@ map_phys(j6_handle_t sys, uintptr_t phys, size_t len, uintptr_t addr)
bool bool
load_program( load_program(
const char *name, const char *name,
uintptr_t base_address, util::const_buffer data,
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)
{ {
j6_handle_t elf_vma = map_phys(sys, base_address, size); uintptr_t base_address = reinterpret_cast<uintptr_t>(data.pointer);
if (elf_vma == j6_handle_invalid) {
sprintf(err_msg, " ** error loading program '%s': creating physical vma", name);
return false;
}
const void *addr = reinterpret_cast<const void *>(base_address); elf::file progelf {data.pointer, data.count};
elf::file progelf {addr, size};
if (!progelf.valid()) { if (!progelf.valid()) {
sprintf(err_msg, " ** error loading program '%s': ELF is invalid", name); sprintf(err_msg, " ** error loading program '%s': ELF is invalid", name);
@@ -89,7 +83,7 @@ load_program(
flags |= j6_vm_flag_exec; flags |= j6_vm_flag_exec;
uintptr_t start = base_address + seg.offset; uintptr_t start = base_address + seg.offset;
size_t prologue = start & 0xfff; size_t prologue = seg.vaddr & 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;
@@ -147,12 +141,7 @@ load_program(
return false; return false;
} }
res = j6_vma_unmap(elf_vma, __handle_self); delete [] reinterpret_cast<const uint8_t*>(data.pointer);
if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': unmapping elf vma: %lx", name, res);
return false;
}
return true; return true;
} }

View File

@@ -9,12 +9,13 @@ namespace bootproto {
} }
bool load_program( bool load_program(
const bootproto::module_program &prog, const char *name,
util::const_buffer data,
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); 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) { inline j6_handle_t map_phys(j6_handle_t sys, const void *phys, size_t len, uintptr_t addr = 0) {
return map_phys(sys, reinterpret_cast<uintptr_t>(phys), len, addr); return map_phys(sys, reinterpret_cast<uintptr_t>(phys), len, addr);
} }

View File

@@ -9,9 +9,9 @@
#include <j6/types.h> #include <j6/types.h>
#include <bootproto/init.h> #include <bootproto/init.h>
#include "j6romfs.h"
#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;
@@ -25,6 +25,24 @@ uintptr_t _arg_modules_phys; // This gets filled in in _start
extern j6_handle_t __handle_self; extern j6_handle_t __handle_self;
util::const_buffer
load_driver_for(const char *name, const j6romfs::fs &initrd)
{
char driver[256];
snprintf(driver, sizeof(driver), "/jsix/drivers/drv.%s.elf", name);
return initrd.load_simple(driver);
}
util::const_buffer
load_service(const char *name, const j6romfs::fs &initrd)
{
char service[256];
snprintf(service, sizeof(service), "/jsix/services/srv.%s.elf", name);
return initrd.load_simple(service);
}
int int
main(int argc, const char **argv) main(int argc, const char **argv)
{ {
@@ -91,8 +109,27 @@ main(int argc, const char **argv)
return 1; return 1;
} }
ramdisk initrd {initrd_module->data}; // TODO: encapsulate this all in a driver_manager, or maybe
util::buffer manifest = initrd.load_file("init.manifest"); // have driver_source objects..
j6romfs::fs initrd {initrd_module->data};
char err_msg [128];
util::const_buffer uart_elf = load_driver_for("uart", initrd);
if (uart_elf.pointer) {
if (!load_program("UART driver", uart_elf, sys_child, slp_mb_child, err_msg)) {
j6_log(err_msg);
return 1;
}
}
util::const_buffer logger_elf = load_service("logger", initrd);
if (uart_elf.pointer) {
if (!load_program("logger service", logger_elf, sys_child, slp_mb_child, err_msg)) {
j6_log(err_msg);
return 1;
}
}
service_locator_start(slp_mb); service_locator_start(slp_mb);
return 0; return 0;

View File

@@ -35,7 +35,14 @@ public:
module_iterator end() const { return {nullptr, 0}; } module_iterator end() const { return {nullptr, 0}; }
private: private:
inline const module * deref() const { return m_page ? &m_page->modules[m_idx] : nullptr; } inline const module * deref() const {
if (m_page) {
const module &m = m_page->modules[m_idx];
if (m.type != type::none)
return &m;
}
return nullptr;
}
unsigned m_idx; unsigned m_idx;
bootproto::modules_page const *m_page; bootproto::modules_page const *m_page;

View File

@@ -1,61 +0,0 @@
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <util/cdb.h>
#include <zstd.h>
#include "ramdisk.h"
inline constexpr uint64_t manifest_magic = 0x74696e697869736a; // "jsixinit"
inline constexpr size_t manifest_min = 18;
inline constexpr size_t manifest_version = 1;
using util::read;
ramdisk::ramdisk(util::buffer data) : m_data {data} {}
util::buffer
ramdisk::load_file(const char *name)
{
util::cdb cdb {m_data};
util::buffer c = cdb.retrieve(name);
if (!c.count)
return c;
size_t size = ZSTD_getFrameContentSize(c.pointer, c.count);
util::buffer d {malloc(size), size};
size_t out = ZSTD_decompress(
d.pointer, d.count,
c.pointer, c.count);
if (out != size) {
free(d.pointer);
return {0,0};
}
return d;
}
manifest::manifest(util::buffer data)
{
if (data.count < manifest_min)
return;
char const *base = reinterpret_cast<char const *>(data.pointer);
if (*read<uint64_t>(data) != manifest_magic)
return;
uint8_t version = *read<uint8_t>(data);
if (version != manifest_version)
return;
read<uint8_t>(data); // reserved byte
uint16_t services_len = *read<uint16_t>(data);
uint16_t drivers_len = *read<uint16_t>(data);
base += *read<uint16_t>(data); // start of the string section
}

View File

@@ -1,30 +0,0 @@
#pragma once
/// \file loader.h
/// Data structure for a ramdisk archive, based on djb's CDB format
#include <unordered_map>
#include <vector>
#include <j6/types.h>
#include <util/counted.h>
class ramdisk
{
public:
ramdisk(util::buffer data);
util::buffer load_file(const char *name);
private:
util::buffer m_data;
};
class manifest
{
public:
manifest(util::buffer data);
private:
std::vector<const char*> m_services;
std::unordered_map<const char*, const char*> m_drivers;
};