Files
LithosAnanke/src/starkernel/hal/host_services.c
T

278 lines
8.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This 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.
See the License for the specific language governing permissions and
limitations under the License.
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This 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.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* vm_host.c - Kernel host services implementation
*
* Provides kernel implementations of host service hooks.
* All VM allocations/time/mutex go through these functions.
*/
#ifdef __STARKERNEL__
#include "vm_host.h"
#include "starkernel/hal/hal.h"
#include "kmalloc.h"
#include "console.h"
#include "timer.h"
#include "arch.h"
#include "starkernel/vm/arena.h"
#include <stdbool.h>
/**
* Parity mode: deterministic fake time
* Increments by 1000ns (1us) per call for reproducibility
*/
#ifndef PARITY_MODE
#define PARITY_MODE 0
#endif
static VMHostServices kernel_services;
static uint64_t parity_fake_ns = 0;
static bool host_services_initialized = false;
static bool host_services_logged = false;
/* ============================================================================
* Memory Allocation
* ============================================================================ */
static void* kernel_alloc(size_t size, size_t align) {
if (size == 0) {
return (void*)0;
}
size_t arena_size = sk_vm_arena_size();
if (size == arena_size) {
if (!sk_vm_arena_is_initialized()) {
/* Hera (first call): use the PMM-backed arena singleton */
if (sk_vm_arena_alloc() == 0) {
return NULL;
}
return sk_vm_arena_ptr();
}
/* Baby VM: each needs its own isolated region; returning the singleton
* here causes all VMs to share Hera's memory, overwriting her compiled
* word bodies when the baby registers its dictionary. */
void *p = (align <= sizeof(void*)) ? kmalloc(size)
: kmalloc_aligned(size, align);
if (p) {
uint8_t *b = (uint8_t *)p;
size_t i;
for (i = 0; i < size; i++) b[i] = 0;
}
return p;
}
void *ptr;
if (align <= sizeof(void*)) {
ptr = kmalloc(size);
} else {
ptr = kmalloc_aligned(size, align);
}
return ptr;
}
static void kernel_free(void *ptr) {
if (!ptr) {
return;
}
/* VM arena lifetime is managed separately; ignore free requests */
if (ptr == sk_vm_arena_ptr()) {
return;
}
kfree(ptr);
}
/* ============================================================================
* Monotonic Time
* ============================================================================ */
static uint64_t kernel_monotonic_ns(void) {
#if PARITY_MODE
/* Deterministic: 1us per call */
parity_fake_ns += 1000;
return parity_fake_ns;
#else
/* Delegate to timer_now_ns() rather than reimplementing TSC->ns
* conversion here. This used to check timer_tsc_hz() itself and give
* up (return 0) whenever TSC calibration failed -- which it reliably
* does under QEMU/TCG (no invariant TSC, CPUID/PM-Timer frequency
* derivation both fail: see "Timer: trust=1 (REL), TSC=0 Hz" in the
* boot log). timer_now_ns() already has the correct fallback for
* exactly this case (RELATIVE mode via the ACPI PM Timer,
* timer_now_ns_vm_relative()) that this function never used. Every
* caller of vm_monotonic_ns() -- including vm_physics_touch and
* word-level background decay -- was silently receiving now_ns=0
* forever as a result. Found via VM-FLEET-ATTRACTOR-DESIGN-20260705.md
* rev g/h. */
return timer_now_ns();
#endif
}
/* ============================================================================
* Mutex Operations (No-op in single-threaded kernel)
* ============================================================================ */
static int kernel_mutex_init(void *mutex) {
(void)mutex;
return 0; /* Success */
}
static int kernel_mutex_lock(void *mutex) {
(void)mutex;
return 0; /* Success */
}
static int kernel_mutex_unlock(void *mutex) {
(void)mutex;
return 0; /* Success */
}
static void kernel_mutex_destroy(void *mutex) {
(void)mutex;
/* Nothing to do */
}
/* ============================================================================
* Console Output
* ============================================================================ */
static int kernel_puts(const char *str) {
if (!str) return -1;
console_puts(str);
/* Count characters (console_puts doesn't return count) */
int count = 0;
while (str[count]) count++;
return count;
}
static int kernel_putc(int c) {
console_putc((char)c);
return c;
}
static bool kernel_xt_entry_owned(const void *ptr, size_t bytes) {
if (!ptr || bytes == 0) {
return false;
}
uintptr_t addr = (uintptr_t)ptr;
uintptr_t entry_end = addr + bytes;
if (entry_end < addr) {
return false; /* Overflow check */
}
/* Check if in VM arena (dictionary entries are allocated here) */
if (sk_vm_arena_is_initialized()) {
uint8_t *arena_ptr = sk_vm_arena_ptr();
size_t arena_size = sk_vm_arena_size();
uintptr_t arena_base = (uintptr_t)arena_ptr;
uintptr_t arena_end = arena_base + arena_size;
if (addr >= arena_base && entry_end <= arena_end) {
return true;
}
}
/* Check if in kmalloc heap (for other allocations) */
uintptr_t heap_base = kmalloc_heap_base_addr();
uintptr_t heap_end = kmalloc_heap_end_addr();
if (heap_base != 0 && heap_end != 0) {
if (addr >= heap_base && entry_end <= heap_end) {
return true;
}
}
return false;
}
/* Static wrappers for HAL functions - avoids GOT references in PE.
* With -fPIC, cross-TU function pointer assignments use GOT-relative loads,
* but PE files don't have a working GOT. These wrappers provide local
* addresses that use RIP-relative LEA instead of GOT loads. */
static bool kernel_is_executable_ptr(const void *ptr) {
return sk_hal_is_executable_ptr(ptr);
}
static void kernel_panic(const char *message) {
sk_hal_panic(message);
}
static void host_services_dump_table(void) {
host_services_logged = true;
}
const VMHostServices* sk_host_services(void) {
if (!host_services_initialized) {
sk_host_init();
}
return &kernel_services;
}
void sk_host_init(void) {
if (host_services_initialized) {
host_services_dump_table();
return;
}
/* Reset parity time if needed */
parity_fake_ns = 0;
kernel_services.alloc = kernel_alloc;
kernel_services.free = kernel_free;
kernel_services.monotonic_ns = kernel_monotonic_ns;
kernel_services.mutex_init = kernel_mutex_init;
kernel_services.mutex_lock = kernel_mutex_lock;
kernel_services.mutex_unlock = kernel_mutex_unlock;
kernel_services.mutex_destroy = kernel_mutex_destroy;
kernel_services.puts = kernel_puts;
kernel_services.putc = kernel_putc;
kernel_services.is_executable_ptr = kernel_is_executable_ptr;
kernel_services.owns_xt_entry = kernel_xt_entry_owned;
kernel_services.panic = kernel_panic;
kernel_services.parity_mode = PARITY_MODE;
kernel_services.verbose = 0;
host_services_initialized = true;
host_services_dump_table();
}
#endif /* __STARKERNEL__ */