Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure
Source tree reorganization: - Move StarForth v3 engine to v3/ (src/, include/, Makefile) - Move kernel to kernel/ (src/, include/, linker/, Makefile) - Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md) - Move FABRIC-0..4.md to docs/fabric/ - Move ONTOLOGY.md and ROADMAP.md to docs/ Board infrastructure: - Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/ - Each board has board.mk (ISA, CPU flags, boot recipe) and README.md - Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET - make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board - ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet) - scripts/mkdiskimage.sh builds disk images for all boards Docs pipeline: - docs/book/ with LaTeX master (main.tex) and Makefile - pandoc converts Markdown to LaTeX at build time - Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks) - make docs builds single PDF (754 pages, 0 missing characters) - make docs TARGET=<board> adds board appendix - build/docs/<book|board>/meta.tex stamps git commit into PDF Bug fixes: - 42 include paths that only worked by accident now use correct relative paths - clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build) - Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved - Doxyfile, .clang-tidy, README.md, Kconfig paths updated Verified: - Hosted v3 build passes 1012 tests, 0 failures - SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE) - Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes - make clean TARGET=<board> removes only that board and its ISA objects - make all builds all boards, hosted v3, and docs in one run Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
# src/starkernel/boot/
|
||||
|
||||
UEFI boot, kernel-image loading, and boot-time command-line handling.
|
||||
|
||||
- `uefi_loader.c` — the UEFI application entry point (`BOOTX64.EFI`);
|
||||
calls `ExitBootServices()` to take ownership of hardware, builds
|
||||
`BootInfo` (memory map, ACPI, framebuffer), and hands off to
|
||||
`kernel_main()`.
|
||||
- `elf_loader.c` — ELF64 kernel image parsing/loading.
|
||||
- `cmdline.c` — parses the boot-time kernel command line / `starforth.cfg`
|
||||
contents (see `include/starkernel/cmdline.h`, `kernel_args.h`).
|
||||
- `reloc_stub.c`, `reloc.S` — position-independent relocation support for
|
||||
the loaded image.
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* cmdline.c — kernel boot command-line parser
|
||||
*
|
||||
* Pure C99 / freestanding. No UEFI types, no libc.
|
||||
* The caller (uefi_loader.c) converts LoadOptions from UCS-2 to ASCII
|
||||
* before passing the string here.
|
||||
*/
|
||||
|
||||
#include "starkernel/cmdline.h"
|
||||
#include "starkernel/kernel_args.h"
|
||||
|
||||
/* ---- freestanding string helpers --------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Test whether a character is ASCII whitespace.
|
||||
*
|
||||
* Returns non-zero for space (@c ' '), horizontal tab (@c '\t'),
|
||||
* carriage return (@c '\r'), and line feed (@c '\n'). Used by
|
||||
* @c cmdline_parse_ascii() to skip inter-token whitespace and by
|
||||
* @c load_cfg_from_esp() to strip trailing CR/LF from the config file.
|
||||
* Replaces the libc @c isspace() which is not available in a freestanding
|
||||
* build; this version does not consider locale.
|
||||
*
|
||||
* @param c Character to test.
|
||||
* @return Non-zero if @p c is ASCII whitespace, 0 otherwise.
|
||||
*/
|
||||
static int sf_isspace(char c) {
|
||||
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare at most @p n characters of two strings (freestanding).
|
||||
*
|
||||
* Iterates up to @p n bytes, stopping at the first differing character
|
||||
* or when either string's null terminator is reached. Returns 0 if the
|
||||
* first @p n characters are equal (or both strings end before @p n),
|
||||
* positive if @p a is lexicographically greater, negative if @p b is.
|
||||
* Comparison is on @c unsigned @c char values per C standard convention.
|
||||
*
|
||||
* Used exclusively by @c cmdline_parse_ascii() and @c parse_log_level()
|
||||
* to match fixed-length flag prefixes without a libc dependency.
|
||||
*
|
||||
* @param a First string.
|
||||
* @param b Second string.
|
||||
* @param n Maximum number of characters to compare.
|
||||
* @return 0 if equal, positive if @p a > @p b, negative if @p a < @p b.
|
||||
*/
|
||||
static int sf_strncmp(const char *a, const char *b, unsigned int n) {
|
||||
while (n && *a && *a == *b) { a++; b++; n--; }
|
||||
if (!n) return 0;
|
||||
return (unsigned char)*a - (unsigned char)*b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a string into a fixed-size buffer with guaranteed NUL termination.
|
||||
*
|
||||
* Copies at most @p n-1 bytes from @p src into @p dst and always writes
|
||||
* a NUL terminator at @c dst[@p n-1] (if @p n > 0). This is the
|
||||
* BSD @c strlcpy() semantics without returning the source length.
|
||||
* If @p n is 0 the function is a no-op (no write, no UB).
|
||||
*
|
||||
* Used by @c cmdline_parse_ascii() to copy the raw command-line string
|
||||
* into @c KernelArgs.raw with a guaranteed NUL, avoiding the truncation
|
||||
* risk of @c strncpy().
|
||||
*
|
||||
* @param dst Destination buffer; receives at most @p n-1 bytes of @p src
|
||||
* followed by a NUL terminator.
|
||||
* @param src NUL-terminated source string.
|
||||
* @param n Size of @p dst in bytes (including the NUL slot).
|
||||
*/
|
||||
static void sf_strlcpy(char *dst, const char *src, unsigned int n) {
|
||||
unsigned int i;
|
||||
for (i = 0; i + 1 < n && src[i]; i++) dst[i] = src[i];
|
||||
if (n) dst[i] = '\0';
|
||||
}
|
||||
|
||||
/* ---- Size-suffix parser ------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Parse a decimal integer with optional K/M/G size suffix.
|
||||
*
|
||||
* Reads a non-negative decimal integer from @p s and multiplies it by:
|
||||
* - 1024 for 'K' or 'k' (kibibytes)
|
||||
* - 1024² for 'M' or 'm' (mebibytes)
|
||||
* - 1024³ for 'G' or 'g' (gibibytes)
|
||||
* - 1 for any other or absent suffix
|
||||
*
|
||||
* Returns 0 if @p s is NULL, empty, or does not begin with a digit.
|
||||
* Overflow is not checked; caller must validate the resulting value
|
||||
* is sensible for its intended use (stack size, heap size, etc.).
|
||||
*
|
||||
* Used by @c cmdline_parse_ascii() to decode the @c --stack=@<size@> and
|
||||
* @c --heap=@<size@> arguments from the boot command line.
|
||||
*
|
||||
* @param s NUL-terminated string beginning with a decimal integer
|
||||
* and optionally followed by a size suffix character.
|
||||
* @return Parsed byte count (0 on parse failure or zero input).
|
||||
*/
|
||||
uint64_t cmdline_parse_size(const char *s) {
|
||||
uint64_t val = 0;
|
||||
if (!s || !(*s >= '0' && *s <= '9')) return 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
val = val * 10U + (uint64_t)(*s - '0');
|
||||
s++;
|
||||
}
|
||||
if (*s == 'K' || *s == 'k') val *= 1024ULL;
|
||||
else if (*s == 'M' || *s == 'm') val *= 1024ULL * 1024ULL;
|
||||
else if (*s == 'G' || *s == 'g') val *= 1024ULL * 1024ULL * 1024ULL;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* ---- Log-level parser -------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Map a log-level name string to a @c KARGS_LOG_* constant.
|
||||
*
|
||||
* Performs case-insensitive prefix matching against the known level names:
|
||||
* - "debug" / "DEBUG" → @c KARGS_LOG_DEBUG
|
||||
* - "info" / "INFO" → @c KARGS_LOG_INFO
|
||||
* - "warn" / "WARN" → @c KARGS_LOG_WARN
|
||||
* - "error" / "ERROR" → @c KARGS_LOG_ERROR
|
||||
*
|
||||
* Any unrecognised string (including empty) maps to @c KARGS_LOG_INFO
|
||||
* as a safe default. Called from @c cmdline_parse_ascii() after
|
||||
* recognising the @c --log-level= prefix.
|
||||
*
|
||||
* @param s String beginning at the first character of the level name
|
||||
* (i.e., after the @c "=" separator has been skipped by the caller).
|
||||
* @return One of the @c KARGS_LOG_* constants defined in @c kernel_args.h;
|
||||
* defaults to @c KARGS_LOG_INFO for unknown input.
|
||||
*/
|
||||
static int parse_log_level(const char *s) {
|
||||
if (sf_strncmp(s, "debug", 5) == 0 || sf_strncmp(s, "DEBUG", 5) == 0)
|
||||
return KARGS_LOG_DEBUG;
|
||||
if (sf_strncmp(s, "info", 4) == 0 || sf_strncmp(s, "INFO", 4) == 0)
|
||||
return KARGS_LOG_INFO;
|
||||
if (sf_strncmp(s, "warn", 4) == 0 || sf_strncmp(s, "WARN", 4) == 0)
|
||||
return KARGS_LOG_WARN;
|
||||
if (sf_strncmp(s, "error", 5) == 0 || sf_strncmp(s, "ERROR", 5) == 0)
|
||||
return KARGS_LOG_ERROR;
|
||||
return KARGS_LOG_INFO; /* unknown → info (safe default) */
|
||||
}
|
||||
|
||||
/* ---- Main parser ------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Parse a NUL-terminated ASCII command-line string into @c KernelArgs.
|
||||
*
|
||||
* Populates @p args from space-separated tokens in @p cmdline. Always
|
||||
* initialises all fields to their defaults first (so partial or empty
|
||||
* command lines produce safe values):
|
||||
* - @c stack_size = 0 (use BSS static stack)
|
||||
* - @c heap_size = 0 (use built-in kmalloc arena)
|
||||
* - @c run_doe = @c KARGS_DEFAULT_RUN_DOE
|
||||
* - @c log_level = @c KARGS_DEFAULT_LOG_LEVEL
|
||||
* - @c raw[0] = '\0'
|
||||
*
|
||||
* Recognised flags:
|
||||
* - @c --doe — sets @c args->run_doe = 1.
|
||||
* - @c --log-level=@<level@> — sets @c args->log_level via
|
||||
* @c parse_log_level().
|
||||
* - @c --stack=@<size@> — sets @c args->stack_size via
|
||||
* @c cmdline_parse_size().
|
||||
* - @c --heap=@<size@> — sets @c args->heap_size via
|
||||
* @c cmdline_parse_size().
|
||||
*
|
||||
* Unknown flags are silently ignored for forward-compatibility with
|
||||
* flags introduced in future kernel versions. The raw command-line string
|
||||
* is copied verbatim into @c args->raw (truncated to
|
||||
* @c KERNEL_ARGS_CMDLINE_MAX - 1) for diagnostic logging.
|
||||
*
|
||||
* Called from @c efi_main() (in @c uefi_loader.c) after assembling the
|
||||
* ASCII command line from the three priority sources: NVRAM variable,
|
||||
* @c starforth.cfg on the ESP, and EFI @c LoadOptions.
|
||||
*
|
||||
* @param cmdline NUL-terminated ASCII command line; may be NULL or empty.
|
||||
* @param args Output @c KernelArgs structure; must not be NULL.
|
||||
*/
|
||||
void cmdline_parse_ascii(const char *cmdline, KernelArgs *args) {
|
||||
const char *p;
|
||||
const char *tok;
|
||||
unsigned int tok_len;
|
||||
|
||||
/* Fill defaults first */
|
||||
args->stack_size = 0;
|
||||
args->heap_size = 0;
|
||||
args->run_doe = KARGS_DEFAULT_RUN_DOE;
|
||||
args->log_level = KARGS_DEFAULT_LOG_LEVEL;
|
||||
args->raw[0] = '\0';
|
||||
|
||||
if (!cmdline || !*cmdline) return;
|
||||
|
||||
sf_strlcpy(args->raw, cmdline, KERNEL_ARGS_CMDLINE_MAX);
|
||||
|
||||
p = cmdline;
|
||||
while (*p) {
|
||||
/* Skip leading whitespace */
|
||||
while (*p && sf_isspace(*p)) p++;
|
||||
if (!*p) break;
|
||||
|
||||
/* Measure token */
|
||||
tok = p;
|
||||
while (*p && !sf_isspace(*p)) p++;
|
||||
tok_len = (unsigned int)(p - tok);
|
||||
|
||||
/* --doe */
|
||||
if (tok_len == 5 && sf_strncmp(tok, "--doe", 5) == 0) {
|
||||
args->run_doe = 1;
|
||||
|
||||
/* --log-level=<level> (prefix = 12 chars) */
|
||||
} else if (tok_len > 12 && sf_strncmp(tok, "--log-level=", 12) == 0) {
|
||||
args->log_level = parse_log_level(tok + 12);
|
||||
|
||||
/* --stack=<size> (prefix = 8 chars) */
|
||||
} else if (tok_len > 8 && sf_strncmp(tok, "--stack=", 8) == 0) {
|
||||
args->stack_size = cmdline_parse_size(tok + 8);
|
||||
|
||||
/* --heap=<size> (prefix = 7 chars) */
|
||||
} else if (tok_len > 7 && sf_strncmp(tok, "--heap=", 7) == 0) {
|
||||
args->heap_size = cmdline_parse_size(tok + 7);
|
||||
}
|
||||
/* Unknown flags are silently ignored (forward-compatibility) */
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* reloc.S - Minimal .reloc section for UEFI PE32+ loader
|
||||
*
|
||||
* UEFI firmware requires a .reloc section for base relocations.
|
||||
* This provides a minimal placeholder that objcopy can recognize
|
||||
* when converting ELF to PE32+ format.
|
||||
*/
|
||||
|
||||
#if defined(__x86_64__) || defined(ARCH_AMD64)
|
||||
|
||||
.section .reloc, "a"
|
||||
.balign 4
|
||||
dummy_reloc:
|
||||
.long 0
|
||||
|
||||
#elif defined(__aarch64__) || defined(ARCH_AARCH64)
|
||||
|
||||
.section .reloc, "a"
|
||||
.balign 4
|
||||
dummy_reloc:
|
||||
.word 0
|
||||
|
||||
#elif defined(__riscv) || defined(ARCH_RISCV64)
|
||||
|
||||
.section .reloc, "a"
|
||||
.balign 4
|
||||
dummy_reloc:
|
||||
.word 0
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* reloc_stub.c - Provide a minimal .reloc section for PE/COFF EFI loader
|
||||
*
|
||||
* Some firmware refuses to load images without a Base Relocation Directory.
|
||||
* This stub supplies an empty relocation block to satisfy the loader.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief PE/COFF Base Relocation Block header.
|
||||
*
|
||||
* Mirrors the @c IMAGE_BASE_RELOCATION layout from the PE specification:
|
||||
* - @c page_rva — RVA of the 4 KB page that the following entries apply to.
|
||||
* - @c block_size — total byte size of this block including the header and
|
||||
* all entries (minimum 8 bytes for the header alone).
|
||||
* - @c entry — one or more 16-bit relocation entries packed as
|
||||
* TYPE (4 bits) | OFFSET (12 bits); TYPE=0 / OFFSET=0
|
||||
* is a no-op padding entry used to 32-bit-align the block.
|
||||
*
|
||||
* Marked @c packed to suppress any padding the compiler might insert between
|
||||
* fields; @c aligned(4) ensures the block sits on a 4-byte boundary as
|
||||
* required by the PE loader.
|
||||
*/
|
||||
struct reloc_block {
|
||||
uint32_t page_rva;
|
||||
uint32_t block_size;
|
||||
uint16_t entry; /* TYPE=0, OFFSET=0 */
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/**
|
||||
* @brief Minimal @c .reloc section stub to satisfy UEFI firmware loaders.
|
||||
*
|
||||
* Some UEFI implementations (notably certain hardware vendor firmware and
|
||||
* strict OVMF builds) refuse to load a PE32+ image that lacks a Base
|
||||
* Relocation Directory entry in the optional-header Data Directory. Even
|
||||
* for monolithic position-independent images that require no run-time fix-ups,
|
||||
* the presence of the @c .reloc section with a well-formed (but empty)
|
||||
* relocation block is required to pass the firmware's image validation.
|
||||
*
|
||||
* This single statically-allocated @c reloc_block satisfies the requirement:
|
||||
* - @c page_rva = 0 — applies to the first 4 KB page (convention for stubs).
|
||||
* - @c block_size = sizeof(reloc_block) — minimal well-formed block (8 bytes
|
||||
* + 2 bytes for the single no-op entry = 10 bytes).
|
||||
* - @c entry = 0 — TYPE=0 (absolute, no-op) / OFFSET=0; ignored by the loader.
|
||||
*
|
||||
* The @c used attribute prevents the compiler/linker from discarding the
|
||||
* symbol as unreferenced; the @c section(".reloc") attribute places it
|
||||
* in the named PE section so the linker emits the correct Data Directory.
|
||||
*/
|
||||
__attribute__((section(".reloc"), used))
|
||||
static const struct reloc_block reloc_stub = {
|
||||
.page_rva = 0,
|
||||
.block_size = sizeof(struct reloc_block),
|
||||
.entry = 0
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user