From f076c97c606567eb4cb99f281fb81d73b0889cd3 Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Fri, 11 Sep 2026 16:17:33 +0530 Subject: [PATCH 01/13] fix: repair master after the 09-08 batch merge Nine PRs were merged into master within minutes on 09-08, each on the base it was written against. Master has not compiled since, and the Python guards that would have named the rest never ran because the C build failed first. Compile (every C job, CodeQL, and the ARM64 kernel in the simulation): - services/linux/src/linux_security.c: #119 and #132 each added an #else to the same #ifndef _WIN32 in eos_busybox_install_to_rootfs(), so master has "#else after #else". The #132 arm (`(void)bb;`) is the one removed -- the #119 arm already uses bb and reports the unsupported platform. Guards from #121 / #93 that later merges walked back: - ci.yml: #132 added windows-test after #121's gate; the gate did not wait for it, so "CI Gate" could be green with the MSVC leg red. - test_ci_gate.py: #129 gave eosim-sanity.yml and simulation-test.yml a path-filtered pull_request trigger (they test their own edits); a path-filtered check cannot be required, so both are recorded in NOT_REQUIRED with the book-build.yml reason. - tests/CMakeLists.txt: test_linux_security_paths (#119) and test_pkg_fetch (#115) had no add_executable(). #115's replay replaced #119's block with its own, and #118's replay replaced that; two suites compiled against nothing. Both registered again. 41 -> 43 suites. - tests/test_kernel.c: four tests from #130 and #131 were defined and never called -- their RUN() lines did not survive the replay of main(). And the one that was not bookkeeping: - kernel/src/task.c: #130 was merged after #131 from a base that predates it, and its copy of task.c replaced #131's. #131 had also flattened 574 CRLF line endings, so its 1166-line diff hid a 39/21 change and the replay took #130's side wholesale. Master kept #131's tests and lost its kernel: the idle task could be deleted and suspended, a half-initialised TCB was published to the scheduler before its stack existed, and eos_schedule() pointed g_current_sp at the outgoing task. test_idle_task_is_permanent fails on master the moment it is called. #131's task.c diff re-applied on top of #130's; the result is #131's file plus #130's wake_armed hunk and nothing else (verified by diff against 3a00bd9). Verified locally (macOS, clang): Release build clean, 43/43 ctest; the README default configuration builds; 47/47 pytest. Not in this PR: the nightly "Upstream drift" job builds eBoot at master and eBoot master is broken separately (its own repair PR); bump EBOOT_COMMIT in eos-simulation.yml once that lands. --- .github/workflows/ci.yml | 2 +- kernel/src/task.c | 60 +++++++++++++++++++---------- services/linux/src/linux_security.c | 2 - tests/CMakeLists.txt | 13 +++++++ tests/test_kernel.c | 4 ++ tests/unit/test_ci_gate.py | 7 ++++ 6 files changed, 64 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 431a67ca..86ef8da1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -257,7 +257,7 @@ jobs: ci-gate: name: CI Gate runs-on: ubuntu-22.04 - needs: [test, default-config, build-arm, static-analysis] + needs: [test, windows-test, default-config, build-arm, static-analysis] if: always() steps: - uses: actions/checkout@v4 diff --git a/kernel/src/task.c b/kernel/src/task.c index 8d93b836..5a6d1949 100644 --- a/kernel/src/task.c +++ b/kernel/src/task.c @@ -125,8 +125,8 @@ static int find_next_task(void) for (int i = 0; i < EOS_MAX_TASKS; i++) { int idx = (start + i) % EOS_MAX_TASKS; eos_task_t *t = &g_tasks[idx]; - if (t->state != EOS_TASK_READY) continue; - if (t->priority < best_prio) { + if (t->entry == NULL || t->state != EOS_TASK_READY) continue; + if (best < 0 || t->priority < best_prio) { best_prio = t->priority; best = idx; } @@ -141,15 +141,7 @@ static int find_next_task(void) */ void eos_schedule(void) { - int next = find_next_task(); - if (next < 0) return; /* No ready tasks — stay on current */ - - /* Mark current task as ready (if it was running) */ - if (g_current >= 0 && g_tasks[g_current].state == EOS_TASK_RUNNING) { - g_tasks[g_current].state = EOS_TASK_READY; - } - - /* Check stack canary on current task */ + /* Validate the outgoing task before making it eligible for selection. */ if (g_current >= 0 && g_tasks[g_current].stack_base) { if (g_tasks[g_current].stack_base[0] != EOS_STACK_CANARY) { /* Stack overflow detected! */ @@ -158,12 +150,24 @@ void eos_schedule(void) } } - /* Switch to next task */ - g_current_sp = &g_tasks[g_current >= 0 ? g_current : next].stack_ptr; + /* A preempted task remains runnable and competes at its normal priority. */ + if (g_current >= 0 && g_tasks[g_current].state == EOS_TASK_RUNNING) { + g_tasks[g_current].state = EOS_TASK_READY; + } + + int next = find_next_task(); + if (next < 0) return; /* No valid task is runnable. */ + + /* + * PendSV has already saved the outgoing PSP through g_current_sp. + * Point both assembly-facing slots at the task that is now current, so + * the next PendSV saves that task's PSP into its own control block. + */ g_current = next; g_tasks[next].state = EOS_TASK_RUNNING; g_tasks[next].run_count++; - g_next_sp = &g_tasks[next].stack_ptr; + g_current_sp = &g_tasks[next].stack_ptr; + g_next_sp = g_current_sp; } /** @@ -283,6 +287,8 @@ int eos_task_create(const char *name, eos_task_func_t entry, void *arg, if (!entry) return EOS_KERN_INVALID; if (stack_size == 0) stack_size = 1024; + uint32_t crit = eos_port_enter_critical(); + /* Find free slot (skip slot 0 = idle) */ int slot = -1; for (int i = 1; i < EOS_MAX_TASKS; i++) { @@ -292,30 +298,41 @@ int eos_task_create(const char *name, eos_task_func_t entry, void *arg, break; } } - if (slot < 0) return EOS_KERN_NO_MEMORY; + if (slot < 0) { + eos_port_exit_critical(crit); + return EOS_KERN_NO_MEMORY; + } /* Allocate stack */ uint32_t *stack_base = alloc_task_stack(stack_size); - if (!stack_base) return EOS_KERN_NO_MEMORY; + if (!stack_base) { + eos_port_exit_critical(crit); + return EOS_KERN_NO_MEMORY; + } uint32_t *stack_top = stack_base + stack_size / sizeof(uint32_t); memset(&g_tasks[slot], 0, sizeof(eos_task_t)); + g_tasks[slot].state = EOS_TASK_DELETED; g_tasks[slot].id = (uint8_t)slot; g_tasks[slot].name = name; - g_tasks[slot].entry = entry; g_tasks[slot].arg = arg; g_tasks[slot].priority = priority; g_tasks[slot].base_priority = priority; g_tasks[slot].stack_size = stack_size; g_tasks[slot].stack_base = stack_base; g_tasks[slot].stack_ptr = eos_port_init_stack(stack_top, entry, arg); + + /* Publish the entry and READY state only after the TCB is complete. */ + g_tasks[slot].entry = entry; g_tasks[slot].state = EOS_TASK_READY; g_task_count++; /* If kernel is running and new task has higher priority, yield */ - if (g_running && g_current >= 0 && - priority < g_tasks[g_current].priority) { + bool should_preempt = g_running && g_current >= 0 && + priority < g_tasks[g_current].priority; + eos_port_exit_critical(crit); + if (should_preempt) { eos_port_yield(); } @@ -324,7 +341,7 @@ int eos_task_create(const char *name, eos_task_func_t entry, void *arg, int eos_task_delete(eos_task_handle_t h) { - if (h >= EOS_MAX_TASKS) return EOS_KERN_INVALID; + if (h == 0 || h >= EOS_MAX_TASKS) return EOS_KERN_INVALID; if (g_tasks[h].state == EOS_TASK_DELETED || g_tasks[h].entry == NULL) return EOS_KERN_INVALID; g_tasks[h].state = EOS_TASK_DELETED; @@ -339,7 +356,8 @@ int eos_task_delete(eos_task_handle_t h) int eos_task_suspend(eos_task_handle_t h) { - if (h >= EOS_MAX_TASKS || g_tasks[h].entry == NULL) return EOS_KERN_INVALID; + if (h == 0 || h >= EOS_MAX_TASKS || g_tasks[h].entry == NULL) + return EOS_KERN_INVALID; g_tasks[h].state = EOS_TASK_SUSPENDED; if (h == (eos_task_handle_t)g_current) { eos_port_yield(); diff --git a/services/linux/src/linux_security.c b/services/linux/src/linux_security.c index 2f86ed59..b294b3c2 100644 --- a/services/linux/src/linux_security.c +++ b/services/linux/src/linux_security.c @@ -692,8 +692,6 @@ int eos_busybox_install_to_rootfs(const EosBusybox *bb, const char *rootfs_dir) "would have no busybox."); return -1; } -#else - (void)bb; #endif /* Create /init symlink for initramfs boot */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a9ec2f8b..5a81ea28 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -350,6 +350,19 @@ add_executable(test_stress test_stress.c) target_link_libraries(test_stress PRIVATE eos_kernel) add_test(NAME test_stress COMMAND test_stress) +# --- test_linux_security_paths: the inputs linux_security must not shell out --- +# Registered by #119, replaced by #115's block in a three-way replay, then +# #118 replaced that; both suites compiled against nothing until +# test_cmake_test_registration.py named them. +add_executable(test_linux_security_paths test_linux_security_paths.c) +target_link_libraries(test_linux_security_paths PRIVATE eos_linux_security) +add_test(NAME test_linux_security_paths COMMAND test_linux_security_paths) + +# --- test_pkg_fetch: source fetch refuses an unverifiable download --- +add_executable(test_pkg_fetch test_pkg_fetch.c) +target_link_libraries(test_pkg_fetch PRIVATE eos_pkg eos_core) +add_test(NAME test_pkg_fetch COMMAND test_pkg_fetch) + # --- Fuzz targets --- # Opt-in (-DEOS_ENABLE_FUZZING=ON, Clang only); the subdirectory guards itself. add_subdirectory(fuzz) diff --git a/tests/test_kernel.c b/tests/test_kernel.c index aa96de71..1ccc2de6 100644 --- a/tests/test_kernel.c +++ b/tests/test_kernel.c @@ -497,6 +497,8 @@ static int kernel_tests_run = 0; int main(void) { printf("=== EoS Kernel Tests ===\n"); RUN(test_kernel_init); + RUN(test_idle_task_is_permanent); + RUN(test_scheduler_selects_valid_task_and_tracks_stack); RUN(test_task_create); RUN(test_task_create_invalid); RUN(test_task_delete); @@ -507,8 +509,10 @@ int main(void) { RUN(test_queue); RUN(test_queue_full); RUN(test_queue_send_waiter_overflow); + RUN(test_queue_no_stale_send_waiter); RUN(test_task_stats); RUN(test_tick_overflow); + RUN(test_wake_tick_zero); /* Counted rather than written out: the literal said 12/12 while a * thirteenth test sat in this file uncalled, so the number agreed * with the omission instead of exposing it. */ diff --git a/tests/unit/test_ci_gate.py b/tests/unit/test_ci_gate.py index d484b875..d49290e9 100644 --- a/tests/unit/test_ci_gate.py +++ b/tests/unit/test_ci_gate.py @@ -72,6 +72,13 @@ "documentation build; its pull_request trigger is filtered to " "paths: ['docs/book/**'], so on a pull request that touches nothing " "under that path it reports no status at all", + "eosim-sanity.yml": + "nightly EoSim install check; its pull_request trigger is filtered to " + "the two simulation workflow files themselves (#129), so a pull " + "request that touches neither reports no status at all", + "simulation-test.yml": + "nightly EoSim simulation; same paths filter as eosim-sanity.yml " + "(#129), so it reports no status on almost every pull request", } # (workflow, job) pairs excluded from their gate's `needs`, with the reason. From 1043ea6cf088f5e694d3289d5f468257f9399efd Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Fri, 11 Sep 2026 16:33:03 +0530 Subject: [PATCH 02/13] tests: build test_linux_security_paths on POSIX hosts only Re-registering it put it in front of the MSVC leg (#132) for the first time, where does not exist. The suite probes with access(), open(O_CREAT) and rmdir to prove refused inputs never reach a shell; on Windows the library's own #else arm refuses every install/build call before is_path_safe() matters, so there is nothing for it to test there. --- tests/CMakeLists.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5a81ea28..1ec6cfa3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -354,9 +354,15 @@ add_test(NAME test_stress COMMAND test_stress) # Registered by #119, replaced by #115's block in a three-way replay, then # #118 replaced that; both suites compiled against nothing until # test_cmake_test_registration.py named them. -add_executable(test_linux_security_paths test_linux_security_paths.c) -target_link_libraries(test_linux_security_paths PRIVATE eos_linux_security) -add_test(NAME test_linux_security_paths COMMAND test_linux_security_paths) +# POSIX only: the suite proves refused inputs never reach a shell by probing +# with access()/open(O_CREAT)/rmdir, and the MSVC leg (#132) has no unistd.h. +# On Windows the library's own #else arm refuses every install/build call +# before is_path_safe() matters. +if(UNIX) + add_executable(test_linux_security_paths test_linux_security_paths.c) + target_link_libraries(test_linux_security_paths PRIVATE eos_linux_security) + add_test(NAME test_linux_security_paths COMMAND test_linux_security_paths) +endif() # --- test_pkg_fetch: source fetch refuses an unverifiable download --- add_executable(test_pkg_fetch test_pkg_fetch.c) From 73be875329b2bd2951cd23ddea0310ae9de17afe Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Fri, 11 Sep 2026 16:40:14 +0530 Subject: [PATCH 03/13] tests(firmware): keep EosConfig off the stack -- it is ~4 MB and MSVC gives 1 MB Re-registering the POSIX-only suite let the MSVC leg reach ctest for the first time, and test_firmware died there with SEGFAULT: five tests declare a stack EosConfig (sizeof 3,953,236 on this ABI), which the 8 MB default stack on Linux and macOS absorbs and MSVC's 1 MB does not. test_config.c already keeps its EosConfig static for the same reason; the same here. --- tests/test_firmware.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_firmware.c b/tests/test_firmware.c index 7134a51d..b931ba40 100644 --- a/tests/test_firmware.c +++ b/tests/test_firmware.c @@ -75,7 +75,7 @@ static void test_arm_cortex_arch(void) { } static void test_firmware_init(void) { - EosConfig cfg; + static EosConfig cfg; /* ~4 MB: MSVC's 1 MB default stack cannot hold it */ memset(&cfg, 0, sizeof(cfg)); strncpy(cfg.workspace.build_dir, ".eos/build", EOS_MAX_PATH - 1); strncpy(cfg.toolchain.rtos_target, "arm-none-eabi", EOS_MAX_NAME - 1); @@ -100,7 +100,7 @@ static void test_firmware_init(void) { } static void test_firmware_dry_run(void) { - EosConfig cfg; + static EosConfig cfg; /* ~4 MB: MSVC's 1 MB default stack cannot hold it */ memset(&cfg, 0, sizeof(cfg)); strncpy(cfg.workspace.build_dir, ".eos/build", EOS_MAX_PATH - 1); @@ -119,7 +119,7 @@ static void test_firmware_dry_run(void) { } static void test_hybrid_init(void) { - EosConfig cfg; + static EosConfig cfg; /* ~4 MB: MSVC's 1 MB default stack cannot hold it */ memset(&cfg, 0, sizeof(cfg)); strncpy(cfg.workspace.build_dir, ".eos/build", EOS_MAX_PATH - 1); cfg.system.kind = EOS_SYSTEM_HYBRID; @@ -154,7 +154,7 @@ static void test_firmware_graph_node(void) { } static void test_config_load_rtos(void) { - EosConfig cfg; + static EosConfig cfg; /* ~4 MB: MSVC's 1 MB default stack cannot hold it */ EosResult res = eos_config_load(&cfg, "examples/motor-controller/eos.yaml"); if (res != EOS_OK) { printf("(skip - example file not found) "); @@ -168,7 +168,7 @@ static void test_config_load_rtos(void) { } static void test_config_load_hybrid(void) { - EosConfig cfg; + static EosConfig cfg; /* ~4 MB: MSVC's 1 MB default stack cannot hold it */ EosResult res = eos_config_load(&cfg, "examples/industrial-gateway/eos.yaml"); if (res != EOS_OK) { printf("(skip - example file not found) "); From 5c19f01396b9f4547e23cf76a13ae5dc009a2b4e Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Mon, 14 Sep 2026 18:57:20 +0530 Subject: [PATCH 04/13] docs(kernel): document the idle-task contract of eos_task_delete/suspend kernel.h declared eos_task_delete() and eos_task_suspend() without saying what handle 0 is. It is the idle task, it is permanent, and both calls return EOS_KERN_INVALID for it -- as they do for an out-of-range handle or an empty slot. The doc comments now say so, in the header's Doxygen style. eos_schedule() has an early return for "no runnable task" that is unreachable by construction: slot 0 cannot be deleted or suspended, the idle task only spins or waits for an interrupt and so never blocks, and find_next_task() can select it because of the "best < 0 ||" term. The comment records that reasoning, and that the return is self-healing if it were ever taken: the outgoing task stays READY and the next eos_schedule() marks it RUNNING. task.c also carried two stray blank lines at EOF from the re-apply of #131; with them gone, the diff against 3a00bd99 is #130's wake_armed hunk plus this comment and nothing else. --- kernel/include/eos/kernel.h | 19 +++++++++++++++++++ kernel/src/task.c | 14 ++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/kernel/include/eos/kernel.h b/kernel/include/eos/kernel.h index cf1a3412..6228f41a 100644 --- a/kernel/include/eos/kernel.h +++ b/kernel/include/eos/kernel.h @@ -100,7 +100,26 @@ bool eos_kernel_is_running(void); int eos_task_create(const char *name, eos_task_func_t entry, void *arg, uint8_t priority, uint32_t stack_size); +/** + * @brief Delete a task; its slot becomes available to eos_task_create(). + * @param handle Task handle (slot index) returned by eos_task_create(). + * @return EOS_KERN_OK on success; EOS_KERN_INVALID for handle 0, an + * out-of-range handle, or a slot that holds no task. + * + * Handle 0 is the idle task, and it is permanent: it is the task the + * scheduler falls back to when nothing else is runnable, so it can be + * neither deleted nor suspended. + */ int eos_task_delete(eos_task_handle_t handle); + +/** + * @brief Suspend a task until eos_task_resume() is called on it. + * @param handle Task handle (slot index) returned by eos_task_create(). + * @return EOS_KERN_OK on success; EOS_KERN_INVALID for handle 0, an + * out-of-range handle, or a slot that holds no task. + * + * Handle 0 is the idle task and is permanent; see eos_task_delete(). + */ int eos_task_suspend(eos_task_handle_t handle); int eos_task_resume(eos_task_handle_t handle); void eos_task_yield(void); diff --git a/kernel/src/task.c b/kernel/src/task.c index 5a6d1949..c8dc5b4f 100644 --- a/kernel/src/task.c +++ b/kernel/src/task.c @@ -156,6 +156,18 @@ void eos_schedule(void) } int next = find_next_task(); + /* + * Unreachable by construction. Slot 0 is the idle task, which + * eos_task_delete() and eos_task_suspend() refuse, and idle_task_func() + * only spins or waits for an interrupt, so it never blocks: the idle + * task is always READY (or was RUNNING and was made READY just above), + * and the "best < 0 ||" term in find_next_task() lets it be chosen at + * priority 255 when nothing else is. The only way to reach it is the + * canary check above marking the idle task DELETED, which is a fault, + * not a schedule. Even then the early return is self-healing: g_current_sp + * and g_next_sp still name the outgoing task, which stays READY, and + * the next eos_schedule() finds it and marks it RUNNING. + */ if (next < 0) return; /* No valid task is runnable. */ /* @@ -592,5 +604,3 @@ int eos_task_get_all_stats(eos_task_stats_t *out, int max_entries, int *count) *count = n; return EOS_KERN_OK; } - - From 9c0ada7df8955f6b8203ff80b35765dc43ec360f Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Mon, 14 Sep 2026 19:00:08 +0530 Subject: [PATCH 05/13] fix(linux_security): refuse NULL arguments in eos_busybox_install_to_rootfs is_path_safe() refuses NULL, but eos_busybox_install_to_rootfs() read bb->source_dir to hand it to the predicate, so a NULL bb reached the dereference before the guard could see it: a crash rather than the -1 every other refusal in this file produces. Both arguments are now checked first, before either is read. test_linux_security_paths gains a case for it: (NULL, dir) and (&bb, NULL) both return -1, and the directory gets no /init. With the guard removed and the target rebuilt, the suite dies with SIGSEGV (exit 139) on the first call; with it restored, the suite passes. --- services/linux/src/linux_security.c | 1 + tests/test_linux_security_paths.c | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/services/linux/src/linux_security.c b/services/linux/src/linux_security.c index b294b3c2..9a1eb57a 100644 --- a/services/linux/src/linux_security.c +++ b/services/linux/src/linux_security.c @@ -670,6 +670,7 @@ int eos_busybox_build(EosBusybox *bb) { } int eos_busybox_install_to_rootfs(const EosBusybox *bb, const char *rootfs_dir) { + if (!bb || !rootfs_dir) return -1; if (!is_path_safe(bb->source_dir) || !is_path_safe(rootfs_dir)) return -1; #ifndef _WIN32 char cmd[2048]; diff --git a/tests/test_linux_security_paths.c b/tests/test_linux_security_paths.c index 7eb9e1d1..1016f3df 100644 --- a/tests/test_linux_security_paths.c +++ b/tests/test_linux_security_paths.c @@ -730,6 +730,31 @@ static void test_busybox_install_reports_an_init_it_could_not_write(void) { PASS_IF_CLEAN(f0, "busybox_install_to_rootfs reports an /init it could not write"); } +/* is_path_safe() refuses NULL, but eos_busybox_install_to_rootfs() read + * bb->source_dir to hand it to the predicate, so a NULL bb was a crash + * rather than a -1. Both arguments are now refused before either is + * dereferenced -- and, as everywhere in this file, refused means nothing + * was written: the rootfs gets no /init. */ +static void test_busybox_install_refuses_null_arguments(void) { + int f0 = failures; + char rootfs[] = "/tmp/eos_lsp_null_XXXXXX"; + char init[128]; + EosBusybox bb; + + if (!mkdtemp(rootfs)) { fprintf(stderr, "[SKIP] mkdtemp failed\n"); return; } + snprintf(init, sizeof init, "%s/init", rootfs); + + CHECK(eos_busybox_install_to_rootfs(NULL, rootfs) == -1); + CHECK(access(init, F_OK) != 0); + + eos_busybox_init(&bb); + CHECK(eos_busybox_install_to_rootfs(&bb, NULL) == -1); + + remove(init); + rmdir(rootfs); + PASS_IF_CLEAN(f0, "busybox_install_to_rootfs refuses NULL arguments"); +} + int main(void) { test_dmverity_verify_refuses_hostile_paths(); test_dmverity_verify_refuses_hostile_hash_device(); @@ -747,6 +772,7 @@ int main(void) { test_selinux_install_reports_a_policy_it_did_not_copy(); test_selinux_install_guard_stops_a_reachable_injection(); test_busybox_install_reports_an_init_it_could_not_write(); + test_busybox_install_refuses_null_arguments(); test_ordinary_paths_still_reach_the_shell(); if (failures) { From 1c03da02f42c16ba66d11cec208cd09bf0060ccf Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Mon, 14 Sep 2026 19:00:46 +0530 Subject: [PATCH 06/13] docs: changelog for the master repair Records under Unreleased what #144 did and why: the "#else after #else" that stopped master compiling after the 09-08 batch merge, the guards the replays walked back (two suites re-registered, four test_kernel tests wired into main(), windows-test in the CI gate, the two path-filtered simulation workflows classified), and the NULL guard in eos_busybox_install_to_rootfs(). The scheduler change gets its own Changed entry because it is a contract: eos_task_delete(0) and eos_task_suspend(0) return EOS_KERN_INVALID again, as #131 specified before #130's replay replaced its task.c. Changed sits above Fixed, the order the older releases in this file use. --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2212ffba..480b4a05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,12 @@ ## [Unreleased] +### Changed +- **`eos_task_delete(0)` and `eos_task_suspend(0)` return `EOS_KERN_INVALID`.** Handle 0 is the idle task and it is permanent: it is the task the scheduler falls back to when nothing else is runnable. This restores the scheduler contract #131 established and #130's replay rolled back -- `kernel/src/task.c` on master was #130's copy, in which the idle task could be deleted or suspended, `eos_task_create()` published a TCB to the scheduler before its stack existed, and `eos_schedule()` left `g_current_sp` pointing at the outgoing task. The file is #131's again, plus #130's `wake_armed` hunk. + ### Fixed +- **Master did not compile after the 09-08 batch merge.** Nine PRs were merged within minutes, each on the base it was written against. #119 and #132 each added an `#else` to the same `#ifndef _WIN32` in `eos_busybox_install_to_rootfs()`, so `linux_security.c` read "#else after #else" and every C job, CodeQL and the simulation's ARM64 kernel failed to compile -- before the Python guards that would have named the rest could run. The duplicate arm is removed. Behind it: `test_linux_security_paths` (#119) and `test_pkg_fetch` (#115), whose `add_executable()` blocks later replays had overwritten, are registered again (41 to 43 suites); four `test_kernel` tests from #130 and #131 that were defined but never called are wired into `main()`; `windows-test` (#132) is in the CI gate's `needs`, so the gate cannot be green with the MSVC leg red; and `eosim-sanity.yml` and `simulation-test.yml` (#129), whose `pull_request` trigger is path-filtered and so cannot be a required check, are classified in `test_ci_gate.py`'s `NOT_REQUIRED`. +- **`eos_busybox_install_to_rootfs()` refuses `NULL`:** it read `bb->source_dir` to hand it to `is_path_safe()`, so a `NULL` `bb` was a crash rather than a -1. Both arguments are now refused before either is dereferenced. - **`linux_security` shell commands:** `is_path_safe()` was a denylist of shell metacharacters that missed the backtick and the newline, was applied at 2 of the 9 call sites that build a command, and returned "safe" for `NULL`. A path of the form `/tmp/id` reached `system()` and ran `id`. The predicate now rejects control characters, backslash, backtick and newline, refuses `NULL`, and guards every shell-building entry point. `eos_busybox_configure()` validated `source_dir` *before* filling in its default, so the default path -- the one a caller gets by not setting the field -- validated the empty string and handed `.eos/build/src/busybox-` to the shell unchecked; the fill now happens first, and `eos_busybox_set_version()` refuses a version that is not one shell word. - **One shell word is an allowlist.** `defconfig` and `cross_compile` are interpolated unquoted. The predicate guarding them was a second denylist (space, tab, `*`, `?`, `~`) that missed `[`/`]` and `{`/`}`, and accepted a leading `-`. It is now `isalnum()` plus `._/+=:-` and nothing else. - **Security steps no longer report success without having run.** `eos_ima_sign_file()`, `eos_selinux_install_to_rootfs()`, `eos_selinux_label_rootfs()`, `eos_ima_install_to_rootfs()` and `eos_busybox_install_to_rootfs()` ended their commands with `|| true` or `|| echo …` and discarded `system()`'s result, so a missing `evmctl` or `setfiles`, a failed policy or key copy, and a `make install` that never ran all returned 0. Each now distinguishes a step that completed from one that could not, logs through `EOS_ERROR` and returns -1. **Contract change:** these five functions previously always returned 0; no caller in this tree relies on that. From 95692266f8f5769e987e8f5edf387ece6984d82a Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Tue, 15 Sep 2026 00:51:37 +0530 Subject: [PATCH 07/13] docs(kernel): say what the next<0 return actually does, and what suspend checks The eos_schedule() comment claimed the early return was self-healing. It is not: find_next_task() scans every slot including the outgoing one, so next < 0 means nothing is READY, and the return leaves the state exactly as the canary check left it. The comment now says so, and that recovery is a fault handler's job rather than the scheduler's. eos_task_suspend() refuses a slot whose entry is NULL, which is what the doc comment now says instead of the looser "holds no task"; a slot the canary check marked DELETED still has its entry set and is accepted. tests/CMakeLists.txt: the reason test_linux_security_paths is POSIX-only is that the shell-building bodies are compiled out on Windows, not that the predicate is skipped there. --- kernel/include/eos/kernel.h | 3 ++- kernel/src/task.c | 8 +++++--- tests/CMakeLists.txt | 5 +++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/kernel/include/eos/kernel.h b/kernel/include/eos/kernel.h index 6228f41a..3a09f15f 100644 --- a/kernel/include/eos/kernel.h +++ b/kernel/include/eos/kernel.h @@ -116,7 +116,8 @@ int eos_task_delete(eos_task_handle_t handle); * @brief Suspend a task until eos_task_resume() is called on it. * @param handle Task handle (slot index) returned by eos_task_create(). * @return EOS_KERN_OK on success; EOS_KERN_INVALID for handle 0, an - * out-of-range handle, or a slot that holds no task. + * out-of-range handle, or a slot whose entry is NULL (never + * created, or deleted through eos_task_delete()). * * Handle 0 is the idle task and is permanent; see eos_task_delete(). */ diff --git a/kernel/src/task.c b/kernel/src/task.c index c8dc5b4f..167e5f27 100644 --- a/kernel/src/task.c +++ b/kernel/src/task.c @@ -164,9 +164,11 @@ void eos_schedule(void) * and the "best < 0 ||" term in find_next_task() lets it be chosen at * priority 255 when nothing else is. The only way to reach it is the * canary check above marking the idle task DELETED, which is a fault, - * not a schedule. Even then the early return is self-healing: g_current_sp - * and g_next_sp still name the outgoing task, which stays READY, and - * the next eos_schedule() finds it and marks it RUNNING. + * not a schedule. find_next_task() scans every slot including the + * outgoing one, so next < 0 means nothing at all is READY; the return + * leaves g_current_sp and g_next_sp naming the outgoing task and its + * state as the canary check left it. Nothing here recovers from that, + * and nothing should: an overflowed idle stack is a fault handler's job. */ if (next < 0) return; /* No valid task is runnable. */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1ec6cfa3..28746170 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -356,8 +356,9 @@ add_test(NAME test_stress COMMAND test_stress) # test_cmake_test_registration.py named them. # POSIX only: the suite proves refused inputs never reach a shell by probing # with access()/open(O_CREAT)/rmdir, and the MSVC leg (#132) has no unistd.h. -# On Windows the library's own #else arm refuses every install/build call -# before is_path_safe() matters. +# On Windows the shell-building bodies are compiled out (#ifndef _WIN32), so +# a path that passes is_path_safe() still reaches no shell there; the +# predicate itself is portable and is exercised by the POSIX legs. if(UNIX) add_executable(test_linux_security_paths test_linux_security_paths.c) target_link_libraries(test_linux_security_paths PRIVATE eos_linux_security) From 3959b553bdcad0e072c6c65359549b894680f550 Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Mon, 14 Sep 2026 19:07:36 +0530 Subject: [PATCH 08/13] =?UTF-8?q?docs(alignment):=20replace=20the=20?= =?UTF-8?q?=E2=9C=85=20Aligned=20table=20with=20measured=20inventories?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The status table said "ALL ALIGNED" and quoted 25 board YAMLs in eos, 25 board ports in eBoot, 41 products, 33 HAL APIs, 39 EOS_ENABLE_* flags and 5 templates. Measured on 2026-09-14 against origin/master of the three repositories, the board row alone is 84 eos/boards/*.yaml, 83 eBoot boards/*/ directories (83 eboot_add_board() calls) and, in ebuild's sdk_generator.py, TARGET_ARCH 14 / EBOOT_BOARD 14 / MCU_TO_EBOOT_BOARD 136: three differently keyed inventories of different sizes, and no tool compares them. The other rows drifted too: 48 product headers against 42 config branches and 41 PRODUCT_MAP keys, 34 EOS_ENABLE_* defines of which ebuild names 24, 114 ComponentDB parts rather than 200+, six templates at templates/ rather than five at ebuild/templates/, and eboot_flash_layout.h is written by EosBootIntegrator, not EosConfigGenerator. The banner is replaced with an honest status line, each row now carries the measured numbers and what was compared, and a "How this was measured" section lists the exact commands and the ast-based script so the next person can re-run them. The 25-board and 41-profile tables are kept as the original subsets with a note on what still holds; the checklist's stale "Should be" values are updated and its reference to a test_full_pipeline.py that no longer exists is marked. ebuild#109 made this correction inside ebuild's vendored copy under core/eos/docs, which is pinned to eos 5544c98, so ebuild#132 reverts it to the pin; the correction belongs upstream here. eos issue #149 tracks it. CHANGELOG.md is untouched: none of the docs-only commits on master have an entry there. tests/unit is unchanged by this edit (39 passed, and the same four pre-existing failures in test_ci_gate.py and test_cmake_test_registration.py before and after; neither reads docs/). --- docs/three-way-alignment.md | 155 ++++++++++++++++++++++++++++++++---- 1 file changed, 139 insertions(+), 16 deletions(-) diff --git a/docs/three-way-alignment.md b/docs/three-way-alignment.md index 57102a92..bdd76035 100644 --- a/docs/three-way-alignment.md +++ b/docs/three-way-alignment.md @@ -4,23 +4,144 @@ This document tracks alignment between all three EoS components to ensure they r --- -## Alignment Status: ✅ ALL ALIGNED +## Alignment Status: ⚠️ NOT ALIGNED — the inventories disagree and nothing cross-checks them + +Measured on 2026-09-14 against origin/master of eos, eBoot and ebuild (eos `9ed6831`, eBoot `221325c`, ebuild `76970c9`). Every number below comes from the commands under "How this was measured"; the previous banner and its counts (25 / 25 / 41 / 24 / 33 / 39 / 5) did not reproduce. | Dimension | eos | eboot | ebuild | Status | |-----------|-----|-------|--------|--------| -| Board definitions | 25 YAML files in `eos/boards/` | 25 board ports in `eboot/boards/` | `MCU_TO_EBOOT_BOARD` + `EOS_BOARD_MAP` in project generator | ✅ Aligned | -| Product profiles | 41 profiles in `eos/products/*.h` | — | `PRODUCT_MAP` (41 entries) in project generator | ✅ Aligned | -| Platform enum | — | 24 `eos_platform_t` entries in `eos_hal.h` | MCU_DATABASE (100+ MCUs) in hw analyzer | ✅ Aligned | -| Peripheral keywords | 33 HAL APIs in `hal.h` + `hal_extended.h` | — | `PERIPHERAL_KEYWORDS` (24 types) + `ComponentDB` (200+ parts) | ✅ Aligned | -| Multicore support | `multicore.h` API | `eos_multicore.h` boot API | `MULTICORE_MCUS` set in project generator | ✅ Aligned | -| Config generation | `eos_config.h` (39 `EOS_ENABLE_*` flags) | `eboot_flash_layout.h` | `EosConfigGenerator` produces both | ✅ Aligned | -| Templates | — | — | 5 templates in `ebuild/templates/` | ✅ Aligned | -| CLI commands | cmake build | cmake build | `ebuild new`, `build`, `analyze`, `generate-project` | ✅ Aligned | +| Board definitions | 84 `boards/*.yaml` (89 entries in `boards/`; the other 5 are `lm3s6965evb.ld`, `startup_stm32f407.S`, `stm32f407vg.ld`, `stm32f407_discovery/`, `validation/`) | 83 `boards/*/` directories, 83 `eboot_add_board()` calls in `CMakeLists.txt`, 83 `boards/*/board_*.h` | `ebuild/sdk_generator.py`: `TARGET_ARCH` 14, `EBOOT_BOARD` 14, `MCU_TO_EBOOT_BOARD` 136 keys (56 distinct eboot dirs); `ebuild/eos_ai/eos_project_generator.py`: `EOS_BOARD_MAP` 116 keys (81 distinct eos YAMLs) | ⚠️ 84 / 83 / 14+136 are differently-keyed inventories of different sizes and no tool compares them. Every `MCU_TO_EBOOT_BOARD` value is an eboot dir (56/56) but 27 eboot dirs are never targeted; every `EOS_BOARD_MAP` value is an eos YAML (81/81) but `hiletgo-esp-wroom-32.yaml`, `qemu-arm64.yaml` and `tms570.yaml` are never mapped; only 7 eos YAML stems share a name with an eboot dir. | +| Product profiles | 48 `products/*.h`; 42 `#if`/`#elif defined(EOS_PRODUCT_*)` branches in `include/eos/eos_config.h` | — | `PRODUCT_MAP` 41 keys in `eos_project_generator.py` | ⚠️ 48 / 42 / 41. All 41 `PRODUCT_MAP` keys have a header and a config branch. `cast_device`, `desktop`, `home_camera`, `iptv_stb`, `smart_speaker`, `tv_os` have a header but neither a config branch nor a `PRODUCT_MAP` entry; `vbox_test` has a header and a config branch but no `PRODUCT_MAP` entry. | +| Platform enum | — | 24 `eos_platform_t` enumerators in `include/eos_hal.h` | `MCU_DATABASE` 171 keys in `eos_hw_analyzer.py`, using 52 distinct `arch` strings | ⚠️ The 24 reproduces. `MCU_DATABASE` describes MCUs by `arch` strings (`arm`, `mips64`, `sh`, …) that share no vocabulary with the enumerators, and nothing maps one onto the other. | +| Peripheral keywords | 33 peripheral families with an `eos__init()`: 5 in `hal/include/eos/hal.h`, 28 in `hal/include/eos/hal_extended.h` | — | `PERIPHERAL_KEYWORDS` 46 keywords mapping to 24 distinct types in `eos_hw_analyzer.py`; `ComponentDB` 114 parts in `component_db.py` (its docstring says 200+) | ⚠️ 33 and 24 reproduce; 200+ does not (114). Only 21 of the 24 types are HAL family names: `ethernet`, `gps`, `watchdog` are spelled `eth`, `gnss`, `wdt` in the HAL, and `cellular`, `flash`, `gpu`, `haptic`, `hdmi`, `ir`, `pcie`, `radar`, `touch` have no keyword at all. | +| Multicore support | `kernel/include/eos/multicore.h` (38 declarations: `eos_core_*`, `eos_spin_*`, `eos_ipi_*`, `eos_rproc_*`, `eos_shmem_*`, …) | `include/eos_multicore.h` (12 `eos_multicore_*` declarations) | `MULTICORE_MCUS` 13 names in `eos_project_generator.py` | ⚠️ All three exist, and that is all that was checked: the two headers are different APIs, and nothing compares either of them to `MULTICORE_MCUS`. | +| Config generation | 34 `#define EOS_ENABLE_*` in `include/eos/eos_config.h` | `eboot_flash_layout.h` is not a file in eBoot (0 tracked files); it is generated output that eBoot only documents (`configs/README.md`, `docs/quickstart.md`, `docs/book/book.md`) | `EosConfigGenerator.generate_eos_config_h()` writes `eos_product_config.h`; `EosBootIntegrator.generate_from_boot_yaml()` writes `eboot_flash_layout.h` | ⚠️ 39 did not reproduce (34), and "`EosConfigGenerator` produces both" was wrong: two classes, two files. ebuild names 24 `EOS_ENABLE_*` flags (all in `eos_hw_analyzer.py`); 19 are defined in `eos_config.h`, `EOS_ENABLE_GPIO`/`I2C`/`SPI`/`TIMER`/`UART` are defined nowhere in eos, and 15 `eos_config.h` flags (`EOS_ENABLE_MULTICORE`, `EOS_ENABLE_NET`, `EOS_ENABLE_PCIE`, …) are never named by ebuild. | +| Templates | — | — | 6 directories in `templates/` at the ebuild repo root (`bare-metal`, `ble-sensor`, `linux-app`, `rtos-app`, `safety-critical`, `secure-boot`); `ebuild/templates/` does not exist | ⚠️ "5 in `ebuild/templates/`" did not reproduce: 6, at a different path. Nothing in eos or eboot refers to them, so there is nothing to align against. | +| CLI commands | cmake build (`CMakeLists.txt`) | cmake build (`CMakeLists.txt`) | `new`, `build`, `analyze`, `generate-project` are `@cli.command` functions in `ebuild/cli/commands.py` (23 `@cli.command` entries plus the `repos` group with 6 subcommands) | ✅ The four named subcommands exist by name. That is a presence check on ebuild alone, not an inventory comparison. | + +--- + +## How this was measured + +Run from a directory holding sibling checkouts `eos/`, `eBoot/` and `ebuild/` with `origin/master` fetched (`git -C fetch origin`). Every command reads `origin/master`, not the working tree, so it gives the same answer whatever is checked out. + +```bash +git -C eos ls-tree --name-only origin/master boards/ | wc -l # 89 +git -C eos ls-tree --name-only origin/master boards/ | grep -c '\.yaml$' # 84 +git -C eos ls-tree --name-only origin/master boards/ | grep -v '\.yaml$' # the other 5 +git -C eBoot ls-tree origin/master boards/ | grep -c '^040000' # 83 +git -C eBoot show origin/master:CMakeLists.txt | grep -c 'eboot_add_board(' # 83 +git -C eBoot ls-tree -r --name-only origin/master boards/ | grep -cE '^boards/[^/]+/board_[^/]+\.h$' # 83 +git -C eos ls-tree --name-only origin/master products/ | grep -c '\.h$' # 48 +git -C eBoot show origin/master:include/eos_hal.h | awk '/typedef enum/{f=1} f && /^[[:space:]]*EOS_PLATFORM_/{n++} /} eos_platform_t;/{f=0} END{print n}' # 24 +git -C eos show origin/master:include/eos/eos_config.h | grep -cE '^\s*#\s*define\s+EOS_ENABLE_' # 34 +git -C ebuild ls-tree origin/master templates/ | grep -c '^040000' # 6 +git -C ebuild ls-tree --name-only origin/master ebuild/templates/ | wc -l # 0: the path does not exist +git -C ebuild show origin/master:ebuild/eos_ai/component_db.py > /tmp/component_db.py +python3 -c "import importlib.util as u, sys; s=u.spec_from_file_location('component_db', sys.argv[1]); m=u.module_from_spec(s); sys.modules['component_db']=m; s.loader.exec_module(m); print(len(m.ComponentDB()._db))" /tmp/component_db.py # 114 +git -C ebuild show origin/master:ebuild/cli/commands.py | grep -cE '^@cli\.command' # 23 +git -C ebuild show origin/master:ebuild/cli/commands.py | grep -cE '^@repos\.command' # 6 +git -C ebuild show origin/master:ebuild/cli/commands.py | grep -oE '^def (new|build|analyze|generate_project)\(' # all four +git -C eos show origin/master:kernel/include/eos/multicore.h | grep -oE '\beos_[a-z0-9_]+\s*\(' | sort -u | wc -l # 38 +git -C eBoot show origin/master:include/eos_multicore.h | tr -d '\r' | grep -oE '\beos_multicore_[a-z0-9_]+\s*\(' | sort -u | wc -l # 12 +git -C ebuild show origin/master:ebuild/eos_ai/eos_config_generator.py | grep -n 'eos_product_config.h' # written by generate_eos_config_h() +git -C ebuild show origin/master:ebuild/eos_ai/eos_boot_integrator.py | grep -n 'eboot_flash_layout.h' # written by generate_from_boot_yaml() +git -C eBoot ls-tree -r --name-only origin/master | grep -c 'eboot_flash_layout\.h' # 0 +git -C eBoot grep -l 'eboot_flash_layout' origin/master -- ':!build_sim' # configs/README.md, docs/book/book.md, docs/quickstart.md +``` + +The dictionary sizes and every set comparison in the table come from this script. It parses the ebuild sources with `ast` instead of grepping them: a grep for `":` counts every quoted key in the file, not the entries of one dict. + +```python +import ast, re, subprocess + +def show(repo, path): + return subprocess.run(["git", "-C", repo, "show", f"origin/master:{path}"], + check=True, capture_output=True, text=True).stdout + +def consts(repo, path, *names): + out = {} + for node in ast.walk(ast.parse(show(repo, path))): + if isinstance(node, (ast.Assign, ast.AnnAssign)): + for t in (node.targets if isinstance(node, ast.Assign) else [node.target]): + if isinstance(t, ast.Name) and t.id in names: + out[t.id] = ast.literal_eval(node.value) + return out + +sdk = consts("ebuild", "ebuild/sdk_generator.py", "TARGET_ARCH", "EBOOT_BOARD", "MCU_TO_EBOOT_BOARD") +gen = consts("ebuild", "ebuild/eos_ai/eos_project_generator.py", "EOS_BOARD_MAP", "PRODUCT_MAP", "MULTICORE_MCUS") +hw = consts("ebuild", "ebuild/eos_ai/eos_hw_analyzer.py", "PERIPHERAL_KEYWORDS", "MCU_DATABASE") +for name, d in {**sdk, **gen, **hw}.items(): + print(name, len(d)) +print("PERIPHERAL_KEYWORDS distinct values", len(set(hw["PERIPHERAL_KEYWORDS"].values()))) +print("MCU_DATABASE distinct arch", len({v["arch"] for v in hw["MCU_DATABASE"].values()})) + +yaml = {l.split("/")[-1] for l in show("eos", "boards/").split() if l.endswith(".yaml")} +dirs = {l.split()[-1].split("/")[-1] for l in subprocess.run( + ["git", "-C", "eBoot", "ls-tree", "origin/master", "boards/"], + check=True, capture_output=True, text=True).stdout.splitlines() if l.startswith("040000")} +m2e, ebm = sdk["MCU_TO_EBOOT_BOARD"], gen["EOS_BOARD_MAP"] +print("eos yaml", len(yaml), "| eboot dirs", len(dirs), + "| yaml stems that are eboot dirs", len({y[:-5] for y in yaml} & dirs)) +print("MCU_TO_EBOOT_BOARD distinct values", len(set(m2e.values())), + "| values that are eboot dirs", len(set(m2e.values()) & dirs), + "| eboot dirs never targeted", len(dirs - set(m2e.values()))) +print("EOS_BOARD_MAP distinct values", len(set(ebm.values())), + "| values that are eos yaml", len(set(ebm.values()) & yaml), + "| eos yaml never mapped", sorted(yaml - set(ebm.values()))) + +prods = {l.split("/")[-1][:-2] for l in show("eos", "products/").split() if l.endswith(".h")} +cfg = show("eos", "include/eos/eos_config.h") +chain = {m.lower() for m in re.findall(r"#\s*(?:el)?if\s+defined\s*\(\s*EOS_PRODUCT_([A-Z0-9_]+)\s*\)", cfg)} +pm = set(gen["PRODUCT_MAP"]) +print("products/*.h", len(prods), "| eos_config.h product branches", len(chain), "| PRODUCT_MAP", len(pm)) +print("headers without PRODUCT_MAP entry", sorted(prods - pm)) +print("headers without config branch", sorted(prods - chain), "| PRODUCT_MAP keys without header", sorted(pm - prods)) + +enables = set(re.findall(r"^\s*#\s*define\s+(EOS_ENABLE_[A-Z0-9_]+)", cfg, re.M)) +named = set(re.findall(r"EOS_ENABLE_[A-Z0-9_]+", + show("ebuild", "ebuild/eos_ai/eos_hw_analyzer.py") + + show("ebuild", "ebuild/eos_ai/eos_config_generator.py"))) +print("EOS_ENABLE_* defines", len(enables), "| named in ebuild", len(named), "| common", len(enables & named), + "| ebuild-only", sorted(named - enables), "| eos-only", len(enables - named)) + +fam = set() +for h in ("hal/include/eos/hal.h", "hal/include/eos/hal_extended.h"): + fam |= set(re.findall(r"\beos_([a-z0-9]+)_init\s*\(", show("eos", h))) - {"hal"} +kw = set(hw["PERIPHERAL_KEYWORDS"].values()) +print("HAL init families", len(fam), "| keyword types that are HAL families", len(kw & fam), + "| keyword types that are not", sorted(kw - fam), "| HAL families with no keyword", sorted(fam - kw)) +``` + +Its output on 2026-09-14: + +``` +MCU_TO_EBOOT_BOARD 136 +TARGET_ARCH 14 +EBOOT_BOARD 14 +MULTICORE_MCUS 13 +PRODUCT_MAP 41 +EOS_BOARD_MAP 116 +MCU_DATABASE 171 +PERIPHERAL_KEYWORDS 46 +PERIPHERAL_KEYWORDS distinct values 24 +MCU_DATABASE distinct arch 52 +eos yaml 84 | eboot dirs 83 | yaml stems that are eboot dirs 7 +MCU_TO_EBOOT_BOARD distinct values 56 | values that are eboot dirs 56 | eboot dirs never targeted 27 +EOS_BOARD_MAP distinct values 81 | values that are eos yaml 81 | eos yaml never mapped ['hiletgo-esp-wroom-32.yaml', 'qemu-arm64.yaml', 'tms570.yaml'] +products/*.h 48 | eos_config.h product branches 42 | PRODUCT_MAP 41 +headers without PRODUCT_MAP entry ['cast_device', 'desktop', 'home_camera', 'iptv_stb', 'smart_speaker', 'tv_os', 'vbox_test'] +headers without config branch ['cast_device', 'desktop', 'home_camera', 'iptv_stb', 'smart_speaker', 'tv_os'] | PRODUCT_MAP keys without header [] +EOS_ENABLE_* defines 34 | named in ebuild 24 | common 19 | ebuild-only ['EOS_ENABLE_GPIO', 'EOS_ENABLE_I2C', 'EOS_ENABLE_SPI', 'EOS_ENABLE_TIMER', 'EOS_ENABLE_UART'] | eos-only 15 +HAL init families 33 | keyword types that are HAL families 21 | keyword types that are not ['ethernet', 'gps', 'watchdog'] | HAL families with no keyword ['cellular', 'eth', 'flash', 'gnss', 'gpu', 'haptic', 'hdmi', 'ir', 'pcie', 'radar', 'touch', 'wdt'] +``` --- ## Board-Level Alignment (25 boards) +> Kept from the original audit as a 25-board subset; the full inventories are 84 / 83 / 136 / 116 (status table above). On 2026-09-14 all 25 YAMLs and all 25 eboot directories still exist. The two ebuild columns are shorthand: in 9 rows (`mips`, `powerpc`, `m68k`, `sh4`, `frv`, `h8300`, `mn103`, `strongarm`, `xscale`) the name left of the arrow is not a literal key in at least one of the two maps, which are keyed by MCU part name or name prefix (`mips32`, `pic32`, `jz4740` → `mips`; `mpc`, `p10`, `ppc` → `generic-powerpc.yaml`); the target right of the arrow does exist in every row. + | MCU | eos board YAML | eboot board port | ebuild MCU_TO_EBOOT_BOARD | ebuild EOS_BOARD_MAP | |-----|---------------|-----------------|--------------------------|---------------------| | nRF52840 | `nrf52840.yaml` ✅ | `nrf52/` ✅ | `nrf52→nrf52` ✅ | `nrf52→nrf52840.yaml` ✅ | @@ -53,6 +174,8 @@ This document tracks alignment between all three EoS components to ensure they r ## Product Profile Alignment (41 profiles) +> Kept from the original audit. On 2026-09-14 all 41 rows still hold, but `products/` has 48 headers; the 7 with no `PRODUCT_MAP` entry are listed in the status table. + | eos Profile (`products/*.h`) | In `eos_config.h` #elif chain | In ebuild `PRODUCT_MAP` | |-----|------|------| | `adapter` | ✅ | ✅ | @@ -152,25 +275,25 @@ To verify alignment is maintained after any change: ```bash # 1. Count eos boards -ls eos/boards/*.yaml | wc -l # Should be 25 +ls eos/boards/*.yaml | wc -l # 84 on 2026-09-14 # 2. Count eboot boards -ls eboot/boards/*/board_*.h | wc -l # Should be 25 +ls eboot/boards/*/board_*.h | wc -l # 83 on 2026-09-14 # 3. Count eos product profiles -ls eos/products/*.h | wc -l # Should be 41 +ls eos/products/*.h | wc -l # 48 on 2026-09-14 # 4. Count eos_config.h #elif entries -grep -c "EOS_PRODUCT_" eos/include/eos/eos_config.h # Should be 41 +grep -c "EOS_PRODUCT_" eos/include/eos/eos_config.h # 42 on 2026-09-14 # 5. Count ebuild PRODUCT_MAP entries -grep -c '":' ebuild/ebuild/eos_ai/eos_project_generator.py | head -1 +grep -c '":' ebuild/ebuild/eos_ai/eos_project_generator.py | head -1 # counts every quoted key in the file; use the ast script above (41) # 6. Count platform enum entries -grep -c "EOS_PLATFORM_" eboot/include/eos_hal.h # Should be 24 +grep -c "EOS_PLATFORM_" eboot/include/eos_hal.h # 24 on 2026-09-14 # 7. Run ebuild test -cd ebuild && python test_full_pipeline.py +cd ebuild && python test_full_pipeline.py # test_full_pipeline.py does not exist on ebuild origin/master (2026-09-14) ``` --- From 6437af817fd75e6fad65f047c4b72183e8b2874f Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Tue, 15 Sep 2026 00:52:51 +0530 Subject: [PATCH 09/13] docs(alignment): count every ebuild file, and the four template names eos does use Two rows overstated. The templates row said nothing in eos refers to ebuild's templates; four eos documents name bare-metal, ble-sensor and linux-app by their ebuild new --template spelling, and all exist. The config-generation row counted EOS_ENABLE_* names in two ebuild files and called the result "ebuild"; measured across every ebuild/**/*.py it is 29 named, 24 in common with eos_config.h, 10 eos-only -- not 24/19/15. The embedded script now scans every file, and its recorded output matches. --- docs/three-way-alignment.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/three-way-alignment.md b/docs/three-way-alignment.md index bdd76035..41f48ac6 100644 --- a/docs/three-way-alignment.md +++ b/docs/three-way-alignment.md @@ -15,8 +15,8 @@ Measured on 2026-09-14 against origin/master of eos, eBoot and ebuild (eos `9ed6 | Platform enum | — | 24 `eos_platform_t` enumerators in `include/eos_hal.h` | `MCU_DATABASE` 171 keys in `eos_hw_analyzer.py`, using 52 distinct `arch` strings | ⚠️ The 24 reproduces. `MCU_DATABASE` describes MCUs by `arch` strings (`arm`, `mips64`, `sh`, …) that share no vocabulary with the enumerators, and nothing maps one onto the other. | | Peripheral keywords | 33 peripheral families with an `eos__init()`: 5 in `hal/include/eos/hal.h`, 28 in `hal/include/eos/hal_extended.h` | — | `PERIPHERAL_KEYWORDS` 46 keywords mapping to 24 distinct types in `eos_hw_analyzer.py`; `ComponentDB` 114 parts in `component_db.py` (its docstring says 200+) | ⚠️ 33 and 24 reproduce; 200+ does not (114). Only 21 of the 24 types are HAL family names: `ethernet`, `gps`, `watchdog` are spelled `eth`, `gnss`, `wdt` in the HAL, and `cellular`, `flash`, `gpu`, `haptic`, `hdmi`, `ir`, `pcie`, `radar`, `touch` have no keyword at all. | | Multicore support | `kernel/include/eos/multicore.h` (38 declarations: `eos_core_*`, `eos_spin_*`, `eos_ipi_*`, `eos_rproc_*`, `eos_shmem_*`, …) | `include/eos_multicore.h` (12 `eos_multicore_*` declarations) | `MULTICORE_MCUS` 13 names in `eos_project_generator.py` | ⚠️ All three exist, and that is all that was checked: the two headers are different APIs, and nothing compares either of them to `MULTICORE_MCUS`. | -| Config generation | 34 `#define EOS_ENABLE_*` in `include/eos/eos_config.h` | `eboot_flash_layout.h` is not a file in eBoot (0 tracked files); it is generated output that eBoot only documents (`configs/README.md`, `docs/quickstart.md`, `docs/book/book.md`) | `EosConfigGenerator.generate_eos_config_h()` writes `eos_product_config.h`; `EosBootIntegrator.generate_from_boot_yaml()` writes `eboot_flash_layout.h` | ⚠️ 39 did not reproduce (34), and "`EosConfigGenerator` produces both" was wrong: two classes, two files. ebuild names 24 `EOS_ENABLE_*` flags (all in `eos_hw_analyzer.py`); 19 are defined in `eos_config.h`, `EOS_ENABLE_GPIO`/`I2C`/`SPI`/`TIMER`/`UART` are defined nowhere in eos, and 15 `eos_config.h` flags (`EOS_ENABLE_MULTICORE`, `EOS_ENABLE_NET`, `EOS_ENABLE_PCIE`, …) are never named by ebuild. | -| Templates | — | — | 6 directories in `templates/` at the ebuild repo root (`bare-metal`, `ble-sensor`, `linux-app`, `rtos-app`, `safety-critical`, `secure-boot`); `ebuild/templates/` does not exist | ⚠️ "5 in `ebuild/templates/`" did not reproduce: 6, at a different path. Nothing in eos or eboot refers to them, so there is nothing to align against. | +| Config generation | 34 `#define EOS_ENABLE_*` in `include/eos/eos_config.h` | `eboot_flash_layout.h` is not a file in eBoot (0 tracked files); it is generated output that eBoot only documents (`configs/README.md`, `docs/quickstart.md`, `docs/book/book.md`) | `EosConfigGenerator.generate_eos_config_h()` writes `eos_product_config.h`; `EosBootIntegrator.generate_from_boot_yaml()` writes `eboot_flash_layout.h` | ⚠️ 39 did not reproduce (34), and "`EosConfigGenerator` produces both" was wrong: two classes, two files. Across `ebuild/**/*.py` (`eos_hw_analyzer.py` 24, `eos_project_generator.py` 24, `component_db.py` 23; 29 distinct) ebuild names 29 `EOS_ENABLE_*` flags; 24 are defined in `eos_config.h`, `EOS_ENABLE_GPIO`/`I2C`/`SPI`/`TIMER`/`UART` are defined nowhere in eos, and 10 `eos_config.h` flags (`EOS_ENABLE_DISPLAY_DRV`, `EOS_ENABLE_ETHERNET_DRV`, `EOS_ENABLE_GPU`, `EOS_ENABLE_HDMI`, `EOS_ENABLE_MULTICORE`, `EOS_ENABLE_NET`, `EOS_ENABLE_PCIE`, `EOS_ENABLE_PCIE_DRV`, `EOS_ENABLE_SDIO`, `EOS_ENABLE_USB_HOST`) are never named by ebuild. | +| Templates | — | — | 6 directories in `templates/` at the ebuild repo root (`bare-metal`, `ble-sensor`, `linux-app`, `rtos-app`, `safety-critical`, `secure-boot`); `ebuild/templates/` does not exist | ⚠️ "5 in `ebuild/templates/`" did not reproduce: 6, at a different path. eos names four of them by their `ebuild new --template` spelling (`bare-metal` in `GETTING_STARTED.md` and `docs/quickstart-host.md`, `ble-sensor` in `docs/quickstart-nrf52.md`, `linux-app` in `docs/quickstart-rpi4.md`); all four exist in ebuild's `templates/`. eBoot names none. Nothing checks that a documented template name exists. | | CLI commands | cmake build (`CMakeLists.txt`) | cmake build (`CMakeLists.txt`) | `new`, `build`, `analyze`, `generate-project` are `@cli.command` functions in `ebuild/cli/commands.py` (23 `@cli.command` entries plus the `repos` group with 6 subcommands) | ✅ The four named subcommands exist by name. That is a presence check on ebuild alone, not an inventory comparison. | --- @@ -99,11 +99,12 @@ print("headers without PRODUCT_MAP entry", sorted(prods - pm)) print("headers without config branch", sorted(prods - chain), "| PRODUCT_MAP keys without header", sorted(pm - prods)) enables = set(re.findall(r"^\s*#\s*define\s+(EOS_ENABLE_[A-Z0-9_]+)", cfg, re.M)) -named = set(re.findall(r"EOS_ENABLE_[A-Z0-9_]+", - show("ebuild", "ebuild/eos_ai/eos_hw_analyzer.py") - + show("ebuild", "ebuild/eos_ai/eos_config_generator.py"))) -print("EOS_ENABLE_* defines", len(enables), "| named in ebuild", len(named), "| common", len(enables & named), - "| ebuild-only", sorted(named - enables), "| eos-only", len(enables - named)) +py_files = [l for l in subprocess.run(["git", "-C", "ebuild", "ls-tree", "-r", "--name-only", + "origin/master", "ebuild/"], capture_output=True, + text=True, check=True).stdout.split() if l.endswith(".py")] +named = set(re.findall(r"EOS_ENABLE_[A-Z0-9_]+", "".join(show("ebuild", f) for f in py_files))) +print("EOS_ENABLE_* defines", len(enables), "| named in ebuild/**/*.py", len(named), "| common", len(enables & named), + "| ebuild-only", sorted(named - enables), "| eos-only", sorted(enables - named)) fam = set() for h in ("hal/include/eos/hal.h", "hal/include/eos/hal_extended.h"): @@ -132,7 +133,7 @@ EOS_BOARD_MAP distinct values 81 | values that are eos yaml 81 | eos yaml never products/*.h 48 | eos_config.h product branches 42 | PRODUCT_MAP 41 headers without PRODUCT_MAP entry ['cast_device', 'desktop', 'home_camera', 'iptv_stb', 'smart_speaker', 'tv_os', 'vbox_test'] headers without config branch ['cast_device', 'desktop', 'home_camera', 'iptv_stb', 'smart_speaker', 'tv_os'] | PRODUCT_MAP keys without header [] -EOS_ENABLE_* defines 34 | named in ebuild 24 | common 19 | ebuild-only ['EOS_ENABLE_GPIO', 'EOS_ENABLE_I2C', 'EOS_ENABLE_SPI', 'EOS_ENABLE_TIMER', 'EOS_ENABLE_UART'] | eos-only 15 +EOS_ENABLE_* defines 34 | named in ebuild/**/*.py 29 | common 24 | ebuild-only ['EOS_ENABLE_GPIO', 'EOS_ENABLE_I2C', 'EOS_ENABLE_SPI', 'EOS_ENABLE_TIMER', 'EOS_ENABLE_UART'] | eos-only ['EOS_ENABLE_DISPLAY_DRV', 'EOS_ENABLE_ETHERNET_DRV', 'EOS_ENABLE_GPU', 'EOS_ENABLE_HDMI', 'EOS_ENABLE_MULTICORE', 'EOS_ENABLE_NET', 'EOS_ENABLE_PCIE', 'EOS_ENABLE_PCIE_DRV', 'EOS_ENABLE_SDIO', 'EOS_ENABLE_USB_HOST'] HAL init families 33 | keyword types that are HAL families 21 | keyword types that are not ['ethernet', 'gps', 'watchdog'] | HAL families with no keyword ['cellular', 'eth', 'flash', 'gnss', 'gpu', 'haptic', 'hdmi', 'ir', 'pcie', 'radar', 'touch', 'wdt'] ``` From 905c9e86ff2a234861509ad945335846cd1b6319 Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Tue, 15 Sep 2026 02:19:47 +0530 Subject: [PATCH 10/13] docs(alignment): annotate the two places the old 25/25 claim survived MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-up on #151. docs/hardware-alignment.md:51 still said "25/25 boards aligned. Every eboot board has a matching eos board definition, and every eos board has a matching eboot port." — a universal claim that this PR's own measurement disproves on both halves: 84 eos board YAMLs against 83 eBoot board directories, only 7 name stems shared, 27 eBoot directories never targeted by MCU_TO_EBOOT_BOARD. It now carries a supersede note in the same shape as the two already in three-way-alignment.md, pointing at the measured status table. docs/three-way-alignment.md: :342 "eboot CMakeLists.txt Board Coverage (25/25)" gets the note the PR body said it had. It did not; the two existing notes were under Board-Level Alignment and Product Profile Alignment. :386 the hardware-alignment.md row is re-graded from ✅ to ⚠️ 25 of 84 / 25 of 83. :387 the row grading this very document "✅ all maps audited" contradicted the ⚠️ NOT ALIGNED banner seven lines from the top of the same file; it now says ⚠️ measured 2026-09-14; see the status table. Every figure written here is from the document's embedded script, re-run at this head: its 18-line output is byte-identical to the recorded block (line 11: "eos yaml 84 | eboot dirs 83 | yaml stems that are eboot dirs 7"; line 12: "... eboot dirs never targeted 27"). pytest tests/unit: 43 passed. --- docs/hardware-alignment.md | 2 ++ docs/three-way-alignment.md | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/hardware-alignment.md b/docs/hardware-alignment.md index 9f37ebe3..b2bec96b 100644 --- a/docs/hardware-alignment.md +++ b/docs/hardware-alignment.md @@ -50,6 +50,8 @@ Legacy/Niche: **Result: 25/25 boards aligned.** Every eboot board has a matching eos board definition, and every eos board has a matching eboot port. +> Superseded: measured 2026-09-14, the full inventories are 84 eos board YAMLs against 83 eBoot board directories, and only 7 names match across them; the 25 rows above are the subset the original audit chose, not the whole. Both halves of the sentence above are false for the full inventories. See `docs/three-way-alignment.md` for the measured status table. + --- ## Detailed Board Inventory diff --git a/docs/three-way-alignment.md b/docs/three-way-alignment.md index 41f48ac6..83376f0f 100644 --- a/docs/three-way-alignment.md +++ b/docs/three-way-alignment.md @@ -341,6 +341,8 @@ python3 sign_image.py --key key.pem --input ../../eos/build/app.bin --output sig ### eboot CMakeLists.txt Board Coverage (25/25) +> Kept from the original audit as a 25-board subset; the full eBoot inventory is 83 board directories (status table above), and 27 of them are never targeted by `MCU_TO_EBOOT_BOARD`. The 25/25 below is true of these 25 rows only. + | Board | In `if/elseif` chain | In FATAL_ERROR help string | |-------|---------------------|---------------------------| | stm32f4 | ✅ | ✅ | @@ -383,8 +385,8 @@ python3 sign_image.py --key key.pem --input ../../eos/build/app.bin --output sig | `eos/README.md` | ✅ full API, profiles | ✅ related project link | ✅ related project link | | `eboot/README.md` | ✅ related project link | ✅ full boot API | ✅ related project link | | `ebuild/README.md` | ✅ builds eos | ✅ builds eboot | ✅ full CLI reference | -| `docs/hardware-alignment.md` | ✅ 25 board YAMLs | ✅ 25 board ports | ✅ MCU maps | -| `docs/three-way-alignment.md` | ✅ profiles, boards | ✅ boards, platform enum | ✅ all maps audited | +| `docs/hardware-alignment.md` | ⚠️ 25 of 84 board YAMLs | ⚠️ 25 of 83 board ports | ✅ MCU maps | +| `docs/three-way-alignment.md` | ⚠️ measured 2026-09-14; see the status table | ⚠️ measured 2026-09-14; see the status table | ⚠️ measured 2026-09-14; see the status table | | `docs/adding-hardware.md` | ✅ HAL backends, profiles | ✅ EBOOT_REGISTER_BOARD | ✅ — | | `docs/api-release-process.md` | ✅ Doxyfile, headers | ✅ Doxyfile, headers | ✅ CLI versioning | | `eos/docs/api-reference.md` | ✅ all modules | — | — | From 237a37f20ecb125e3a958d6a1576ac257a2df413 Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Tue, 15 Sep 2026 02:21:49 +0530 Subject: [PATCH 11/13] docs(alignment): the third section the body promised a note on 905c9e8 annotated hardware-alignment.md:51 and the "eboot CMakeLists.txt Board Coverage (25/25)" section, and turned the two Documentation Alignment rows to warnings -- the review's findings 1 and 2. The PR body also named the Build System table as carrying a supersede note, and it did not. Now it does, with the figure measured today rather than the audit's: eboot's CMakeLists.txt has 84 EBLDR_BOARD STREQUAL "" branches against the table's 25 (grep -cE '(if|elseif)\(EBLDR_BOARD STREQUAL "[a-z0-9_]+"\)' on origin/master; 86 lines match the bare pattern, two of them the "none" guards). EOS_PRODUCT is a free-form cache string, so "41 values" is the help text, not an enumeration. --- docs/three-way-alignment.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/three-way-alignment.md b/docs/three-way-alignment.md index 83376f0f..99ab01a0 100644 --- a/docs/three-way-alignment.md +++ b/docs/three-way-alignment.md @@ -301,6 +301,8 @@ cd ebuild && python test_full_pipeline.py # test_full_pipeline.py doe ## Build System Alignment +> Kept from the original audit; the counts in this table are the original audit's, and the status table at the top supersedes them. On 2026-09-15 eboot's `CMakeLists.txt` has 84 `EBLDR_BOARD STREQUAL ""` branches, not 25, and eos's `EOS_PRODUCT` is a free-form cache string whose accepted values are listed in its help text, not enumerated in CMake. + ### CMake Variables Cross-Reference | Variable | eos `CMakeLists.txt` | eboot `CMakeLists.txt` | ebuild CLI | From 9481429d90179403e6ad7077d3d9d2daa498eb1c Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Tue, 15 Sep 2026 02:26:46 +0530 Subject: [PATCH 12/13] docs(alignment): the branch count is 86 lines naming 83 boards, not 84 The Build System note added at 237a37f says eboot's CMakeLists.txt has 84 EBLDR_BOARD STREQUAL "" branches. Measured at eBoot 221325c, the SHA the document pins: grep -c 'EBLDR_BOARD STREQUAL' gives 86, all of them if/elseif branches with a quoted literal; the distinct literals number 84, but one of them is the sentinel "none", which appears three times and is not a board. So the figure a reviewer reproduces is 86 (lines) or 83 (boards), and 84 is the one count neither obvious command returns. The note now gives both, with the command, and the 83 matches the eBoot board-directory count in the status table. pytest tests/unit: 43 passed. --- docs/three-way-alignment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/three-way-alignment.md b/docs/three-way-alignment.md index 99ab01a0..6d041188 100644 --- a/docs/three-way-alignment.md +++ b/docs/three-way-alignment.md @@ -301,7 +301,7 @@ cd ebuild && python test_full_pipeline.py # test_full_pipeline.py doe ## Build System Alignment -> Kept from the original audit; the counts in this table are the original audit's, and the status table at the top supersedes them. On 2026-09-15 eboot's `CMakeLists.txt` has 84 `EBLDR_BOARD STREQUAL ""` branches, not 25, and eos's `EOS_PRODUCT` is a free-form cache string whose accepted values are listed in its help text, not enumerated in CMake. +> Kept from the original audit; the counts in this table are the original audit's, and the status table at the top supersedes them. On 2026-09-15 eboot's `CMakeLists.txt` has 86 `EBLDR_BOARD STREQUAL ""` branches naming 83 distinct boards (the sentinel `"none"` appears three times; `grep -c 'EBLDR_BOARD STREQUAL' CMakeLists.txt` → 86, distinct quoted literals → 84 including `"none"`), not 25, and eos's `EOS_PRODUCT` is a free-form cache string whose accepted values are listed in its help text, not enumerated in CMake. ### CMake Variables Cross-Reference From ad8ea8209429e4b5f9d601e1ec78cf8628ca6da8 Mon Sep 17 00:00:00 2001 From: kartikey1306 Date: Tue, 15 Sep 2026 11:56:14 +0530 Subject: [PATCH 13/13] docs(alignment): date the Build System note like the rest, and spell the eBoot board as its directory Review follow-up on #151, both findings. three-way-alignment.md:304 -- the Build System note said 2026-09-15 while the file header and the three other supersede notes say 2026-09-14, the date the pins at the top (eos 9ed6831, eBoot 221325c, ebuild 76970c9) were measured against. The figures in the note were measured against that same eBoot 221325c, so the date was the only thing wrong -- and in a document whose argument is that every claim carries a date and a pin, a later date on one row asks whether it was measured against something newer. Now 09-14. hardware-alignment.md -- the Architecture Alignment table (:128) named the eBoot board qemu-arm64; the directory is boards/qemu_arm64/. The cell now says qemu_arm64 and notes that the eos side is qemu-arm64.yaml, since the hyphen/underscore split is the reason only 7 of 84 names match across the repos. The Detailed Board Inventory (:101) listed the same board as `qemu-arm64/` in directory form, the only entry in that column that does not resolve; fixed to `qemu_arm64/` for the same reason. `qemu-arm64.yaml` at :71 is the eos YAML and stays as it is. Verified: every eBoot-column entry in the inventory table now resolves to a directory under eBoot/boards/; the embedded script re-run still matches its recorded 18-line block byte for byte; pytest tests/unit 43 passed. --- docs/hardware-alignment.md | 4 ++-- docs/three-way-alignment.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/hardware-alignment.md b/docs/hardware-alignment.md index b2bec96b..aa43d191 100644 --- a/docs/hardware-alignment.md +++ b/docs/hardware-alignment.md @@ -98,7 +98,7 @@ Legacy/Niche: | `imx8m/` | ARM64 | `EOS_PLATFORM_ARM_CA53` | Cortex-A53 | eMMC | 2 GB | | `am64x/` | ARM | `EOS_PLATFORM_ARM_CA53` | A53+R5F | 64 MB | 2 GB | | `stm32mp1/` | ARM | `EOS_PLATFORM_ARM_CA72` | A7+M4 | eMMC | 512 MB | -| `qemu-arm64/` | ARM64 | `EOS_PLATFORM_ARM_CA53` | Cortex-A57 | virtio | 512 MB | +| `qemu_arm64/` | ARM64 | `EOS_PLATFORM_ARM_CA53` | Cortex-A57 | virtio | 512 MB | | `riscv64_virt/` | RISC-V | `EOS_PLATFORM_RISCV64` | RV64GC | virtio | 256 MB | | `sifive_u/` | RISC-V | `EOS_PLATFORM_RISCV64` | U74 | SPI | 8 GB | | `esp32/` | Xtensa | `EOS_PLATFORM_XTENSA` | LX6 | 4 MB | 520 KB | @@ -125,7 +125,7 @@ Legacy/Niche: | Architecture | eos Toolchain | eos Boards | eboot Boards | Platform Enum | |-------------|--------------|-----------|-------------|---------------| | **ARM Cortex-M** | `arm-none-eabi` | nRF52840, STM32F4, STM32H7, SAMD51 | nrf52, stm32f4, stm32h7, samd51 | `ARM_CM0`..`ARM_CM33` | -| **ARM Cortex-A** | `aarch64-linux-gnu` | RPi4, i.MX8M, AM64x, STM32MP1, QEMU | rpi4, imx8m, am64x, stm32mp1, qemu-arm64 | `ARM_CA53`, `ARM_CA72` | +| **ARM Cortex-A** | `aarch64-linux-gnu` | RPi4, i.MX8M, AM64x, STM32MP1, QEMU | rpi4, imx8m, am64x, stm32mp1, qemu_arm64 (eos: `qemu-arm64.yaml` — names differ) | `ARM_CA53`, `ARM_CA72` | | **RISC-V 64** | `riscv64-linux-gnu` | generic-riscv64, SiFive U | riscv64_virt, sifive_u | `RISCV64` | | **Xtensa** | (vendor SDK) | ESP32 | esp32 | `XTENSA` | | **x86 / x86_64** | `x86_64-linux-gnu` | generic-x86, generic-x86_64 | x86, x86_64_efi | `X86`, `X86_64` | diff --git a/docs/three-way-alignment.md b/docs/three-way-alignment.md index 6d041188..6774cfdb 100644 --- a/docs/three-way-alignment.md +++ b/docs/three-way-alignment.md @@ -301,7 +301,7 @@ cd ebuild && python test_full_pipeline.py # test_full_pipeline.py doe ## Build System Alignment -> Kept from the original audit; the counts in this table are the original audit's, and the status table at the top supersedes them. On 2026-09-15 eboot's `CMakeLists.txt` has 86 `EBLDR_BOARD STREQUAL ""` branches naming 83 distinct boards (the sentinel `"none"` appears three times; `grep -c 'EBLDR_BOARD STREQUAL' CMakeLists.txt` → 86, distinct quoted literals → 84 including `"none"`), not 25, and eos's `EOS_PRODUCT` is a free-form cache string whose accepted values are listed in its help text, not enumerated in CMake. +> Kept from the original audit; the counts in this table are the original audit's, and the status table at the top supersedes them. On 2026-09-14 eboot's `CMakeLists.txt` has 86 `EBLDR_BOARD STREQUAL ""` branches naming 83 distinct boards (the sentinel `"none"` appears three times; `grep -c 'EBLDR_BOARD STREQUAL' CMakeLists.txt` → 86, distinct quoted literals → 84 including `"none"`), not 25, and eos's `EOS_PRODUCT` is a free-form cache string whose accepted values are listed in its help text, not enumerated in CMake. ### CMake Variables Cross-Reference