[srv.init] Create init server and read init args

Create a new usermode program, srv.init, and have it read the initial
module_page args sent to it by the bootloader. Doesn't yet do anything
useful but sets up the way for loading the rest of the programs from
srv.init.

Other (mostly) related changes:

- bootloader: The allocator now has a function for allocating init
  modules out of a modules_page slab. Also changed how the allocator is
  initialized and passes the allocation register and modules_page list
  to efi_main().
- bootloader: Expose the simple wstrlen() to the rest of the program
- bootloader: Move check_cpu_supported() to hardware.cpp
- bootloader: Moved program_desc to loader.h and made the loader
  functions take it as an argument instead of paths.
- kernel: Rename the system_map_mmio syscall to system_map_phys, and
  stop having it default those VMAs to having the vm_flags::mmio flag.
  Added a new flag mask, vm_flags::driver_mask, so that drivers can be
  allowed to ask for the MMIO flag.
- kernel: Rename load_simple_process() to load_init_server() and got rid
  of all the stack setup routines in memory_bootstrap.cpp and task.s
- Fixed formatting in config/debug.toml, undefined __linux and other
  linux-specific defines, and got rid of _LIBCPP_HAS_THREAD_API_EXTERNAL
  because that's just not true.
This commit is contained in:
Justin C. Miller
2021-07-31 10:00:08 -07:00
parent 1802c5ea2e
commit 5524ca5b25
28 changed files with 464 additions and 270 deletions

View File

@@ -4,6 +4,7 @@
#include "kutil/no_construct.h"
#include "allocator.h"
#include "error.h"
#include "init_args.h"
#include "kernel_args.h"
#include "memory.h"
@@ -15,23 +16,32 @@ memory::allocator &g_alloc = __g_alloc_storage.value;
namespace memory {
using kernel::init::allocation_register;
using kernel::init::module;
using kernel::init::page_allocation;
static_assert(sizeof(allocation_register) == page_size);
void
init_allocator(uefi::boot_services *bs)
allocator::init(
allocation_register *&allocs,
modules_page *&modules,
uefi::boot_services *bs)
{
new (&g_alloc) allocator(*bs);
allocs = g_alloc.m_register;
modules = g_alloc.m_modules;
}
allocator::allocator(uefi::boot_services &bs) :
m_bs(bs),
m_register(nullptr),
m_current(nullptr)
{}
m_modules(nullptr)
{
add_register();
add_modules();
}
void
allocator::add_register()
@@ -45,13 +55,25 @@ allocator::add_register()
m_bs.set_mem(reg, sizeof(allocation_register), 0);
if (!m_register) {
m_register = m_current = reg;
return;
}
if (m_register)
m_register->next = reg;
m_current->next = reg;
m_current = reg;
m_register = reg;
return;
}
void
allocator::add_modules()
{
modules_page *mods = reinterpret_cast<modules_page*>(
allocate_pages(1, alloc_type::init_args, true));
if (m_modules)
m_modules->next = reinterpret_cast<uintptr_t>(mods);
mods->modules = reinterpret_cast<module*>(mods + 1);
m_modules = mods;
m_next_mod = mods->modules;
return;
}
@@ -64,7 +86,7 @@ allocator::allocate_pages(size_t count, alloc_type type, bool zero)
__LINE__);
}
if (!m_current || m_current->count == 0xff)
if (!m_register || m_register->count == 0xff)
add_register();
void *pages = nullptr;
@@ -74,7 +96,7 @@ allocator::allocate_pages(size_t count, alloc_type type, bool zero)
uefi::memory_type::loader_data, count, &pages),
L"Failed allocating usable pages");
page_allocation &ent = m_current->entries[m_current->count++];
page_allocation &ent = m_register->entries[m_register->count++];
ent.address = reinterpret_cast<uintptr_t>(pages);
ent.count = count;
ent.type = type;
@@ -85,6 +107,24 @@ allocator::allocate_pages(size_t count, alloc_type type, bool zero)
return pages;
}
module *
allocator::allocate_module_untyped(size_t size)
{
size_t remaining =
reinterpret_cast<uintptr_t>(m_modules) + page_size
- reinterpret_cast<uintptr_t>(m_next_mod);
if (size > remaining)
add_modules();
++m_modules->count;
module *m = m_next_mod;
m_next_mod = offset_ptr<module>(m_next_mod, size);
m->mod_length = size;
return m;
}
void *
allocator::allocate(size_t size, bool zero)
{

View File

@@ -2,12 +2,18 @@
/// \file allocator.h
/// Page allocator class definition
#include "kernel_args.h"
namespace uefi {
class boot_services;
}
namespace kernel {
namespace init {
enum class allocation_type : uint8_t;
struct allocation_register;
struct module;
struct modules_page;
}}
namespace boot {
namespace memory {
@@ -17,6 +23,8 @@ class allocator
{
public:
using allocation_register = kernel::init::allocation_register;
using module = kernel::init::module;
using modules_page = kernel::init::modules_page;
allocator(uefi::boot_services &bs);
@@ -25,25 +33,37 @@ public:
void * allocate_pages(size_t count, alloc_type type, bool zero = false);
template <typename M>
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 copy(void *to, void *from, size_t size);
inline void zero(void *start, size_t size) { memset(start, size, 0); }
allocation_register * get_register() { return m_register; }
/// Initialize the global allocator
/// \arg allocs [out] Poiinter to the initial allocation register
/// \arg modules [out] Pointer to the initial modules_page
/// \arg bs UEFI boot services
static void init(
allocation_register *&allocs,
modules_page *&modules,
uefi::boot_services *bs);
private:
void add_register();
void add_modules();
module * allocate_module_untyped(size_t size);
uefi::boot_services &m_bs;
allocation_register *m_register;
allocation_register *m_current;
modules_page *m_modules;
module *m_next_mod;
};
/// Initialize the global allocator
void init_allocator(uefi::boot_services *bs);
} // namespace memory
extern memory::allocator &g_alloc;

View File

@@ -22,7 +22,7 @@ static const wchar_t digits[] = {u'0', u'1', u'2', u'3', u'4', u'5',
u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
static size_t
size_t
wstrlen(const wchar_t *s)
{
size_t count = 0;

View File

@@ -4,6 +4,7 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
namespace uefi {
namespace protos {
@@ -40,4 +41,6 @@ private:
static console *s_console;
};
size_t wstrlen(const wchar_t *s);
} // namespace boot

View File

@@ -9,6 +9,7 @@
#include "console.h"
#include "error.h"
#include "fs.h"
#include "kernel_args.h"
#include "memory.h"
#include "status.h"

View File

@@ -1,6 +1,7 @@
#include "hardware.h"
#include "console.h"
#include "cpu/cpu_id.h"
#include "error.h"
#include "hardware.h"
#include "status.h"
namespace boot {
@@ -53,7 +54,6 @@ wrmsr(uint32_t addr, uint64_t value)
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
}
void
setup_control_regs()
{
@@ -77,5 +77,27 @@ setup_control_regs()
wrmsr(IA32_EFER, efer);
}
void
check_cpu_supported()
{
status_line status {L"Checking CPU features"};
cpu::cpu_id cpu;
uint64_t missing = cpu.missing();
if (missing) {
#define CPU_FEATURE_OPT(...)
#define CPU_FEATURE_REQ(name, ...) \
if (!cpu.has_feature(cpu::feature::name)) { \
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \
}
#include "cpu/features.inc"
#undef CPU_FEATURE_REQ
#undef CPU_FEATURE_OPT
error::raise(uefi::status::unsupported, L"CPU not supported");
}
}
} // namespace hw
} // namespace boot

View File

@@ -16,5 +16,8 @@ void * find_acpi_table(uefi::system_table *st);
/// Enable CPU options in CR4 etc for the kernel starting state.
void setup_control_regs();
/// Check that all required cpu features are supported
void check_cpu_supported();
} // namespace hw
} // namespace boot

View File

@@ -6,6 +6,7 @@
#include "elf.h"
#include "error.h"
#include "fs.h"
#include "init_args.h"
#include "loader.h"
#include "memory.h"
#include "paging.h"
@@ -22,12 +23,11 @@ using memory::alloc_type;
buffer
load_file(
fs::file &disk,
const wchar_t *name,
const wchar_t *path)
const program_desc &desc)
{
status_line status(L"Loading file", name);
status_line status(L"Loading file", desc.path);
fs::file file = disk.open(path);
fs::file file = disk.open(desc.path);
buffer b = file.load();
//console::print(L" Loaded at: 0x%lx, %d bytes\r\n", b.data, b.size);
@@ -50,13 +50,37 @@ is_elfheader_valid(const elf::header *header)
header->header_version == elf::version;
}
void
load_program(
init::program &program,
const wchar_t *name,
buffer data)
static void
create_module(buffer data, const program_desc &desc, bool loaded)
{
status_line status(L"Loading program:", name);
size_t path_len = wstrlen(desc.path);
init::module_program *mod = g_alloc.allocate_module<init::module_program>(path_len);
mod->mod_type = init::module_type::program;
mod->base_address = reinterpret_cast<uintptr_t>(data.pointer);
if (loaded)
mod->mod_flags = static_cast<init::module_flags>(
static_cast<uint8_t>(mod->mod_flags) |
static_cast<uint8_t>(init::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)
mod->filename[i] = desc.path[i];
mod->filename[path_len] = 0;
}
init::program *
load_program(
fs::file &disk,
const program_desc &desc,
bool add_module)
{
status_line status(L"Loading program", desc.name);
buffer data = load_file(disk, desc);
if (add_module)
create_module(data, desc, true);
const elf::header *header = reinterpret_cast<const elf::header*>(data.pointer);
uintptr_t program_base = reinterpret_cast<uintptr_t>(data.pointer);
@@ -103,9 +127,22 @@ load_program(
section.type = static_cast<init::section_flags>(pheader->flags);
}
program.sections = { .pointer = sections, .count = num_sections };
program.phys_base = program_base;
program.entrypoint = header->entrypoint;
init::program *prog = new init::program;
prog->sections = { .pointer = sections, .count = num_sections };
prog->phys_base = program_base;
prog->entrypoint = header->entrypoint;
return prog;
}
void
load_module(
fs::file &disk,
const program_desc &desc)
{
status_line status(L"Loading module", desc.name);
buffer data = load_file(disk, desc);
create_module(data, desc, false);
}
void

View File

@@ -7,6 +7,7 @@
namespace kernel {
namespace init {
struct program;
struct module;
}}
namespace boot {
@@ -17,25 +18,37 @@ namespace fs {
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 name Name of the module (informational only)
/// \arg path Path on `disk` of the file to load
/// \arg desc The program descriptor identifying the file
buffer
load_file(
fs::file &disk,
const wchar_t *name,
const wchar_t *path);
const program_desc &desc);
/// Parse and load an ELF file in memory into a loaded image.
/// \arg program The program structure to fill
/// \arg name The name of the program being loaded
/// \arg data Buffer of the ELF file in memory
void
/// \arg disk The opened UEFI filesystem to load from
/// \arg desc The program descriptor identifying the program
/// \arg add_module Also create a module for this loaded program
kernel::init::program *
load_program(
kernel::init::program &program,
const wchar_t *name,
buffer data);
fs::file &disk,
const program_desc &desc,
bool add_module = false);
/// Load a file from disk into memory, creating an init args module
/// \arg disk The opened UEFI filesystem to load from
/// \arg desc The program descriptor identifying the file
void
load_module(
fs::file &disk,
const program_desc &desc);
/// Verify that a loaded ELF has the j6 kernel header
/// \arg program The program to check for a header

View File

@@ -9,7 +9,6 @@
#include "allocator.h"
#include "console.h"
#include "cpu/cpu_id.h"
#include "error.h"
#include "fs.h"
#include "hardware.h"
@@ -30,19 +29,12 @@ namespace init = kernel::init;
namespace boot {
constexpr int max_modules = 5; // Max modules to allocate room for
constexpr int max_programs = 5; // Max programs to allocate room for
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"};
struct program_desc
{
const wchar_t *name;
const wchar_t *path;
};
const program_desc program_list[] = {
{L"kernel", L"jsix.elf"},
const loader::program_desc extra_programs[] = {
{L"test application", L"testapp.elf"},
{L"UEFI framebuffer driver", L"drv.uefi_fb.elf"},
};
/// Change a pointer to point to the higher-half linear-offset version
@@ -53,40 +45,6 @@ void change_pointer(T *&pointer)
pointer = offset_ptr<T>(pointer, kernel::memory::page_offset);
}
/// Add a module to the kernel args list
inline void
add_module(init::args *args, init::mod_type type, buffer &data)
{
init::module &m = args->modules[args->modules.count++];
m.type = type;
m.location = data.pointer;
m.size = data.count;
change_pointer(m.location);
}
/// Check that all required cpu features are supported
void
check_cpu_supported()
{
status_line status {L"Checking CPU features"};
cpu::cpu_id cpu;
uint64_t missing = cpu.missing();
if (missing) {
#define CPU_FEATURE_OPT(...)
#define CPU_FEATURE_REQ(name, ...) \
if (!cpu.has_feature(cpu::feature::name)) { \
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \
}
#include "cpu/features.inc"
#undef CPU_FEATURE_REQ
#undef CPU_FEATURE_OPT
error::raise(uefi::status::unsupported, L"CPU not supported");
}
}
/// The main procedure for the portion of the loader that runs while
/// UEFI is still in control of the machine. (ie, while the loader still
/// has access to boot services.)
@@ -98,13 +56,11 @@ uefi_preboot(uefi::handle image, uefi::system_table *st)
status_line status {L"Performing UEFI pre-boot"};
check_cpu_supported();
hw::check_cpu_supported();
memory::init_pointer_fixup(bs, rs);
init::args *args = new init::args;
g_alloc.zero(args, sizeof(init::args));
args->programs.pointer = new init::program[5];
args->modules.pointer = new init::module[5];
args->magic = init::args_magic;
args->version = init::args_version;
@@ -114,21 +70,32 @@ uefi_preboot(uefi::handle image, uefi::system_table *st)
paging::allocate_tables(args);
return args;
}
/// Load the kernel and other programs from disk
void
load_resources(init::args *args, video::screen *screen, uefi::handle image, uefi::boot_services *bs)
{
status_line status {L"Loading programs"};
fs::file disk = fs::get_boot_volume(image, bs);
buffer symbols = loader::load_file(disk, L"symbol table", L"symbol_table.dat");
add_module(args, init::mod_type::symbol_table, symbols);
for (auto &desc : program_list) {
buffer buf = loader::load_file(disk, desc.name, desc.path);
init::program &program = args->programs[args->programs.count++];
loader::load_program(program, desc.name, buf);
if (screen) {
video::make_module(screen);
loader::load_module(disk, fb_driver);
}
// First program *must* be the kernel
loader::verify_kernel_header(args->programs[0]);
args->symbol_table = loader::load_file(disk, {L"symbol table", L"symbol_table.dat"});
args->kernel = loader::load_program(disk, kern_desc, true);
args->init = loader::load_program(disk, init_desc);
for (auto &desc : extra_programs)
loader::load_module(disk, desc);
loader::verify_kernel_header(*args->kernel);
return args;
}
memory::efi_mem_map
@@ -141,7 +108,6 @@ uefi_exit(init::args *args, uefi::handle image, uefi::boot_services *bs)
args->mem_map = memory::build_kernel_map(map);
args->frame_blocks = memory::build_frame_blocks(args->mem_map);
args->allocations = g_alloc.get_register();
map.update(*bs);
try_or_raise(
@@ -162,17 +128,24 @@ efi_main(uefi::handle image, uefi::system_table *st)
uefi::boot_services *bs = st->boot_services;
console con(st->con_out);
memory::init_allocator(bs);
init::allocation_register *allocs = nullptr;
init::modules_page *modules = nullptr;
memory::allocator::init(allocs, modules, bs);
video::screen *screen = video::pick_mode(bs);
con.announce();
init::args *args = uefi_preboot(image, st);
load_resources(args, screen, image, bs);
memory::efi_mem_map map = uefi_exit(args, image, st->boot_services);
args->allocations = allocs;
args->modules = reinterpret_cast<uintptr_t>(modules);
status_bar status {screen}; // Switch to fb status display
// Map the kernel to the appropriate address
init::program &kernel = args->programs[0];
init::program &kernel = *args->kernel;
for (auto &section : kernel.sections)
paging::map_section(args, section);
@@ -180,20 +153,22 @@ efi_main(uefi::handle image, uefi::system_table *st)
init::entrypoint kentry =
reinterpret_cast<init::entrypoint>(kernel.entrypoint);
status.next();
//status.next();
hw::setup_control_regs();
memory::virtualize(args->pml4, map, st->runtime_services);
status.next();
//status.next();
change_pointer(args);
change_pointer(args->pml4);
change_pointer(args->modules.pointer);
change_pointer(args->programs.pointer);
for (auto &program : args->programs)
change_pointer(program.sections.pointer);
change_pointer(args->symbol_table.pointer);
status.next();
change_pointer(args->kernel);
change_pointer(args->kernel->sections.pointer);
change_pointer(args->init);
change_pointer(args->init->sections.pointer);
//status.next();
kentry(args);
debug_break();

View File

@@ -2,6 +2,7 @@
#include <uefi/graphics.h>
#include <uefi/protos/graphics_output.h>
#include "allocator.h"
#include "console.h"
#include "error.h"
#include "init_args.h"
@@ -106,15 +107,15 @@ pick_mode(uefi::boot_services *bs)
}
void
make_module(screen *s, module_framebuffer *mod)
make_module(screen *s)
{
mod->mod_type = module_type::framebuffer;
mod->mod_flags = module_flags::none;
mod->mod_length = sizeof(module_framebuffer);
mod->type = fb_type::uefi;
using kernel::init::module_framebuffer;
module_framebuffer *modfb = g_alloc.allocate_module<module_framebuffer>();
modfb->mod_type = module_type::framebuffer;
modfb->type = fb_type::uefi;
mod->framebuffer = s->framebuffer;
mod->mode = s->mode;
modfb->framebuffer = s->framebuffer;
modfb->mode = s->mode;
}
} // namespace video

View File

@@ -26,7 +26,7 @@ struct screen {
screen * pick_mode(uefi::boot_services *bs);
/// Make an init arg module from the video mode
void make_module(screen *s, kernel::init::module_framebuffer *mod);
void make_module(screen *s);
} // namespace video
} // namespace boot