From f1a72cc9da69e2806432ef87fe4f3bf3f1dc4fee Mon Sep 17 00:00:00 2001 From: DcraftBg Date: Wed, 20 May 2026 17:24:36 +0300 Subject: [PATCH] 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). --- kernel/CMakeLists.txt | 1 + kernel/arch/x86_64/linker.ld | 11 + kernel/include/banos/driver.h | 27 +++ kernel/include/banos/export.h | 15 ++ kernel/include/banos/print.h | 13 ++ kernel/include/banos/revision.h | 2 + kernel/include/banos/version.h | 28 +++ kernel/include/kernel/Banos.h | 10 + kernel/include/kernel/Process.h | 2 + kernel/kernel/Banos.cpp | 215 ++++++++++++++++++ kernel/kernel/Process.cpp | 4 + kernel/kernel/kernel.cpp | 3 + .../libraries/LibC/include/sys/syscall.h | 1 + 13 files changed, 332 insertions(+) create mode 100644 kernel/include/banos/driver.h create mode 100644 kernel/include/banos/export.h create mode 100644 kernel/include/banos/print.h create mode 100644 kernel/include/banos/revision.h create mode 100644 kernel/include/banos/version.h create mode 100644 kernel/include/kernel/Banos.h create mode 100644 kernel/kernel/Banos.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 09209a7d..2351f441 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -11,6 +11,7 @@ set(KERNEL_SOURCES kernel/Audio/Controller.cpp kernel/Audio/HDAudio/AudioFunctionGroup.cpp kernel/Audio/HDAudio/Controller.cpp + kernel/Banos.cpp kernel/BootInfo.cpp kernel/CPUID.cpp kernel/Credentials.cpp diff --git a/kernel/arch/x86_64/linker.ld b/kernel/arch/x86_64/linker.ld index 43e9ce4d..a4d861f6 100644 --- a/kernel/arch/x86_64/linker.ld +++ b/kernel/arch/x86_64/linker.ld @@ -41,7 +41,18 @@ SECTIONS { g_kernel_writable_start = .; *(.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) { g_kernel_bss_start = .; diff --git a/kernel/include/banos/driver.h b/kernel/include/banos/driver.h new file mode 100644 index 00000000..f57c212c --- /dev/null +++ b/kernel/include/banos/driver.h @@ -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))) diff --git a/kernel/include/banos/export.h b/kernel/include/banos/export.h new file mode 100644 index 00000000..04657897 --- /dev/null +++ b/kernel/include/banos/export.h @@ -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) diff --git a/kernel/include/banos/print.h b/kernel/include/banos/print.h new file mode 100644 index 00000000..219d400c --- /dev/null +++ b/kernel/include/banos/print.h @@ -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 diff --git a/kernel/include/banos/revision.h b/kernel/include/banos/revision.h new file mode 100644 index 00000000..5b677dcf --- /dev/null +++ b/kernel/include/banos/revision.h @@ -0,0 +1,2 @@ +#pragma once +typedef unsigned long banos_revision_t; diff --git a/kernel/include/banos/version.h b/kernel/include/banos/version.h new file mode 100644 index 00000000..a454cd88 --- /dev/null +++ b/kernel/include/banos/version.h @@ -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) diff --git a/kernel/include/kernel/Banos.h b/kernel/include/kernel/Banos.h new file mode 100644 index 00000000..f51d7d63 --- /dev/null +++ b/kernel/include/kernel/Banos.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include +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 load_driver_from_image(const char* u_image); +} diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 939342b0..707c164c 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -221,6 +221,8 @@ namespace Kernel BAN::ErrorOr sys_load_keymap(const char* path); + BAN::ErrorOr sys_banos_install(const char* object); + BAN::RefPtr controlling_terminal() { return m_controlling_terminal; } static Process& current() { return Thread::current().process(); } diff --git a/kernel/kernel/Banos.cpp b/kernel/kernel/Banos.cpp new file mode 100644 index 00000000..b497ba26 --- /dev/null +++ b/kernel/kernel/Banos.cpp @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 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 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 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(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(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 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(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(at) = value; break; + case 8: *reinterpret_cast(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; + } +} diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 2bd0307f..433d486f 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -3950,4 +3951,7 @@ namespace Kernel return BAN::Error::from_errno(EFAULT); } + BAN::ErrorOr Process::sys_banos_install(const char* u_image) { + return TRY(Banos::load_driver_from_image(u_image)); + } } diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 03de6f3c..9b5d76c1 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -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(&console_path_sv, 1))); diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index 72c0b602..b759e896 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -117,6 +117,7 @@ __BEGIN_DECLS O(SYS_SETGROUPS, setgroups) \ O(SYS_CHROOT, chroot) \ O(SYS_EVENTFD, eventfd) \ + O(SYS_BANOS_INSTALL, banos_install) \ enum Syscall {