系列文章 : [6.1810] 跟著 MIT 6.1810 學習基礎作業系統觀念
void
plicinit(void)
只有一個 CPU 需要呼叫這個 function。在 boot 時的 main funtion 會呼叫 plicinit 以便對 PLIC 進行初始化。
{
// set desired IRQ priorities non-zero (otherwise disabled).
*(uint32*)(PLIC + UART0_IRQ*4) = 1;
*(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1;
}
設定 Interrupt Priorities register,把相對應的 IRQ 的 priority 設為 1。
在 xv6-riscv,PLIC 目前會接上 VIRTIO0_IRQ, 以及 UART0_IRQ。
virtio_mmio@10001000 {
interrupts = <0x01>;
interrupt-parent = <0x07>;
};
serial@10000000 {
interrupts = <0x0a>;
interrupt-parent = <0x07>;
};
plic@c000000 {
phandle = <0x07>;
};
我們可以翻開 QEMU RISC-V virt machine 的 dts 來看到相對應的資訊。
interrupt-parent 都是 0x7,表示這兩個 device 會接到 plicvoid
plicinithart(void)
{
int hart = cpuid();
plicinithart 是每一個 hart 都需要呼叫的初始化,
因為每一個 hart 的 S-mode 都是一個 context ( 也就是 PLIC 的 target ),每一個 hart 都需要為自己的 S-mode 做設定。
// set enable bits for this hart's S-mode
// for the uart and virtio disk.
*(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ);
設定每一個 hart 的 S-mode 的 Interrupt Enable register,將 UART0_IRQ 跟 VIRTIO0_IRQ 設為 enable。
#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100)
// set this hart's S-mode priority threshold to 0.
*(uint32*)PLIC_SPRIORITY(hart) = 0;
}
把每一個 hart 的 S-mode 的 Priority Threshold register 設為 0,
代表每一個 priority 大於 0 的 interrupt 都接受。
#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000)
// ask the PLIC what interrupt we should serve.
int
plic_claim(void)
{
int hart = cpuid();
int irq = *(uint32*)PLIC_SCLAIM(hart);
return irq;
}
讀取該 hart 的 S-mode 相對應的 claim register,取出目前已經發生的 interrupt 中 priority 最高的 interrupt 來進行處理。
// tell the PLIC we've served this IRQ.
void
plic_complete(int irq)
{
int hart = cpuid();
*(uint32*)PLIC_SCLAIM(hart) = irq;
}
寫入該 hart 的 S-mode 相對應的 completion register。
代表該 interrupt ID 已經被處理完了。