Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/skills/arch-platform-porting/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Current Axvisor LoongArch QEMU tests intentionally use the static `ax-hal/loonga
- **someboot arch layer**: implement or audit entry, relocation, BSS clearing, stack setup, memory map parsing, paging, trap vectors, timer, IRQ, power, SMP, and address translation.
- **CPU runtime**: update `components/axcpu/src/<arch>` for trap entry, context switch, user/kernel context, syscall return path, FP/SIMD state, and per-CPU assumptions.
- **Platform bridge**: update `platforms/axplat-dyn`, `platforms/somehal`, platform config, memory regions, IRQ routing, timer source, power operations, and CPU boot operations.
- **Runtime IRQ ownership**: ArceOS runtime IRQ traps are owned by `ax-cpu` and dispatched through `ax_hal::irq::handle_irq`. `somehal` must stay OS-free and expose controller transactions through `somehal::irq::begin_irq(raw) -> ActiveIrq`; `ActiveIrq` is held while `axplat-dyn` dispatches the IRQ and its `Drop` performs the architecture-specific EOI/complete. Do not reintroduce `_someboot_handle_irq` or `#[somehal::irq_handler]` as runtime dispatch glue.
- **Dynamic firmware devices**: for `rdrive` ACPI probes, real non-empty ACPI ID lists enumerate namespace `Device` nodes and expose `_CRS` memory, I/O port, and IRQ resources through `AcpiInfo`; empty ID lists or synthetic root IDs are reserved for root-table style callbacks.
- **Page tables and memory**: check PTE flags, huge page support, direct map, kernel high map, MMIO map, TLB/cache barriers, and early `phys_to_virt` behavior before MMU state is fully recorded.
- **Drivers and rootfs**: check PCI command bits, MMIO/iomap, DMA address width, virtio transport, block device visibility, rootfs patching, and console/input feature flags.
Expand All @@ -42,6 +43,7 @@ Current Axvisor LoongArch QEMU tests intentionally use the static `ax-hal/loonga
- On LoongArch OVMF, capture the EFI FDT configuration table as well as ACPI RSDP. QEMU exposes LS7A RTC through the preserved FDT node `loongson,ls7a-rtc`; its ACPI DSDT may not contain a `LOON0001` RTC device.
- Allocate and align boot stack, per-CPU areas, secondary stacks, boot arguments, and page tables before enabling SMP.
- Install trap vectors before enabling interrupts, timer interrupts, MMU faults, or secondary CPU execution.
- On x86 QEMU, do not trust CPUID timing leaves unless the reported TSC frequency is plausible; some virtual CPU combinations expose invalid zero or tiny values. Prefer a trusted hypervisor timing leaf, then CPUID timing data, then PIT-based TSC calibration before falling back to processor base frequency.
- Build page tables for identity/firmware access, direct map, kernel high map, MMIO, and per-CPU data as the arch requires.
- Flush TLB/cache and use architecture barriers around page table writes, boot argument writes, and secondary CPU release.
- After `ExitBootServices`, do not call UEFI Boot Services. Retry only through the correct memory-map-key sequence before exit.
Expand Down
2 changes: 1 addition & 1 deletion components/axcpu/src/aarch64/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn aarch64_trap_handler(tf: &mut TrapFrame, kind: TrapKind, source: TrapSource)
);
}
TrapKind::Irq => {
crate::trap::irq_handler(0);
crate::trap::dispatch_irq(0);
}
TrapKind::Synchronous => {
#[cfg(not(feature = "arm-el2"))]
Expand Down
2 changes: 1 addition & 1 deletion components/axcpu/src/aarch64/uspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl UserContext {

let ret = match kind {
TrapKind::Irq => {
crate::trap::irq_handler(0);
crate::trap::dispatch_irq(0);
ReturnReason::Interrupt
}
TrapKind::Fiq | TrapKind::SError => ReturnReason::Unknown,
Expand Down
2 changes: 1 addition & 1 deletion components/axcpu/src/loongarch64/uspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl UserContext {
let ret = match estat.cause() {
Trap::Interrupt(_) => {
let irq_num: usize = estat.is().trailing_zeros() as usize;
crate::trap::irq_handler(irq_num);
crate::trap::dispatch_irq(irq_num);
ReturnReason::Interrupt
}
Trap::Exception(Exception::Syscall) => {
Expand Down
2 changes: 1 addition & 1 deletion components/axcpu/src/riscv/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn riscv_trap_handler(tf: &mut TrapFrame) {
}
Trap::Exception(E::Breakpoint) => handle_breakpoint(tf),
Trap::Interrupt(_) => {
crate::trap::irq_handler(scause.bits());
crate::trap::dispatch_irq(scause.bits());
}
_ => {
let bt = tf.backtrace();
Expand Down
2 changes: 1 addition & 1 deletion components/axcpu/src/riscv/uspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl UserContext {
let stval = stval::read();
match cause {
Trap::Interrupt(_) => {
crate::trap::irq_handler(scause.bits());
crate::trap::dispatch_irq(scause.bits());
ReturnReason::Interrupt
}
Trap::Exception(E::UserEnvCall) => {
Expand Down
2 changes: 1 addition & 1 deletion components/axcpu/src/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub fn dispatch_page_fault(addr: VirtAddr, flags: PageFaultFlags) -> bool {
/// IRQ handler.
#[eii]
pub fn irq_handler(irq: usize) -> bool {
default_irq_handler(irq)
dispatch_irq(irq)
}

/// Page fault handler.
Expand Down
2 changes: 1 addition & 1 deletion components/axcpu/src/x86_64/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fn x86_trap_handler(tf: &mut TrapFrame) {
);
}
IRQ_VECTOR_START..=IRQ_VECTOR_END => {
crate::trap::irq_handler(tf.vector as _);
crate::trap::dispatch_irq(tf.vector as _);
}
_ => {
let bt = tf.backtrace();
Expand Down
2 changes: 1 addition & 1 deletion components/axcpu/src/x86_64/uspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl UserContext {
}
(LEGACY_SYSCALL_VECTOR, _) => ReturnReason::Syscall,
(IRQ_VECTOR_START..=IRQ_VECTOR_END, _) => {
crate::trap::irq_handler(vector as _);
crate::trap::dispatch_irq(vector as _);
ReturnReason::Interrupt
}
_ => ReturnReason::Exception(ExceptionInfo {
Expand Down
7 changes: 1 addition & 6 deletions components/someboot/src/arch/aarch64/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ use super::context::Context;

#[aarch64_trap_handler(kind = "irq")]
fn handle_irq(_ctx: &Context) {
unsafe extern "Rust" {
fn __aarch64_irq_handler();
}
unsafe {
__aarch64_irq_handler();
}
panic!("unexpected AArch64 IRQ in someboot before runtime trap setup");
}

#[aarch64_trap_handler(kind = "fiq")]
Expand Down
16 changes: 4 additions & 12 deletions components/someboot/src/arch/loongarch64/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,18 +186,10 @@ fn configure_exception_vector(verbose: bool) {

/// 处理向量中断
fn do_vint(_tf: &mut TrapFrame) {
let mut estat = estat::read().is();

while estat != 0 {
let hwirq = estat.trailing_zeros() + 1;
estat &= !(1 << (hwirq - 1));

unsafe extern "Rust" {
fn _someboot_handle_irq(hwirq: usize);
}

unsafe { _someboot_handle_irq((hwirq - 1) as _) };
}
panic!(
"unexpected LoongArch interrupt in someboot before runtime trap setup: pending={:#x}",
estat::read().is()
);
}

/// Page Fault 处理函数 (普通 TLB 异常: TLBL, TLBS, TLBI, TLBM, TLBNR, TLBNX, TLBPE)
Expand Down
4 changes: 2 additions & 2 deletions components/someboot/src/arch/riscv64/trap.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::{arch::global_asm, mem::offset_of};

use crate::irq;
use crate::timer;

const SCAUSE_INTERRUPT_BIT: usize = 1usize << (usize::BITS as usize - 1);
const SCAUSE_SUPERVISOR_TIMER: usize = 5;
Expand Down Expand Up @@ -68,7 +68,7 @@ extern "C" fn __riscv64_handle_trap(tf: &mut TrapFrame) {
if (scause & SCAUSE_INTERRUPT_BIT) != 0 {
let cause = scause & !SCAUSE_INTERRUPT_BIT;
if cause == SCAUSE_SUPERVISOR_TIMER {
irq::handle_irq(irq::systimer_irq());
timer::ack();
return;
}
}
Expand Down
85 changes: 83 additions & 2 deletions components/someboot/src/arch/x86_64/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@ pub struct Console;

const COM1_PORT: u16 = 0x3f8;
const COM1_CLOCK_HZ: u32 = 1_843_200;
const COM1_IRQ_VECTOR: usize = 0x30 + 4;

const UART_IER: u16 = 1;
const UART_IIR: u16 = 2;
const UART_LCR: u16 = 3;
const UART_MCR: u16 = 4;
const UART_LSR: u16 = 5;

const IER_RECEIVED_DATA_AVAILABLE: u8 = 1 << 0;
const IER_RECEIVER_LINE_STATUS: u8 = 1 << 2;

const LCR_DIVISOR_LATCH_ACCESS: u8 = 1 << 7;
const MCR_INTERRUPT_OUTPUT_ENABLE: u8 = 1 << 3;

const IIR_NO_INTERRUPT_PENDING: u8 = 1 << 0;
const IIR_INTERRUPT_ID_MASK: u8 = 0x0e;
const IIR_RECEIVER_LINE_STATUS: u8 = 0x06;
const IIR_RECEIVED_DATA_AVAILABLE: u8 = 0x04;
const IIR_CHARACTER_TIMEOUT: u8 = 0x0c;

const LSR_DATA_READY: u8 = 1 << 0;
const LSR_OVERRUN_ERROR: u8 = 1 << 1;
const LSR_RX_ERROR_MASK: u8 = 0x1e;

impl crate::console::ArchConsoleOps for Console {
fn init() -> bool {
Expand All @@ -24,12 +47,70 @@ impl crate::console::ArchConsoleOps for Console {

fn read_byte() -> Option<u8> {
unsafe {
let status = x86::io::inb(COM1_PORT + 5);
if status & 1 == 0 {
let status = x86::io::inb(COM1_PORT + UART_LSR);
if status & LSR_DATA_READY == 0 {
None
} else {
Some(x86::io::inb(COM1_PORT))
}
}
}

fn irq_num() -> Option<usize> {
Some(COM1_IRQ_VECTOR)
}

fn set_input_irq_enabled(enabled: bool) {
unsafe {
let lcr = x86::io::inb(COM1_PORT + UART_LCR);
x86::io::outb(COM1_PORT + UART_LCR, lcr & !LCR_DIVISOR_LATCH_ACCESS);

let mut mcr = x86::io::inb(COM1_PORT + UART_MCR);
if enabled {
mcr |= MCR_INTERRUPT_OUTPUT_ENABLE;
} else {
mcr &= !MCR_INTERRUPT_OUTPUT_ENABLE;
}
x86::io::outb(COM1_PORT + UART_MCR, mcr);

x86::io::outb(
COM1_PORT + UART_IER,
if enabled {
IER_RECEIVED_DATA_AVAILABLE | IER_RECEIVER_LINE_STATUS
} else {
0
},
);
}
}

fn handle_irq() -> u32 {
let iir = unsafe { x86::io::inb(COM1_PORT + UART_IIR) };
let lsr = unsafe { x86::io::inb(COM1_PORT + UART_LSR) };
let mut events = 0;

if lsr & LSR_DATA_READY != 0 {
events |= crate::console::CONSOLE_IRQ_RX_READY;
}
if lsr & LSR_OVERRUN_ERROR != 0 {
events |= crate::console::CONSOLE_IRQ_OVERRUN;
}
if lsr & LSR_RX_ERROR_MASK != 0 {
events |= crate::console::CONSOLE_IRQ_RX_ERROR;
}

if iir & IIR_NO_INTERRUPT_PENDING == 0 {
match iir & IIR_INTERRUPT_ID_MASK {
IIR_RECEIVED_DATA_AVAILABLE | IIR_CHARACTER_TIMEOUT => {
events |= crate::console::CONSOLE_IRQ_RX_READY;
}
IIR_RECEIVER_LINE_STATUS => {
events |= crate::console::CONSOLE_IRQ_RX_ERROR;
}
_ => {}
}
}

events
}
}
Loading