xv6, line by line
kernel/plic.c

kernel/plic.c

C · 47 lines · annotated 100% · kernel · upstream

About this file

The driver for the PLIC (platform-level interrupt controller), the device that sits between the other devices and the harts. The UART and the disk do not interrupt a hart directly: each raises a numbered interrupt source at the PLIC, and the PLIC decides which harts to signal, using a supervisor external interrupt.

The PLIC is programmed entirely through memory-mapped registers starting at PLIC = 0x0c000000, whose addresses are defined in kernel/memlayout.h. There are four kinds:

Register Address Meaning
priority of source i PLIC + 4*i 0 = never deliver; higher = more urgent
enable bits for a context PLIC_SENABLE(hart) bit i = deliver source i to this hart’s supervisor mode
threshold for a context PLIC_SPRIORITY(hart) deliver only sources whose priority is above this
claim / complete PLIC_SCLAIM(hart) read: which source to serve; write: done with it

A context is one hart in one privilege mode. On QEMU’s virt machine, hart h has context 2h for machine mode and 2h+1 for supervisor mode; the S macros compute the addresses for context 2h+1.

The four functions split into setup (plicinit once, plicinithart per hart, from main) and the per-interrupt pair plic_claim / plic_complete, called from devintr.

Read before: kernel/memlayout.h. Read next: devintr in kernel/trap.c.

1#include "types.h"
2#include "param.h"
4#include "riscv.h"
5#include "defs.h"
7//
8// the riscv Platform Level Interrupt Controller (PLIC).
9//
11void
14 // set desired IRQ priorities non-zero (otherwise disabled).
15 *(uint32 *)(PLIC + UART0_IRQ * 4) = 1;
16 *(uint32 *)(PLIC + VIRTIO0_IRQ * 4) = 1;
19void
22 int hart = cpuid();
24 // set enable bits for this hart's S-mode
25 // for the uart and virtio disk.
28 // set this hart's S-mode priority threshold to 0.
32// ask the PLIC what interrupt we should serve.
33int
36 int hart = cpuid();
38 return irq;
41// tell the PLIC we've served this IRQ.
42void
45 int hart = cpuid();