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,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) */
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user