iT邦幫忙

0

[6.1810][code] xv6 的 Device (二) : plic

  • 分享至 

  • xImage
  •  

系列文章 : [6.1810] 跟著 MIT 6.1810 學習基礎作業系統觀念

大綱

  • kernel/plic.c/plicinit
  • kernel/plic.c/plicinithart
  • kernel/plic.c/plic_claim
  • kernel/plic.c/plic_complete

kernel/plic.c/plicinit

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。

  • VIRTIO0_IRQ == 1
  • UART0_IRQ == 10

               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 來看到相對應的資訊。

  • 可以看到 plic 的 phandle 是 7
  • virtio_mmio 跟 serial ( uart ) 的 interrupt-parent 都是 0x7,表示這兩個 device 會接到 plic
  • virtio_mmio 的 interrupt ID 為 1,符合 xv6-riscv driver 的設定
  • serial 的 interrupt ID 為 10,符合 xv6-riscv driver 的設定


kernel/plic.c/plicinithart

void
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)
  • PLIC 的 Interrupt Enable register 的 offset 為 0x2000。
  • 每一根 interrupt source 需要有一個 bit,最多支援 1023 個 interrupt ( 外加一個永遠不會 assert 的 interrupt ),1024 bits = 128 bytes = 0x80 bytes
  • 0x2000 = 第 0 個 hart 的 M-mode
  • 0x2080 = 第 0 個 hart 的 S-mode
  • 0x2100 = 第 1 個 hart 的 M-mode

  // 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)
  • 每一個 context 會配置 0x1000 的大小
  • 0x201000 = 第 0 個 hart 的 M-mode
  • 0x202000 = 第 0 個 hart 的 S-mode
  • 0x203000 = 第 1 個 hart 的 M-mode


kernel/plic.c/plic_claim

// 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 來進行處理。



kernel/plic.c/plic_complete

// 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 已經被處理完了。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言