[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:
This commit is contained in:
Justin C. Miller
2022-01-13 22:08:35 -08:00
parent 939022bb5e
commit b3aaddadc8
11 changed files with 250 additions and 0 deletions

34
definitions/sysconf.yaml Normal file
View File

@@ -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

17
scripts/sysconf.py Normal file
View File

@@ -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)

View File

@@ -60,6 +60,8 @@ kernel = module("kernel",
"syscalls/system.cpp", "syscalls/system.cpp",
"syscalls/thread.cpp", "syscalls/thread.cpp",
"syscalls/vm_area.cpp", "syscalls/vm_area.cpp",
"sysconf.cpp",
"sysconf.h.cog",
"task.s", "task.s",
"tss.cpp", "tss.cpp",
"vm_space.cpp", "vm_space.cpp",
@@ -69,9 +71,11 @@ from glob import glob
from os.path import join from os.path import join
layout = join(source_root, "definitions/memory_layout.yaml") layout = join(source_root, "definitions/memory_layout.yaml")
sysconf = join(source_root, "definitions/sysconf.yaml")
definitions = glob('definitions/**/*.def', recursive=True) definitions = glob('definitions/**/*.def', recursive=True)
kernel.add_depends(["memory.h.cog"], [layout]) 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.add_depends(["syscall.cpp.cog", "syscall.h.cog", "syscalls.inc.cog"], definitions)
kernel.variables['ldflags'] = ["${ldflags}", "-T", "${source_root}/src/kernel/kernel.ld"] kernel.variables['ldflags'] = ["${ldflags}", "-T", "${source_root}/src/kernel/kernel.ld"]

View File

@@ -27,6 +27,7 @@
#include "scheduler.h" #include "scheduler.h"
#include "serial.h" #include "serial.h"
#include "syscall.h" #include "syscall.h"
#include "sysconf.h"
#include "tss.h" #include "tss.h"
#include "vm_space.h" #include "vm_space.h"
@@ -151,6 +152,7 @@ kernel_main(bootproto::args *args)
const auto &apic_ids = devices.get_apic_ids(); const auto &apic_ids = devices.get_apic_ids();
g_num_cpus = start_aps(*apic, apic_ids, args->pml4); g_num_cpus = start_aps(*apic, apic_ids, args->pml4);
sysconf_create();
interrupts_enable(); interrupts_enable();
g_com1.handle_interrupt(); g_com1.handle_interrupt();

View File

@@ -55,6 +55,11 @@ for region in layout.regions:
/// through the linear_offset area. /// through the linear_offset area.
constexpr bool linear_mappable(uintptr_t a) { return (a & linear_offset) == 0; } 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) /// Convert a physical address to a virtual one (in the linear-mapped area)
template <typename T> T * to_virtual(uintptr_t a) { template <typename T> T * to_virtual(uintptr_t a) {
return reinterpret_cast<T*>(a|linear_offset); return reinterpret_cast<T*>(a|linear_offset);

53
src/kernel/sysconf.cpp Normal file
View File

@@ -0,0 +1,53 @@
#include <string.h>
#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<system_config>(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;
}

28
src/kernel/sysconf.h.cog Normal file
View File

@@ -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();

View File

@@ -9,6 +9,7 @@
#include "objects/process.h" #include "objects/process.h"
#include "objects/thread.h" #include "objects/thread.h"
#include "objects/vm_area.h" #include "objects/vm_area.h"
#include "sysconf.h"
#include "vm_space.h" #include "vm_space.h"
// The initial memory for the array of areas for the kernel space // 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); memset(m_pml4, 0, mem::frame_size/2);
for (unsigned i = arch::kernel_root_index; i < arch::table_entries; ++i) for (unsigned i = arch::kernel_root_index; i < arch::table_entries; ++i)
m_pml4->entries[i] = kpml4->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() vm_space::~vm_space()

View File

@@ -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 <stddef.h>
#include <stdint.h>
/*[[[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

View File

@@ -6,10 +6,15 @@ j6 = module("j6",
sources = [ sources = [
"init.cpp", "init.cpp",
"include/j6/syscalls.h.cog", "include/j6/syscalls.h.cog",
"include/j6/sysconf.h.cog",
"syscalls.s.cog", "syscalls.s.cog",
"sysconf.cpp.cog",
]) ])
from glob import glob from glob import glob
from os.path import join
sysconf = join(source_root, "definitions/sysconf.yaml")
definitions = glob('definitions/**/*.def', recursive=True) definitions = glob('definitions/**/*.def', recursive=True)
j6.add_depends([ j6.add_depends([
@@ -17,3 +22,8 @@ j6.add_depends([
"syscalls.s.cog", "syscalls.s.cog",
], definitions) ], definitions)
j6.add_depends([
"include/j6/sysconf.h.cog",
"sysconf.cpp.cog",
], [sysconf])

View File

@@ -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 <j6/sysconf.h>
/*[[[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