qemu-riscv
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Handling Timer Interrupts in Supervisor Mode (machine virt)


From: Alek Fröhlich
Subject: Re: Handling Timer Interrupts in Supervisor Mode (machine virt)
Date: Mon, 5 Apr 2021 14:01:51 -0300

It is also worth noting that mideleg[5] (MTI) should be delegatable; however, it is not. I've also tested in the qemu I have (I am using a pre compiled version; the only thing I know is that it is v5.0 with risc32-softmmu), and

CPU::mideleg_write(CPU::MTI);

does nothing (mideleg continues to be 0 after the csrw).

Is this a bug? If it is not, then what is the reason for this?

Thanks, again,
Alek.

On Mon, Apr 5, 2021 at 1:08 PM Alek Fröhlich <alek.frohlich@gmail.com> wrote:
Hello all,

I am implementing a C++ Micro Kernel for RISCV, and I am having trouble to arrive at a good solution for the handling of timer interrupts.

I'd desire to run the entire system (after its setup phase) in supervisor/user mode, but, as it seems, there is no way to make the CLINT generate STI interrupt requests so that mideleg can automatically forward them to the S-mode handler pointed by stvec.

I took this conclusion based on two pieces of evidence:
  1. The RISCV privileged specification (version 1.10) lets it be understood that the bit MTI (on MIP) is hardwired to the comparison (mtime >= mtimecmp)?
  2. The QEMU implementation of Sifive's CLINT doesn't perform any tests (e.g., "am I configured to cause S-Mode IRQs"?) before issuing a MTI request (see this)
Then, the only solution I found was to always let the STI bit on SIP turned on and to let a M-mode handler turn the corresponding SIE bit (the S-mode handler is responsible for clearing this bit so as to avoid looping on STI IRQs).

This is not very satisfying; are there any other alternatives that would allow my kernel to have a preemptive scheduler without the need of ever switching back to M-mode?

After all, switching to M-mode after the setup has all kinds of unnecessary complications; to name one, consider the scenario where the M-mode handler has to save some of the process' context before being able to perform its task (this is currently the case; I've left a picture of my actual M-mode handler below). In this scenario, a stack must be used; but which stack? $sp, at the time the handler starts executing, points to a virtual address that, in my case, isn't even valid physical memory. Hence, one must also set aside a kernel stack for handling M-mode interrupts.

Of course, later when I have syscalls implemented, there will be another need for such kstack; however, that is only to illustrate my point.

[Handler; how would you even take advantage of __attribute__((interrupt)) if you had to fiddle with $sp before saving context?]

extern "C" [[gnu::interrupt, gnu::aligned(4)]] void _mmode_forward() {
Reg id = CPU::mcause();
if((id & IC::INT_MASK) == CLINT::IRQ_MAC_TIMER) {
Timer::reset();
CPU::sie(CPU::STI);
}
Reg interrupt_id = 1 << ((id & IC::INT_MASK) - 2);
if(CPU::int_enabled() && (CPU::sie() & (interrupt_id)))
CPU::mip(interrupt_id);
}


[Flags]
-machine virt -cpu rv32gcsu-v1.10.0 -smp $(CPUS) -m $(MEM_SIZE)k -serial mon:stdio -bios none -nographic -no-reboot -kernel

Where, for now, CPUS=1 and MEM_SIZE ends up totalling 128Mb.

Thanks very much for your attention,
Alek Frohlich.

ps: I was not sure if this was the correct medium to post this question; if it was not, I apologize.
ps2: If needed, I can attach the current version of the kernel I mentioned.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]