Kernel: Implemented banos - a WIP C driver API

Banos is a stable WIP C driver API that is supposed to provide a simple
interface to interact with the kernel and load the modules dynamically.
It is WIP and atm this just implements module loading with a custom
banos_install syscall. Banos will not try to substitute parts of the
kernel instead it will just expose kernel functionality via a stable
BINARY API. Meaning binaries (should) remain forward and backward
compatible on a binary level.

Banos modules work similarly to those in linux, you expose symbols via
BANOS_EXPORT which allows you to export a name + addr paired symbol.
It puts it in the .banos-export section. Drivers provide metadata about
themselves in the REQUIRED .banos-driver section. Symbols are resolved
at runtime. The kernel exposes the driver functionality via the same
.banos-export export mechanism.

Banos modules are elf RELOCATABLE files (object files) which have
partial linking (only banos symbols should remain). Modules will
eventually define dependencies, will export symbols and will allow you
to build a complex object hierarchy.

This patch adds the banos_install syscall which takes in the driver
image to install and may only be executed by super users. The API
doesn't validate already loaded modules, as thats something the
userspace MAY choose to keep track of. Multi-instance functionality
shall be implemented via driver specific behaviuor (exposed in the dev
filesystem or some other means).

Modules are supposed to allow you to alter kernel behavior and extend
it, allowing you to create filesystems, drivers, networking
modifications, schedulers, probers, and more (hopefully) whilst
remaining binary compatible with any version of the kernel (again,
hopefully).
This commit is contained in:
2026-05-20 17:24:36 +03:00
committed by Bananymous
parent 718379ce3b
commit f1a72cc9da
13 changed files with 332 additions and 0 deletions

215
kernel/kernel/Banos.cpp Normal file
View File

@@ -0,0 +1,215 @@
#include <kernel/Debug.h>
#include <kernel/Banos.h>
#include <BAN/Assert.h>
#include <banos/driver.h>
#include <banos/print.h>
#include <banos/export.h>
#include <kernel/FS/VirtualFileSystem.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/ELF.h>
#include <LibELF/Types.h>
#include <LibELF/Values.h>
#include <kernel/Process.h>
#include <BAN/HashMap.h>
#include <kernel/Lock/SpinLock.h>
#include <kernel/UserCopy.h>
using namespace LibELF;
using namespace Kernel;
extern "C" {
void banos_dprintln(const char* str) {
dprintln("{}", str);
}
void* banos_lookup_symbol(const char* str) {
return Banos::resolve_symbol(str);
}
}
BANOS_EXPORT(banos_dprintln);
BANOS_EXPORT(banos_lookup_symbol);
BAN::HashMap<BAN::StringView, void*> g_banos_symbols;
void* Banos::resolve_symbol(const char* name) {
auto it = g_banos_symbols.find(name);
return it == g_banos_symbols.end() ? NULL : it->value;
}
void Banos::import_symbols(Banos_Symbol* symbols, size_t count) {
for(size_t i = 0; i < count; ++i) {
auto sym = symbols + i;
MUST(g_banos_symbols.insert(sym->name, sym->arg));
}
}
// TODO: driver unloading with a reference counter
struct Driver_Instance {
Banos_Driver* drv;
};
static BAN::Vector<Driver_Instance> s_driver_instaces;
static SpinLock s_driver_instaces_lock;
extern Banos_Symbol g_banos_export[],
g_banos_export_end[];
static void load_drv(Banos_Driver* drv) {
ASSERT(drv->driver_size >= sizeof(Banos_Driver));
dprintln("Loading driver:");
dprintln(" name: {}", drv->name);
if(drv->license) dprintln(" license: {}", drv->license);
dprintln(" version: {}.{}.{}", BANOS_VERSION_GET_MAJOR(drv->version), BANOS_VERSION_GET_MINOR(drv->version), BANOS_VERSION_GET_PATCH(drv->version));
int e = drv->init(drv);
if(e < 0) dprintln(" Failed to init {} => {}", drv->name, -e);
}
BAN::ErrorOr<size_t> Banos::load_driver_from_image(const char* u_image) {
if(!Process::current().credentials().is_superuser()) return BAN::Error::from_errno(EPERM);
// TODO: permission verification. Only root should be allowed to do this
LibELF::ElfNativeFileHeader header;
const unsigned char elf_class =
#if ARCH(i686)
ELFCLASS32;
#elif ARCH(x86_64)
ELFCLASS64;
#else
# error update elf class
#endif
// TODO: is banan-os really ever gonna be running on MSB machines?
const unsigned char elf_data = ELFDATA2LSB;
// TODO: do we need to verify e_machine? I mean we do not really care.
// But I'm leaving this todo:
// Look up EM_X86_64 and EM_360|EM_860|EM_960
TRY(read_from_user(u_image, &header, sizeof header));
if( header.e_ident[EI_MAG0] != ELFMAG0 ||
header.e_ident[EI_MAG1] != ELFMAG1 ||
header.e_ident[EI_MAG2] != ELFMAG2 ||
header.e_ident[EI_MAG3] != ELFMAG3 ||
header.e_ident[EI_CLASS] != elf_class ||
header.e_ident[EI_DATA] != elf_data ||
header.e_ident[EI_VERSION] != EV_CURRENT ||
header.e_type != ET_REL ||
header.e_version != EV_CURRENT ||
header.e_ehsize != sizeof(header) ||
header.e_shentsize != sizeof(ElfNativeSectionHeader))
return BAN::Error::from_errno(EINVAL);
BAN::Vector<LibELF::ElfNativeSectionHeader> secs(header.e_shnum);
TRY(read_from_user(u_image + header.e_shoff, secs.data(), secs.size() * sizeof(*secs.data())));
auto shstr = secs[header.e_shstrndx];
size_t total_size = 0;
LibELF::ElfNativeSectionHeader *strtab = nullptr,
*symtab = nullptr,
*driver_section = nullptr;
for(auto& sec : secs) {
if(sec.sh_flags & LibELF::SHF_ALLOC) {
sec.sh_addr = total_size;
total_size += sec.sh_size;
}
if(sec.sh_name == 0) continue;
char name[256];
TRY(read_string_from_user(u_image + shstr.sh_offset + sec.sh_name, name, sizeof name));
BAN::StringView name_sv(name);
if(sec.sh_type == LibELF::SHT_SYMTAB) {
symtab = &sec;
}
// TODO: verify sh_type for both of these?
if(name_sv == ".strtab") {
strtab = &sec;
} else if(name_sv == ".banos-driver") {
driver_section = &sec;
}
}
if(!symtab || !strtab || !driver_section)
return BAN::Error::from_errno(EINVAL);
total_size += PAGE_SIZE;
total_size &= ~(PAGE_SIZE-1);
auto driver = TRY(VirtualRange::create_to_vaddr_range(PageTable::kernel(), { KERNEL_OFFSET, UINTPTR_MAX }, total_size, PageTable::Execute | PageTable::ReadWrite | PageTable::Present, true));
for(auto& sec : secs) {
if(sec.sh_flags & LibELF::SHF_ALLOC) {
sec.sh_addr += driver->vaddr();
}
}
Banos_Driver* banos_driver = reinterpret_cast<Banos_Driver*>(driver_section->sh_addr);
for(auto& sec : secs) {
if(sec.sh_name == 0) continue;
if(sec.sh_flags & LibELF::SHF_ALLOC) {
TRY(read_from_user(u_image + sec.sh_offset, reinterpret_cast<char*>(sec.sh_addr), sec.sh_size));
}
if(sec.sh_type == LibELF::SHT_RELA) {
auto& link_sec = secs[sec.sh_info];
size_t rela_count = sec.sh_size/sizeof(LibELF::ElfNativeRelocationA);
BAN::Vector<LibELF::ElfNativeRelocationA> rela_data(rela_count);
TRY(read_from_user(u_image + sec.sh_offset, rela_data.data(), rela_count * sizeof *rela_data.data()));
for(auto rela : rela_data) {
auto type = ELF64_R_TYPE(rela.r_info);
auto symbol = ELF64_R_SYM(rela.r_info);
vaddr_t value = 0;
LibELF::ElfNativeSymbol sym;
TRY(read_from_user(u_image + symtab->sh_offset + sizeof(sym) * symbol, &sym, sizeof sym));
if(sym.st_shndx) {
value = secs[sym.st_shndx].sh_addr;
} else {
char name[256];
TRY(read_string_from_user(u_image + strtab->sh_offset + sym.st_name, name, sizeof name));
value = reinterpret_cast<vaddr_t>(Banos::resolve_symbol(name));
if(!value) {
derrorln("Failed to find symbol {}", name);
return BAN::Error::from_errno(ENOENT);
}
}
vaddr_t at = link_sec.sh_addr + rela.r_offset;
size_t size = 0;
switch(type) {
case LibELF::R_X86_64_PLT32:
case LibELF::R_X86_64_PC32:
value -= at;
// fallthrough
case LibELF::R_X86_64_32:
case LibELF::R_X86_64_32S:
value += rela.r_addend;
size = sizeof(uint32_t);
break;
case LibELF::R_X86_64_64:
value += rela.r_addend;
size = sizeof(uint64_t);
break;
default:
derrorln("TODO: Unsupported relocation type {}", type);
return BAN::Error::from_errno(ENOSYS);
}
switch(size) {
case 4: *reinterpret_cast<uint32_t*>(at) = value; break;
case 8: *reinterpret_cast<uint64_t*>(at) = value; break;
}
}
}
}
Driver_Instance instance;
instance.drv = banos_driver;
load_drv(instance.drv);
SpinLockGuard _(s_driver_instaces_lock);
TRY(s_driver_instaces.push_back(instance));
// TODO: import symbols and resolve redefintions :)
return s_driver_instaces.size() - 1;
}
// NOTE: should be more than plenty ;)
extern char g_drv_builtin_begin[];
extern char g_drv_builtin_end[];
void Banos::initialize_initial_drivers(void) {
import_symbols(g_banos_export, g_banos_export_end - g_banos_export);
char* head = g_drv_builtin_begin;
while(head < g_drv_builtin_end) {
Banos_Driver* drv = (Banos_Driver*)head;
load_drv(drv);
head += drv->driver_size;
}
}

View File

@@ -22,6 +22,7 @@
#include <kernel/Timer/Timer.h>
#include <kernel/UserCopy.h>
#include <kernel/Banos.h>
#include <LibELF/AuxiliaryVector.h>
#include <LibInput/KeyboardLayout.h>
@@ -3950,4 +3951,7 @@ namespace Kernel
return BAN::Error::from_errno(EFAULT);
}
BAN::ErrorOr<long> Process::sys_banos_install(const char* u_image) {
return TRY(Banos::load_driver_from_image(u_image));
}
}

View File

@@ -27,6 +27,7 @@
#include <kernel/Terminal/VirtualTTY.h>
#include <kernel/Timer/Timer.h>
#include <kernel/USB/USBManager.h>
#include <kernel/Banos.h>
#include <LibInput/KeyboardLayout.h>
@@ -261,6 +262,8 @@ static void init2(void*)
TTY::initialize_devices();
Banos::initialize_initial_drivers();
auto console_path = MUST(BAN::String::formatted("/dev/{}", cmdline.console));
auto console_path_sv = console_path.sv();
MUST(Process::create_userspace({ 0, 0, 0, 0 }, "/usr/bin/init"_sv, BAN::Span<BAN::StringView>(&console_path_sv, 1)));