Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure
Source tree reorganization: - Move StarForth v3 engine to v3/ (src/, include/, Makefile) - Move kernel to kernel/ (src/, include/, linker/, Makefile) - Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md) - Move FABRIC-0..4.md to docs/fabric/ - Move ONTOLOGY.md and ROADMAP.md to docs/ Board infrastructure: - Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/ - Each board has board.mk (ISA, CPU flags, boot recipe) and README.md - Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET - make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board - ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet) - scripts/mkdiskimage.sh builds disk images for all boards Docs pipeline: - docs/book/ with LaTeX master (main.tex) and Makefile - pandoc converts Markdown to LaTeX at build time - Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks) - make docs builds single PDF (754 pages, 0 missing characters) - make docs TARGET=<board> adds board appendix - build/docs/<book|board>/meta.tex stamps git commit into PDF Bug fixes: - 42 include paths that only worked by accident now use correct relative paths - clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build) - Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved - Doxyfile, .clang-tidy, README.md, Kconfig paths updated Verified: - Hosted v3 build passes 1012 tests, 0 failures - SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE) - Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes - make clean TARGET=<board> removes only that board and its ISA objects - make all builds all boards, hosted v3, and docs in one run Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,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`.
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user