From b3aaddadc82b17edb1c74df62012477ff3846131 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Thu, 13 Jan 2022 22:08:35 -0800 Subject: [PATCH] [kernel] Expose a sysconf page to userspace A structure, system_config, which is dynamically defined by the definitions/sysconf.yaml config, is now mapped into every user address space. The kernel fills this with information about itself and the running machine. User programs access this through the new j6_sysconf fake syscall in libj6. See: Github bug #242 See: [frobozz blog post](https://jsix.dev/posts/frobozz/) Tags: --- definitions/sysconf.yaml | 34 +++++++++++++++ scripts/sysconf.py | 17 ++++++++ src/kernel/kernel.module | 4 ++ src/kernel/main.cpp | 2 + src/kernel/memory.h.cog | 5 +++ src/kernel/sysconf.cpp | 53 +++++++++++++++++++++++ src/kernel/sysconf.h.cog | 28 ++++++++++++ src/kernel/vm_space.cpp | 9 ++++ src/libraries/j6/include/j6/sysconf.h.cog | 44 +++++++++++++++++++ src/libraries/j6/j6.module | 10 +++++ src/libraries/j6/sysconf.cpp.cog | 44 +++++++++++++++++++ 11 files changed, 250 insertions(+) create mode 100644 definitions/sysconf.yaml create mode 100644 scripts/sysconf.py create mode 100644 src/kernel/sysconf.cpp create mode 100644 src/kernel/sysconf.h.cog create mode 100644 src/libraries/j6/include/j6/sysconf.h.cog create mode 100644 src/libraries/j6/sysconf.cpp.cog diff --git a/definitions/sysconf.yaml b/definitions/sysconf.yaml new file mode 100644 index 0000000..e133d09 --- /dev/null +++ b/definitions/sysconf.yaml @@ -0,0 +1,34 @@ +--- +address: 0x6a360000 +vars: + - name: version_major + section: kernel + type: uint8_t + + - name: version_minor + section: kernel + type: uint8_t + + - name: version_patch + section: kernel + type: uint16_t + + - name: version_gitsha + section: kernel + type: uint32_t + + - name: page_size + section: sys + type: size_t + + - name: large_page_size + section: sys + type: size_t + + - name: huge_page_size + section: sys + type: size_t + + - name: num_cpus + section: sys + type: uint32_t diff --git a/scripts/sysconf.py b/scripts/sysconf.py new file mode 100644 index 0000000..c369ccb --- /dev/null +++ b/scripts/sysconf.py @@ -0,0 +1,17 @@ +class Sysconf: + from collections import namedtuple + Var = namedtuple("Var", ("name", "section", "type")) + + def __init__(self, path): + from yaml import safe_load + + sys_vars = [] + + with open(path, 'r') as infile: + data = safe_load(infile.read()) + self.address = data["address"] + + for v in data["vars"]: + sys_vars.append(Sysconf.Var(v["name"], v["section"], v["type"])) + + self.vars = tuple(sys_vars) diff --git a/src/kernel/kernel.module b/src/kernel/kernel.module index b388ef4..6d987ed 100644 --- a/src/kernel/kernel.module +++ b/src/kernel/kernel.module @@ -60,6 +60,8 @@ kernel = module("kernel", "syscalls/system.cpp", "syscalls/thread.cpp", "syscalls/vm_area.cpp", + "sysconf.cpp", + "sysconf.h.cog", "task.s", "tss.cpp", "vm_space.cpp", @@ -69,9 +71,11 @@ from glob import glob from os.path import join layout = join(source_root, "definitions/memory_layout.yaml") +sysconf = join(source_root, "definitions/sysconf.yaml") definitions = glob('definitions/**/*.def', recursive=True) kernel.add_depends(["memory.h.cog"], [layout]) +kernel.add_depends(["sysconf.h.cog"], [sysconf]) kernel.add_depends(["syscall.cpp.cog", "syscall.h.cog", "syscalls.inc.cog"], definitions) kernel.variables['ldflags'] = ["${ldflags}", "-T", "${source_root}/src/kernel/kernel.ld"] diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index b2e8c9d..78b21f1 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -27,6 +27,7 @@ #include "scheduler.h" #include "serial.h" #include "syscall.h" +#include "sysconf.h" #include "tss.h" #include "vm_space.h" @@ -151,6 +152,7 @@ kernel_main(bootproto::args *args) const auto &apic_ids = devices.get_apic_ids(); g_num_cpus = start_aps(*apic, apic_ids, args->pml4); + sysconf_create(); interrupts_enable(); g_com1.handle_interrupt(); diff --git a/src/kernel/memory.h.cog b/src/kernel/memory.h.cog index 04f66de..4f4f52a 100644 --- a/src/kernel/memory.h.cog +++ b/src/kernel/memory.h.cog @@ -55,6 +55,11 @@ for region in layout.regions: /// through the linear_offset area. constexpr bool linear_mappable(uintptr_t a) { return (a & linear_offset) == 0; } +/// Get the number of pages needed to hold `bytes` bytes +inline constexpr size_t bytes_to_pages(size_t bytes) { + return ((bytes - 1) / frame_size) + 1; +} + /// Convert a physical address to a virtual one (in the linear-mapped area) template T * to_virtual(uintptr_t a) { return reinterpret_cast(a|linear_offset); diff --git a/src/kernel/sysconf.cpp b/src/kernel/sysconf.cpp new file mode 100644 index 0000000..41157af --- /dev/null +++ b/src/kernel/sysconf.cpp @@ -0,0 +1,53 @@ +#include + +#include "assert.h" +#include "cpu.h" +#include "frame_allocator.h" +#include "memory.h" +#include "sysconf.h" + +struct kheader +{ + uint64_t magic; + uint16_t header_len; + uint16_t header_version; + + uint16_t version_major; + uint16_t version_minor; + uint16_t version_patch; + uint16_t _reserved; + uint32_t version_gitsha; + + uint64_t flags; +}; + +extern kheader _kernel_header; + +system_config *g_sysconf = nullptr; +uintptr_t g_sysconf_phys = 0; + +constexpr size_t sysconf_pages = mem::bytes_to_pages(sizeof(system_config)); + +void +sysconf_create() +{ + auto &fa = frame_allocator::get(); + + size_t count = fa.allocate(sysconf_pages, &g_sysconf_phys); + + kassert(count == sysconf_pages, + "Could not get enough contiguous pages for sysconf"); + + g_sysconf = mem::to_virtual(g_sysconf_phys); + memset(g_sysconf, 0, sysconf_pages * mem::frame_size); + + g_sysconf->kernel_version_major = _kernel_header.version_major; + g_sysconf->kernel_version_minor = _kernel_header.version_minor; + g_sysconf->kernel_version_patch = _kernel_header.version_patch; + g_sysconf->kernel_version_gitsha = _kernel_header.version_gitsha; + + g_sysconf->sys_page_size = mem::frame_size; + g_sysconf->sys_large_page_size = 0; + g_sysconf->sys_huge_page_size = 0; + g_sysconf->sys_num_cpus = g_num_cpus; +} diff --git a/src/kernel/sysconf.h.cog b/src/kernel/sysconf.h.cog new file mode 100644 index 0000000..91ca41c --- /dev/null +++ b/src/kernel/sysconf.h.cog @@ -0,0 +1,28 @@ +#pragma once +// vim: ft=cpp + +/// \file sysconf.h +/// Kernel-side implementation of sysconf struct + +/*[[[cog code generation +from os.path import join +from sysconf import Sysconf +sc = Sysconf(join(definitions_path, "sysconf.yaml")) + +cog.outl(f"constexpr uintptr_t sysconf_user_address = {sc.address:#x};") +]]]*/ +///[[[end]]] + +struct system_config +{ + /*[[[cog code generation + for var in sc.vars: + cog.outl(f"{var.type:10} {var.section}_{var.name};") + ]]]*/ + ///[[[end]]] +}; + +extern system_config *g_sysconf; +extern uintptr_t g_sysconf_phys; + +void sysconf_create(); diff --git a/src/kernel/vm_space.cpp b/src/kernel/vm_space.cpp index 76f0cdb..072ef37 100644 --- a/src/kernel/vm_space.cpp +++ b/src/kernel/vm_space.cpp @@ -9,6 +9,7 @@ #include "objects/process.h" #include "objects/thread.h" #include "objects/vm_area.h" +#include "sysconf.h" #include "vm_space.h" // The initial memory for the array of areas for the kernel space @@ -46,6 +47,14 @@ vm_space::vm_space() : memset(m_pml4, 0, mem::frame_size/2); for (unsigned i = arch::kernel_root_index; i < arch::table_entries; ++i) m_pml4->entries[i] = kpml4->entries[i]; + + // Every vm space has sysconf in it + vm_area *sysc = new vm_area_fixed( + g_sysconf_phys, + sizeof(system_config), + vm_flags::none); + + add(sysconf_user_address, sysc); } vm_space::~vm_space() diff --git a/src/libraries/j6/include/j6/sysconf.h.cog b/src/libraries/j6/include/j6/sysconf.h.cog new file mode 100644 index 0000000..29d78e8 --- /dev/null +++ b/src/libraries/j6/include/j6/sysconf.h.cog @@ -0,0 +1,44 @@ +#pragma once +// vim: ft=cpp + +/// \file sysconf.h +/// Get kernel configuration + +// The kernel depends on libj6 for some shared code, +// but should not include the user-specific code. +#ifndef __j6kernel + +#include +#include + +/*[[[cog code generation +from os.path import join +from sysconf import Sysconf +sc = Sysconf(join(definitions_path, "sysconf.yaml")) +]]]*/ +///[[[end]]] + +#ifdef __cplusplus +extern "C" { +#endif + +enum j6_sysconf_arg +{ + /*[[[cog code generation + for var in sc.vars: + cog.outl(f"j6sc_{var.name},") + ]]]*/ + ///[[[end]]] + + j6sc_MAX +}; + +/// Get the kernel configuration value specified by +/// the argument. +unsigned long j6_sysconf(j6_sysconf_arg arg); + +#ifdef __cplusplus +} // extern C +#endif + +#endif // __j6kernel diff --git a/src/libraries/j6/j6.module b/src/libraries/j6/j6.module index ea564e8..ef4c13c 100644 --- a/src/libraries/j6/j6.module +++ b/src/libraries/j6/j6.module @@ -6,10 +6,15 @@ j6 = module("j6", sources = [ "init.cpp", "include/j6/syscalls.h.cog", + "include/j6/sysconf.h.cog", "syscalls.s.cog", + "sysconf.cpp.cog", ]) from glob import glob +from os.path import join + +sysconf = join(source_root, "definitions/sysconf.yaml") definitions = glob('definitions/**/*.def', recursive=True) j6.add_depends([ @@ -17,3 +22,8 @@ j6.add_depends([ "syscalls.s.cog", ], definitions) +j6.add_depends([ + "include/j6/sysconf.h.cog", + "sysconf.cpp.cog", + ], [sysconf]) + diff --git a/src/libraries/j6/sysconf.cpp.cog b/src/libraries/j6/sysconf.cpp.cog new file mode 100644 index 0000000..f10d2dd --- /dev/null +++ b/src/libraries/j6/sysconf.cpp.cog @@ -0,0 +1,44 @@ +// vim: ft=cpp + +// The kernel depends on libj6 for some shared code, +// but should not include the user-specific code. +#ifndef __j6kernel + +#include + +/*[[[cog code generation +from os.path import join +from sysconf import Sysconf +sc = Sysconf(join(definitions_path, "sysconf.yaml")) + +cog.outl(f"constexpr uintptr_t __sysconf_address = {sc.address:#x};") +]]]*/ +///[[[end]]] + +struct __system_config +{ + /*[[[cog code generation + for var in sc.vars: + cog.outl(f"{var.type:10} {var.section}_{var.name};") + ]]]*/ + ///[[[end]]] +}; + +unsigned long +j6_sysconf(j6_sysconf_arg arg) +{ + __system_config &sc = + * reinterpret_cast<__system_config*>(__sysconf_address); + + switch (arg) { + /*[[[cog code generation + for var in sc.vars: + cog.outl(f"case j6sc_{var.name}: return sc.{var.section}_{var.name};") + ]]]*/ + ///[[[end]]] + + default: return 0; + } +} + +#endif // __j6kernel