136 lines
4.0 KiB
Zig
136 lines
4.0 KiB
Zig
// SPDX-License-Identifier: LCL-1.0
|
|
// Copyright (c) 2026 Markus Maiwald
|
|
// Stewardship: Self Sovereign Society Foundation
|
|
//
|
|
// This file is part of the Nexus Commonwealth.
|
|
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
|
|
|
//! Rumpk Layer 0: libc Stubs (Bump Allocator)
|
|
//!
|
|
//! We are the standard library now. Provides malloc/free/realloc/calloc
|
|
//! for libraries that expect libc (e.g., LwIP).
|
|
//!
|
|
//! DECISION(Alloc): Bump allocator chosen for simplicity and determinism.
|
|
//! Memory is never reclaimed; system reboots to reset.
|
|
|
|
const uart = @import("uart.zig");
|
|
|
|
// =========================================================
|
|
// Heap Stubs (Bump Allocator with Block Headers)
|
|
// =========================================================
|
|
|
|
// Simple Bump Allocator for L0
|
|
// SAFETY(Heap): Memory is written by malloc before any read occurs.
|
|
// Initialized to `undefined` to avoid zeroing 32MB at boot.
|
|
var heap: [96 * 1024 * 1024]u8 align(4096) = undefined;
|
|
var heap_idx: usize = 0;
|
|
var heap_init_done: bool = false;
|
|
|
|
export fn debug_print(s: [*]const u8, len: usize) void {
|
|
uart.print(s[0..len]);
|
|
}
|
|
|
|
// Header structure (64 bytes aligned to match LwIP MEM_ALIGNMENT)
|
|
const BlockHeader = struct {
|
|
size: usize,
|
|
_pad: [64 - @sizeOf(usize)]u8,
|
|
};
|
|
|
|
export fn malloc(size: usize) ?*anyopaque {
|
|
if (size == 0) return null;
|
|
|
|
if (!heap_init_done) {
|
|
if (heap_idx != 0) {
|
|
uart.print("[Alloc] WARNING: BSS NOT ZEROED! heap_idx: ");
|
|
uart.print_hex(heap_idx);
|
|
uart.print("\n");
|
|
heap_idx = 0;
|
|
}
|
|
heap_init_done = true;
|
|
}
|
|
|
|
const total_needed = size + @sizeOf(BlockHeader);
|
|
const align_mask: usize = 63; // 64-byte alignment
|
|
const aligned_idx = (heap_idx + align_mask) & ~align_mask;
|
|
|
|
if (aligned_idx + total_needed > heap.len) {
|
|
uart.print("[Alloc] OOM! Size: ");
|
|
uart.print_hex(size);
|
|
uart.print(" Used: ");
|
|
uart.print_hex(heap_idx);
|
|
uart.print("\n");
|
|
return null;
|
|
}
|
|
|
|
// Trace allocations (disabled to reduce noise)
|
|
// uart.print("[Alloc] ");
|
|
// uart.print_hex(size);
|
|
// uart.print(" -> Used: ");
|
|
// uart.print_hex(aligned_idx + total_needed);
|
|
// uart.print("\n");
|
|
|
|
const base_ptr = &heap[aligned_idx];
|
|
const header = @as(*BlockHeader, @ptrCast(@alignCast(base_ptr)));
|
|
header.size = size;
|
|
|
|
heap_idx = aligned_idx + total_needed;
|
|
return @as(*anyopaque, @ptrFromInt(@intFromPtr(base_ptr) + @sizeOf(BlockHeader)));
|
|
}
|
|
|
|
export fn free(ptr: ?*anyopaque) void {
|
|
// Bump allocator: no-op free.
|
|
_ = ptr;
|
|
}
|
|
|
|
export fn realloc(ptr: ?*anyopaque, size: usize) ?*anyopaque {
|
|
if (ptr == null) return malloc(size);
|
|
if (size == 0) {
|
|
free(ptr);
|
|
return null;
|
|
}
|
|
|
|
// Retrieve old size from header
|
|
const base_addr = @intFromPtr(ptr.?) - @sizeOf(BlockHeader);
|
|
const header = @as(*BlockHeader, @ptrFromInt(base_addr));
|
|
const old_size = header.size;
|
|
|
|
// Optimization: If new size is smaller and it's the last block, we could shrink?
|
|
// But for a bump allocator, just allocate new.
|
|
const new_ptr = malloc(size);
|
|
if (new_ptr) |np| {
|
|
const copy_size = if (size < old_size) size else old_size;
|
|
const src = @as([*]const u8, @ptrCast(ptr.?));
|
|
const dst = @as([*]u8, @ptrCast(np));
|
|
|
|
var i: usize = 0;
|
|
while (i < copy_size) : (i += 1) {
|
|
dst[i] = src[i];
|
|
}
|
|
|
|
free(ptr);
|
|
return np;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export fn calloc(nmemb: usize, size: usize) ?*anyopaque {
|
|
const total = nmemb * size;
|
|
const ptr = malloc(total);
|
|
if (ptr) |p| {
|
|
const dst = @as([*]u8, @ptrCast(p));
|
|
var i: usize = 0;
|
|
while (i < total) : (i += 1) {
|
|
dst[i] = 0;
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
// =========================================================
|
|
// Rumpk Runtime Support
|
|
// =========================================================
|
|
|
|
export fn get_ticks() u32 {
|
|
return 0; // TODO: Implement real timer
|
|
}
|