Closed NoeTerrier closed 2 months ago
I found a workaround with either one of these solutions:
replacing
self.csr.mip = Arch::read_csr(Csr::Mip);
by:
self.csr.mip = Arch::read_csr(Csr::Mip) & !mie::SEIE_FILTER;
in virts.rs::switch_from_payload_to_firmware
cleaning the SEIP bit in virts.rs::switch_from_firmware_to_payload
when installing the register
Hiding mip.SEIE (return value with SEIE bit set to 0) when firmware reads mip
So if the SEIP bit is reset it works, but I don't know why.
After further investigation, it seems that the SEIP bit of mip is not cleared. Typing to the keyboard seems to produce an external interrupt, which is correctly identified by Linux (exception code 9). Linux traps and the trap handler eventually calls plic_handle_irq
. After handling of the interrupt, SEIP bit is still set.
Here is the code of plic_handle_irq
in linux/drivers/irqchip/irq-sifive-plic.c
.
/*
* Handling an interrupt is a two-step process: first you claim the interrupt
* by reading the claim register, then you complete the interrupt by writing
* that source ID back to the same claim register. This automatically enables
* and disables the interrupt, so there's nothing else to do.
*/
static void plic_handle_irq(struct irq_desc *desc)
{
struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
struct irq_chip *chip = irq_desc_get_chip(desc);
void __iomem *claim = handler->hart_base + CONTEXT_CLAIM;
irq_hw_number_t hwirq;
WARN_ON_ONCE(!handler->present);
chained_irq_enter(chip, desc);
while ((hwirq = readl(claim))) {
int err = generic_handle_domain_irq(handler->priv->irqdomain,
hwirq);
if (unlikely(err)) {
dev_warn_ratelimited(handler->priv->dev,
"can't find mapping for hwirq %lu\n", hwirq);
}
}
chained_irq_exit(chip, desc);
}
Using gdb and stopping at the point of time when the loop occurs, in the trap handler, and manually clearing SEIP allows the shell to function a bit more before falling into the same loop again.
Using sifive-u54 cpu as the virtual cpu for qemu, linux boots but randomly falls in what appears to be an infinite loop.
To reproduce: checkout commit
b7f014fe04fd78294af1ad965ec3a2a7b2dc9fad
, add.arg("-cpu").arg("sifive-u54")
to the runner's QEMU arguments and runjust run linux
. (with info log level and sufficient number of exits)Result: after few tries, it eventually ends up in a loop and finishes by maximal number of exits:
Expected: Linux boots and power down gracefully: