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:
rajames
2026-10-01 15:40:09 -04:00
co-authored by Junie
parent 3c709c115b
commit a8b70e88d3
630 changed files with 2646 additions and 2036 deletions
+249
View File
@@ -0,0 +1,249 @@
/*
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.
*/
/**
* arch.c (amd64) - Architecture abstractions for StarKernel on x86_64
*/
#include "arch.h"
#include <stdint.h>
/*
* GDT entries for 64-bit mode.
* In long mode, the base and limit fields of code/data segments are ignored,
* but the access and flags bytes must be correct.
*/
struct gdt_entry {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_mid;
uint8_t access;
uint8_t granularity; /* includes high nibble of limit */
uint8_t base_high;
} __attribute__((packed));
struct gdtr {
uint16_t limit;
uint64_t base;
} __attribute__((packed));
/*
* GDT layout:
* 0x00: Null descriptor
* 0x08: 64-bit kernel code segment (selector used in IDT)
* 0x10: Kernel data segment
*/
static struct gdt_entry gdt[3] __attribute__((aligned(16)));
/**
* @brief Populate a GDT entry with limit 0xFFFF and a zero base.
*
* In 64-bit long mode the base and limit fields of code/data segment
* descriptors are ignored by the processor; only the @p access and
* @p granularity flag bytes are architecturally significant. This helper
* writes the canonical long-mode descriptor layout: limit = 0xFFFF, all base
* bytes = 0, then stores the provided access and granularity values.
*
* @param index Index into the @c gdt[] array (0 = null, 1 = code, 2 = data).
* @param access Access byte (e.g., 0x9A for kernel code, 0x92 for data).
* @param granularity Granularity/flags byte (e.g., 0x20 sets the L-bit for 64-bit code).
*/
static void gdt_set_entry(int index, uint8_t access, uint8_t granularity)
{
gdt[index].limit_low = 0xFFFF;
gdt[index].base_low = 0;
gdt[index].base_mid = 0;
gdt[index].access = access;
gdt[index].granularity = granularity;
gdt[index].base_high = 0;
}
/**
* @brief Initialise and load the minimal 3-entry GDT required for long mode.
*
* Builds three GDT entries in the @c gdt[] array and loads the descriptor via
* @c LGDT followed by a far-return sequence to reload @c CS to selector 0x08
* and all other segment registers to 0x10:
*
* - Slot 0 (0x00): Null descriptor — all fields zero.
* - Slot 1 (0x08): 64-bit kernel code (access=0x9A, granularity=0x20).
* - Slot 2 (0x10): Kernel data (access=0x92, granularity=0x00).
*
* Must be called before @c arch_interrupts_init() because the IDT uses
* selector 0x08 for all gate descriptors.
*/
static void gdt_init(void)
{
/* Null descriptor */
gdt[0].limit_low = 0;
gdt[0].base_low = 0;
gdt[0].base_mid = 0;
gdt[0].access = 0;
gdt[0].granularity = 0;
gdt[0].base_high = 0;
/*
* 64-bit kernel code segment (selector 0x08)
* Access: 0x9A = Present (1), DPL 0 (00), S=1 (code/data), Exec (1), Conforming (0), Readable (1), Accessed (0)
* Granularity: 0x20 = Long mode (L=1), Default operand size (D=0, must be 0 for long mode)
*/
gdt_set_entry(1, 0x9A, 0x20);
/*
* Kernel data segment (selector 0x10)
* Access: 0x92 = Present (1), DPL 0 (00), S=1 (code/data), Exec (0), Direction (0), Writable (1), Accessed (0)
* Granularity: 0x00 (no special flags for data segment)
*/
gdt_set_entry(2, 0x92, 0x00);
struct gdtr gdtr_val;
gdtr_val.limit = sizeof(gdt) - 1;
gdtr_val.base = (uint64_t)&gdt[0];
__asm__ volatile (
"lgdt %0\n\t"
"pushq $0x08\n\t" /* Push new CS */
"leaq 1f(%%rip), %%rax\n\t" /* Load address of label 1 */
"pushq %%rax\n\t" /* Push return address */
"lretq\n\t" /* Far return to reload CS */
"1:\n\t"
"mov $0x10, %%ax\n\t" /* Load new DS/ES/SS/FS/GS */
"mov %%ax, %%ds\n\t"
"mov %%ax, %%es\n\t"
"mov %%ax, %%ss\n\t"
"mov %%ax, %%fs\n\t"
"mov %%ax, %%gs\n\t"
:
: "m"(gdtr_val)
: "rax", "memory"
);
}
/**
* @brief Perform the earliest x86-64 architecture initialisation.
*
* Called immediately after the UEFI boot stub transfers control to the
* kernel, before the console or interrupt controller are available.
* Loads the minimal three-entry GDT via @c gdt_init() so that subsequent
* code has valid segment selectors. IDT setup is deferred until after the
* console is live (called separately via @c arch_interrupts_init()).
*/
void arch_early_init(void)
{
gdt_init(); /* Must be done before IDT, since IDT uses selector 0x08 */
/* IDT setup is done later via arch_interrupts_init() after console is live */
}
/**
* @brief Enable hardware interrupts on the current CPU core (x86-64 STI).
*
* Issues the @c STI instruction to set the Interrupt Flag. Must be called
* after the IDT is loaded and the APIC is configured, otherwise the first
* interrupt will triple-fault.
*/
void arch_enable_interrupts(void)
{
__asm__ volatile ("sti");
}
/**
* @brief Disable hardware interrupts on the current CPU core (x86-64 CLI).
*
* Issues the @c CLI instruction to clear the Interrupt Flag. Used around
* critical sections in the single-threaded kernel where interrupt delivery
* would corrupt shared state.
*/
void arch_disable_interrupts(void)
{
__asm__ volatile ("cli");
}
/**
* @brief Halt the CPU until the next interrupt (x86-64 HLT).
*
* Issues the @c HLT instruction. In the kernel panic path this is called
* in an infinite loop (interrupts disabled) to stop the processor permanently.
* In normal use it is called once per idle iteration to reduce power consumption.
*/
void arch_halt(void)
{
__asm__ volatile ("hlt");
}
void arch_cold_reset(void)
{
arch_disable_interrupts();
/* Pulse keyboard controller reset line — standard bare-metal amd64 reset */
__asm__ volatile ("outb %0, %1" : : "a"((uint8_t)0xFE), "Nd"((uint16_t)0x64));
/* Should not reach here; spin as fallback */
for (;;) __asm__ volatile ("cli; hlt");
}
/**
* @brief Read the x86-64 Time Stamp Counter (RDTSC).
*
* Executes @c RDTSC and combines @c EDX:EAX into a 64-bit value. Does not
* serialise instruction execution; use @c RDTSCP or a surrounding @c CPUID
* where ordering guarantees are required.
*
* @return Current TSC value in CPU cycles.
*/
uint64_t arch_read_timestamp(void)
{
uint32_t lo, hi;
__asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi));
return ((uint64_t)hi << 32) | lo;
}
/**
* @brief Initialise the x86-64 MMU (stub — deferred to a later milestone).
*
* Full four-level page table setup is deferred; the VMM subsystem
* (@c vmm.c) handles page table management independently. This function
* exists as an architectural hook for future per-CPU MMU configuration
* that must run after GDT/IDT setup but before the first memory access
* through a non-identity-mapped virtual address.
*/
void arch_mmu_init(void)
{
/* Stub: paging will be wired up in later milestones */
}