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

View File

@@ -11,6 +11,7 @@ set(KERNEL_SOURCES
kernel/Audio/Controller.cpp kernel/Audio/Controller.cpp
kernel/Audio/HDAudio/AudioFunctionGroup.cpp kernel/Audio/HDAudio/AudioFunctionGroup.cpp
kernel/Audio/HDAudio/Controller.cpp kernel/Audio/HDAudio/Controller.cpp
kernel/Banos.cpp
kernel/BootInfo.cpp kernel/BootInfo.cpp
kernel/CPUID.cpp kernel/CPUID.cpp
kernel/Credentials.cpp kernel/Credentials.cpp

View File

@@ -41,7 +41,18 @@ SECTIONS
{ {
g_kernel_writable_start = .; g_kernel_writable_start = .;
*(.data) *(.data)
. = ALIGN(8);
g_drv_builtin_begin = .;
KEEP(*(.banos-driver))
g_drv_builtin_end = .;
. = ALIGN(8);
g_banos_export = .;
KEEP(*(.banos-export))
g_banos_export_end = .;
} }
.bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_OFFSET) .bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_OFFSET)
{ {
g_kernel_bss_start = .; g_kernel_bss_start = .;

View File

@@ -0,0 +1,27 @@
#pragma once
// Copyright (c) 2026 Dcraftbg
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "version.h"
#include "revision.h"
#define BANOS_DRIVER_REVISION_CURRENT 0
typedef struct Banos_Driver Banos_Driver;
struct Banos_Driver {
unsigned long driver_size;
banos_version_t minimal_banos_version;
const char* name;
const char* license;
banos_version_t version;
// NOTE: checkout BANOS_DRIVER_INSTANCE_SIZE.
// You may use this instance data for anything you wish to store.
// If you need more than that just allocate it on the heap or
// globally if you add the proper verification of having your driver run only
// within a single instance
int (*init)(Banos_Driver* drv);
int (*uninit)(Banos_Driver* drv);
};
#define BANOS_DRIVER_API static __attribute__((section(".banos-driver"), used, aligned(8)))

View File

@@ -0,0 +1,15 @@
#pragma once
// Copyright (c) 2026 Dcraftbg
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
typedef struct Banos_Symbol {
const char* name;
void* arg;
} Banos_Symbol;
#define BANOS_EXPORT_SYMBOL(symname, str, ptr) \
static __attribute__((section(".banos-export"), used, aligned(8))) Banos_Symbol __symbol_##symname = {\
.name = str, \
.arg = ptr \
};
#define BANOS_EXPORT(name) BANOS_EXPORT_SYMBOL(name, #name, (void*)&name)

View File

@@ -0,0 +1,13 @@
#pragma once
// Copyright (c) 2026 Dcraftbg
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifdef __cplusplus
extern "C" {
#endif
void banos_dprintln(const char* str);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,2 @@
#pragma once
typedef unsigned long banos_revision_t;

View File

@@ -0,0 +1,28 @@
#pragma once
// Copyright (c) 2026 Dcraftbg
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// [ 8 bit major ] [ 8 minor ] [ 16 patch]
typedef unsigned int banos_version_t;
#define BANOS_VERSION_MAJOR_SHIFT 24
#define BANOS_VERSION_MINOR_SHIFT 16
#define BANOS_VERSION_PATCH_SHIFT 0
#define BANOS_VERSION_MAJOR_MASK 0xFF
#define BANOS_VERSION_MINOR_MASK 0xFF
#define BANOS_VERSION_PATCH_MASK 0xFFFF
#define BANOS_VERSION_MAKE(major, minor, patch) \
(banos_version_t)( \
(((major) & BANOS_VERSION_MAJOR_MASK) << BANOS_VERSION_MAJOR_SHIFT) | \
(((minor) & BANOS_VERSION_MINOR_MASK) << BANOS_VERSION_MINOR_SHIFT) | \
(((patch) & BANOS_VERSION_PATCH_MASK) << BANOS_VERSION_PATCH_SHIFT) \
)
#define BANOS_VERSION_CURRENT BANOS_VERSION_MAKE(0, 0, 1)
#define BANOS_VERSION_GET_MAJOR(v) (((v) >> BANOS_VERSION_MAJOR_SHIFT) & BANOS_VERSION_MAJOR_MASK)
#define BANOS_VERSION_GET_MINOR(v) (((v) >> BANOS_VERSION_MINOR_SHIFT) & BANOS_VERSION_MINOR_MASK)
#define BANOS_VERSION_GET_PATCH(v) (((v) >> BANOS_VERSION_PATCH_SHIFT) & BANOS_VERSION_PATCH_MASK)

View File

@@ -0,0 +1,10 @@
#pragma once
#include <BAN/Vector.h>
#include <BAN/StringView.h>
typedef struct Banos_Symbol Banos_Symbol;
namespace Banos {
void* resolve_symbol(const char* name);
void import_symbols(Banos_Symbol* symbols, size_t count);
void initialize_initial_drivers(void);
BAN::ErrorOr<size_t> load_driver_from_image(const char* u_image);
}

View File

@@ -221,6 +221,8 @@ namespace Kernel
BAN::ErrorOr<long> sys_load_keymap(const char* path); BAN::ErrorOr<long> sys_load_keymap(const char* path);
BAN::ErrorOr<long> sys_banos_install(const char* object);
BAN::RefPtr<TTY> controlling_terminal() { return m_controlling_terminal; } BAN::RefPtr<TTY> controlling_terminal() { return m_controlling_terminal; }
static Process& current() { return Thread::current().process(); } static Process& current() { return Thread::current().process(); }

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/Timer/Timer.h>
#include <kernel/UserCopy.h> #include <kernel/UserCopy.h>
#include <kernel/Banos.h>
#include <LibELF/AuxiliaryVector.h> #include <LibELF/AuxiliaryVector.h>
#include <LibInput/KeyboardLayout.h> #include <LibInput/KeyboardLayout.h>
@@ -3950,4 +3951,7 @@ namespace Kernel
return BAN::Error::from_errno(EFAULT); 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/Terminal/VirtualTTY.h>
#include <kernel/Timer/Timer.h> #include <kernel/Timer/Timer.h>
#include <kernel/USB/USBManager.h> #include <kernel/USB/USBManager.h>
#include <kernel/Banos.h>
#include <LibInput/KeyboardLayout.h> #include <LibInput/KeyboardLayout.h>
@@ -261,6 +262,8 @@ static void init2(void*)
TTY::initialize_devices(); TTY::initialize_devices();
Banos::initialize_initial_drivers();
auto console_path = MUST(BAN::String::formatted("/dev/{}", cmdline.console)); auto console_path = MUST(BAN::String::formatted("/dev/{}", cmdline.console));
auto console_path_sv = console_path.sv(); 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))); MUST(Process::create_userspace({ 0, 0, 0, 0 }, "/usr/bin/init"_sv, BAN::Span<BAN::StringView>(&console_path_sv, 1)));

View File

@@ -117,6 +117,7 @@ __BEGIN_DECLS
O(SYS_SETGROUPS, setgroups) \ O(SYS_SETGROUPS, setgroups) \
O(SYS_CHROOT, chroot) \ O(SYS_CHROOT, chroot) \
O(SYS_EVENTFD, eventfd) \ O(SYS_EVENTFD, eventfd) \
O(SYS_BANOS_INSTALL, banos_install) \
enum Syscall enum Syscall
{ {