Files
LithosAnanke/kernel/src/arch/amd64/arch.c
T
rajamesandJunie a8b70e88d3 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>
2026-10-01 15:40:09 -04:00

250 lines
8.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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 */
}