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
3 changes: 2 additions & 1 deletion .claude/skills/arch-platform-porting/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ Adjust the package list to match the crates touched.
3. Use QEMU debug flags such as `-d int,cpu_reset,guest_errors` and `-S -s` when xtask exposes or can be patched to pass them.
4. Inspect symbols and generated images with `llvm-objdump`, `readelf`, and map files. Confirm runtime addresses, not only link addresses.
5. Compare with local Linux architecture code for ordering of MMU, trap, SMP, and cache/TLB barriers when uncertain. First search for a local Linux source tree, then inspect the matching `arch/<linux-arch>` directory; do not assume a fixed path.
6. Turn the root cause into a regression test or a focused QEMU case when practical.
6. On one-shot timer platforms, verify the IRQ handler acknowledges the current timer interrupt before dispatching into code that reprograms the next event. In particular, LoongArch timer handlers must not clear `TICLR` after `_handle_irq()` / `dispatch_irq()`, because the timer tick path may already have armed a near-deadline event and a late acknowledge can clear the freshly-pending interrupt, leaving timer-based sleeps stuck.
7. Turn the root cause into a regression test or a focused QEMU case when practical.

## Common Failure Signals

Expand Down
50 changes: 28 additions & 22 deletions platforms/ax-plat-loongarch64-qemu-virt/src/irq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,34 +121,40 @@ impl IrqIf for IrqIfImpl {

trace!("IRQ {irq:?}");

if let IrqType::Ipi = irq {
let mut status = iocsr_read_w(IOCSR_IPI_STATUS);
if status != 0 {
iocsr_write_w(IOCSR_IPI_CLEAR, status);
trace!("IPI status = {:#x}", status);

while status != 0 {
let vector = status.trailing_zeros() as usize;
status &= !(1 << vector);
if !dispatch_irq(irq.as_usize()).handled {
warn!("Unhandled IRQ {irq:?}");
match irq {
IrqType::Timer => {
// Clear the interrupt before dispatching. The timer handler
// programs the next one-shot event; clearing afterwards can
// drop a freshly-pending event and leave sleepers blocked.
ticlr::clear_timer_interrupt();
if !dispatch_irq(irq.as_usize()).handled {
debug!("Unhandled IRQ {irq:?}");
}
}
IrqType::Ipi => {
let mut status = iocsr_read_w(IOCSR_IPI_STATUS);
if status != 0 {
iocsr_write_w(IOCSR_IPI_CLEAR, status);
trace!("IPI status = {:#x}", status);

while status != 0 {
let vector = status.trailing_zeros() as usize;
status &= !(1 << vector);
if !dispatch_irq(irq.as_usize()).handled {
warn!("Unhandled IRQ {irq:?}");
}
}
}
}
} else {
if !dispatch_irq(irq.as_usize()).handled {
debug!("Unhandled IRQ {irq:?}");
IrqType::Io | IrqType::Ex(_) => {
if !dispatch_irq(irq.as_usize()).handled {
debug!("Unhandled IRQ {irq:?}");
}
}
}

match irq {
IrqType::Timer => {
ticlr::clear_timer_interrupt();
}
IrqType::Ex(irq) => {
eiointc::complete_irq(irq);
}
_ => {}
if let IrqType::Ex(irq) = irq {
eiointc::complete_irq(irq);
}

Some(irq.as_usize())
Expand Down
6 changes: 5 additions & 1 deletion platforms/somehal/src/arch/loongarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ impl PlatOp for Plat {
let irq = match raw {
raw if raw == someboot::irq::systimer_irq().raw() => {
let irq = someboot::irq::IrqId::new(raw);
_handle_irq(raw.into());
// Clear the current timer interrupt before dispatching. The
// dispatch path reprograms the next one-shot timer; clearing
// afterwards can drop a newly-arrived timer edge and strand
// timer-based sleeps.
someboot::timer::ack();
_handle_irq(raw.into());
irq
}
IPI_IRQ => {
Expand Down