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
+5
View File
@@ -0,0 +1,5 @@
# src/starkernel/pci/
- `pci.c` — PCI bus enumeration (config-space access, device discovery);
used to locate the VirtIO block device backing
`src/starkernel/virtio/virtio_blk.c`.
+393
View File
@@ -0,0 +1,393 @@
/*
* pci.c — Generic PCI/PCIe subsystem for StarKernel
*
* Config space strategy:
* ARCH_AMD64 : Type 1 port I/O (0xCF8 / 0xCFC). No ECAM mapping needed
* for config access; ECAM is still parsed for completeness
* but port I/O is always the primary path on amd64.
* ARCH_AARCH64/ARCH_RISCV64 : ECAM via ACPI MCFG. UEFI identity-maps
* the window so no additional VMM call is required.
*
* BAR MMIO strategy:
* ARCH_AMD64 : vmm_map_range(phys, phys, size, WR|CD) before first access.
* others : no-op; the UEFI identity map covers PCI MMIO windows.
*
* Only bus 0 is scanned (sufficient for all QEMU virtio devices).
*/
#ifndef __STARKERNEL__
#error "pci.c is kernel-only"
#endif
#include <stddef.h>
#include <stdint.h>
#include "starkernel/pci.h"
#include "console.h"
#ifdef ARCH_AMD64
#include "vmm.h"
#endif
/* -------------------------------------------------------------------------
* ACPI structures for MCFG / ECAM discovery
* ------------------------------------------------------------------------- */
typedef struct {
uint8_t signature[8];
uint8_t checksum;
uint8_t oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
uint32_t length;
uint64_t xsdt_address;
uint8_t extended_checksum;
uint8_t reserved[3];
} __attribute__((packed)) Rsdp2;
typedef struct {
uint8_t signature[4];
uint32_t length;
uint8_t revision;
uint8_t checksum;
uint8_t oem_id[6];
uint8_t oem_table_id[8];
uint32_t oem_revision;
uint32_t creator_id;
uint32_t creator_revision;
} __attribute__((packed)) AcpiHeader;
typedef struct {
uint64_t base_address;
uint16_t segment_group;
uint8_t start_bus;
uint8_t end_bus;
uint32_t reserved;
} __attribute__((packed)) McfgEntry;
typedef struct {
AcpiHeader hdr;
uint64_t reserved;
/* McfgEntry[] follows */
} __attribute__((packed)) McfgTable;
/* -------------------------------------------------------------------------
* Module state
* ------------------------------------------------------------------------- */
static uint64_t g_ecam_base = 0;
/* -------------------------------------------------------------------------
* Port I/O helpers (amd64 only)
* ------------------------------------------------------------------------- */
#ifdef ARCH_AMD64
static inline void sk_outl(uint16_t port, uint32_t val) {
__asm__ volatile("outl %0, %1" : : "a"(val), "Nd"(port) : "memory");
}
static inline uint32_t sk_inl(uint16_t port) {
uint32_t val;
__asm__ volatile("inl %1, %0" : "=a"(val) : "Nd"(port) : "memory");
return val;
}
static inline void sk_outw(uint16_t port, uint16_t val) {
__asm__ volatile("outw %0, %1" : : "a"(val), "Nd"(port) : "memory");
}
static inline uint16_t sk_inw(uint16_t port) {
uint16_t val;
__asm__ volatile("inw %1, %0" : "=a"(val) : "Nd"(port) : "memory");
return val;
}
static inline void sk_outb(uint16_t port, uint8_t val) {
__asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port) : "memory");
}
static inline uint8_t sk_inb(uint16_t port) {
uint8_t val;
__asm__ volatile("inb %1, %0" : "=a"(val) : "Nd"(port) : "memory");
return val;
}
#define PCI_CF8 ((uint16_t)0x0CF8)
#define PCI_CFC ((uint16_t)0x0CFC)
static uint32_t cfg_addr(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
return 0x80000000u | ((uint32_t)bus << 16) | ((uint32_t)slot << 11) |
((uint32_t)fn << 8) | (off & 0xFCu);
}
static uint32_t portio_read32(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
sk_outl(PCI_CF8, cfg_addr(bus, slot, fn, off));
return sk_inl(PCI_CFC);
}
static uint16_t portio_read16(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
sk_outl(PCI_CF8, cfg_addr(bus, slot, fn, off));
return sk_inw((uint16_t)(PCI_CFC + (off & 2u)));
}
static uint8_t portio_read8(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
sk_outl(PCI_CF8, cfg_addr(bus, slot, fn, off));
return sk_inb((uint16_t)(PCI_CFC + (off & 3u)));
}
static void portio_write32(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off, uint32_t v) {
sk_outl(PCI_CF8, cfg_addr(bus, slot, fn, off));
sk_outl(PCI_CFC, v);
}
static void portio_write16(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off, uint16_t v) {
sk_outl(PCI_CF8, cfg_addr(bus, slot, fn, off));
sk_outw((uint16_t)(PCI_CFC + (off & 2u)), v);
}
static void portio_write8(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off, uint8_t v) {
sk_outl(PCI_CF8, cfg_addr(bus, slot, fn, off));
sk_outb((uint16_t)(PCI_CFC + (off & 3u)), v);
}
#endif /* ARCH_AMD64 */
/* -------------------------------------------------------------------------
* ECAM helpers (aarch64 / riscv64 only — amd64 uses port I/O)
* ------------------------------------------------------------------------- */
#ifndef ARCH_AMD64
static volatile uint32_t *ecam_cfg_ptr(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
uint64_t addr = g_ecam_base +
((uint64_t)bus << 20) +
((uint64_t)slot << 15) +
((uint64_t)fn << 12) +
(off & 0xFFCu);
return (volatile uint32_t *)(uintptr_t)addr;
}
static uint32_t ecam_read32(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
return *ecam_cfg_ptr(bus, slot, fn, off);
}
static uint16_t ecam_read16(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
uint32_t dw = *ecam_cfg_ptr(bus, slot, fn, off & ~2u);
return (uint16_t)(dw >> ((off & 2u) * 8u));
}
static uint8_t ecam_read8(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
uint32_t dw = *ecam_cfg_ptr(bus, slot, fn, off & ~3u);
return (uint8_t)(dw >> ((off & 3u) * 8u));
}
static void ecam_write32(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off, uint32_t v) {
*ecam_cfg_ptr(bus, slot, fn, off) = v;
}
static void ecam_write16(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off, uint16_t v) {
volatile uint32_t *p = ecam_cfg_ptr(bus, slot, fn, off & ~2u);
uint32_t dw = *p;
uint32_t shift = (off & 2u) * 8u;
dw = (dw & ~(0xFFFFu << shift)) | ((uint32_t)v << shift);
*p = dw;
}
static void ecam_write8(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off, uint8_t v) {
volatile uint32_t *p = ecam_cfg_ptr(bus, slot, fn, off & ~3u);
uint32_t dw = *p;
uint32_t shift = (off & 3u) * 8u;
dw = (dw & ~(0xFFu << shift)) | ((uint32_t)v << shift);
*p = dw;
}
#endif /* !ARCH_AMD64 */
/* -------------------------------------------------------------------------
* Config space dispatch (portio on amd64, ecam on others)
* ------------------------------------------------------------------------- */
static uint32_t cfg_read32(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
#ifdef ARCH_AMD64
return portio_read32(bus, slot, fn, off);
#else
return ecam_read32(bus, slot, fn, off);
#endif
}
static uint16_t cfg_read16(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
#ifdef ARCH_AMD64
return portio_read16(bus, slot, fn, off);
#else
return ecam_read16(bus, slot, fn, off);
#endif
}
static uint8_t cfg_read8(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off) {
#ifdef ARCH_AMD64
return portio_read8(bus, slot, fn, off);
#else
return ecam_read8(bus, slot, fn, off);
#endif
}
static void cfg_write32(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off, uint32_t v) {
#ifdef ARCH_AMD64
portio_write32(bus, slot, fn, off, v);
#else
ecam_write32(bus, slot, fn, off, v);
#endif
}
static void cfg_write16(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off, uint16_t v) {
#ifdef ARCH_AMD64
portio_write16(bus, slot, fn, off, v);
#else
ecam_write16(bus, slot, fn, off, v);
#endif
}
static void cfg_write8(uint8_t bus, uint8_t slot, uint8_t fn, uint16_t off, uint8_t v) {
#ifdef ARCH_AMD64
portio_write8(bus, slot, fn, off, v);
#else
ecam_write8(bus, slot, fn, off, v);
#endif
}
/* -------------------------------------------------------------------------
* ACPI MCFG parsing
* ------------------------------------------------------------------------- */
static int sig4_eq(const uint8_t *p, const char *s) {
return p[0] == (uint8_t)s[0] && p[1] == (uint8_t)s[1] &&
p[2] == (uint8_t)s[2] && p[3] == (uint8_t)s[3];
}
static int sig8_eq(const uint8_t *p, const char *s) {
int i;
for (i = 0; i < 8; i++)
if (p[i] != (uint8_t)s[i]) return 0;
return 1;
}
static int parse_mcfg(void *rsdp) {
if (!rsdp) return -1;
Rsdp2 *r = (Rsdp2 *)rsdp;
if (!sig8_eq(r->signature, "RSD PTR ")) return -1;
if (r->revision < 2 || r->xsdt_address == 0) return -1;
AcpiHeader *xsdt = (AcpiHeader *)(uintptr_t)r->xsdt_address;
if (!sig4_eq(xsdt->signature, "XSDT")) return -1;
uint32_t hdr_len = xsdt->length;
if (hdr_len <= 36u) return -1;
uint32_t n_entries = (hdr_len - 36u) / 8u;
uint64_t *entries = (uint64_t *)((uint8_t *)xsdt + 36u);
uint32_t i;
for (i = 0; i < n_entries; i++) {
AcpiHeader *sdt = (AcpiHeader *)(uintptr_t)entries[i];
if (!sig4_eq(sdt->signature, "MCFG")) continue;
McfgTable *mcfg = (McfgTable *)sdt;
uint32_t mcfg_len = mcfg->hdr.length;
if (mcfg_len <= 44u) return -1;
uint32_t n_alloc = (mcfg_len - 44u) / 16u;
McfgEntry *ents = (McfgEntry *)((uint8_t *)mcfg + 44u);
uint32_t j;
for (j = 0; j < n_alloc; j++) {
if (ents[j].segment_group == 0) {
g_ecam_base = ents[j].base_address;
return 0;
}
}
return -1;
}
return -1;
}
/* -------------------------------------------------------------------------
* Public API
* ------------------------------------------------------------------------- */
int pci_init(void *rsdp) {
int rc = parse_mcfg(rsdp);
#if defined(ARCH_AARCH64) || defined(ARCH_RISCV64)
if (rc != 0) {
#ifdef ARCH_RISCV64
g_ecam_base = 0x30000000ULL;
console_println("PCI: MCFG miss; using riscv64 fallback ECAM 0x30000000");
rc = 0;
#else
console_println("PCI: MCFG parse failed on aarch64 — no ECAM");
#endif
}
#endif
#ifdef ARCH_AMD64
if (rc != 0) {
console_println("PCI: MCFG parse failed; using port I/O (amd64 default)");
rc = 0;
} else {
/* Map the ECAM window (256 buses × 1 MB = 256 MB) on amd64 so the
* VMM page tables cover it. */
if (g_ecam_base) {
vmm_map_range(g_ecam_base, g_ecam_base, 0x10000000ULL,
VMM_FLAG_WRITABLE | VMM_FLAG_CACHE_DISABLE);
}
console_println("PCI: ECAM mapped (amd64)");
}
#endif
return rc;
}
int pci_find_first(uint16_t vendor_id, uint16_t device_id, PciDevice *out) {
uint8_t slot;
for (slot = 0; slot < 32; slot++) {
uint32_t vid_did = cfg_read32(0, slot, 0, PCI_CFG_VENDOR_ID);
if ((vid_did & 0xFFFFu) == 0xFFFFu) continue;
if ((uint16_t)(vid_did & 0xFFFFu) == vendor_id &&
(uint16_t)(vid_did >> 16) == device_id) {
if (out) {
out->bus = 0;
out->device = slot;
out->function = 0;
out->vendor_id = vendor_id;
out->device_id = device_id;
}
return 0;
}
}
return -1;
}
uint32_t pci_read32(const PciDevice *d, uint16_t off) {
return cfg_read32(d->bus, d->device, d->function, off);
}
uint16_t pci_read16(const PciDevice *d, uint16_t off) {
return cfg_read16(d->bus, d->device, d->function, off);
}
uint8_t pci_read8(const PciDevice *d, uint16_t off) {
return cfg_read8(d->bus, d->device, d->function, off);
}
void pci_write32(const PciDevice *d, uint16_t off, uint32_t v) {
cfg_write32(d->bus, d->device, d->function, off, v);
}
void pci_write16(const PciDevice *d, uint16_t off, uint16_t v) {
cfg_write16(d->bus, d->device, d->function, off, v);
}
void pci_write8(const PciDevice *d, uint16_t off, uint8_t v) {
cfg_write8(d->bus, d->device, d->function, off, v);
}
void pci_enable(const PciDevice *d) {
uint16_t cmd = pci_read16(d, PCI_CFG_COMMAND);
cmd |= (uint16_t)(PCI_CMD_IO_SPACE | PCI_CMD_MEM_SPACE | PCI_CMD_BUS_MASTER);
pci_write16(d, PCI_CFG_COMMAND, cmd);
}
uint64_t pci_bar(const PciDevice *d, int bar_idx) {
uint16_t off = (uint16_t)(PCI_CFG_BAR0 + bar_idx * 4);
uint32_t lo = pci_read32(d, off);
if (lo & PCI_BAR_TYPE_MASK) return 0; /* I/O BAR — unsupported */
if ((lo & 0x6u) == PCI_BAR_MEM_64) {
uint32_t hi = pci_read32(d, (uint16_t)(off + 4));
return ((uint64_t)hi << 32) | (lo & ~0xFu);
}
return lo & ~0xFu;
}
int pci_map_bar(uint64_t phys_addr, uint64_t size) {
#ifdef ARCH_AMD64
if (!phys_addr || !size) return -1;
/* Round size up to page boundary */
uint64_t aligned_size = (size + 0xFFFu) & ~0xFFFu;
return vmm_map_range(phys_addr, phys_addr, aligned_size,
VMM_FLAG_WRITABLE | VMM_FLAG_CACHE_DISABLE);
#else
(void)phys_addr; (void)size;
return 0;
#endif
}