Trap from Kernel Space Sequence

sequenceDiagram
  autonumber

  actor K as Kernel code
  participant CPU as RISC-V hardware / CSRs
  participant KV as kernelvec<br/>(kernelvec.S)
  participant KT as kerneltrap()<br/>(trap.c)
  participant DI as devintr()<br/>(trap.c)
  participant B as device / timer backend
  participant P as panic / yield

  Note over K,CPU: Mode = S<br/>satp = kernel page table<br/>stvec = kernelvec

  K->>CPU: device interrupt / timer interrupt / kernel exception

  CPU->>CPU: save trap state<br/>sepc = interrupted kernel PC<br/>scause = cause<br/>stval = fault address if any
  CPU->>CPU: stay in supervisor mode
  CPU->>KV: set PC = stvec<br/>enter kernelvec

  Note over KV: Mode = S<br/>satp already = kernel page table<br/>sp already = current kernel stack

  KV->>KV: make stack frame on current kernel stack
  KV->>KV: save caller-saved registers<br/>ra gp tp t0-t2 a0-a7 t3-t6
  KV->>KT: call kerneltrap()

  Note over KT: kerneltrap runs on interrupted kernel stack

  KT->>CPU: read sepc
  KT->>CPU: read sstatus
  KT->>CPU: read scause

  KT->>KT: check SPP == supervisor
  alt trap did not come from supervisor mode
    KT->>P: panic("kerneltrap: not from supervisor mode")
  else came from supervisor mode
    KT->>KT: continue
  end

  KT->>KT: check interrupts are disabled
  alt interrupts are enabled
    KT->>P: panic("kerneltrap: interrupts enabled")
  else interrupts disabled
    KT->>DI: devintr()
  end

  alt supervisor external interrupt
    DI->>B: plic_claim()
    alt UART interrupt
      B->>B: uartintr()
    else virtio disk interrupt
      B->>B: virtio_disk_intr()
    else unknown external irq
      B->>B: print unexpected irq
    end
    B->>B: plic_complete(irq)
    DI-->>KT: return 1

    KT->>KT: ordinary device interrupt<br/>no yield required

  else timer interrupt
    DI->>B: clockintr()
    B->>B: ticks++ on CPU 0<br/>wakeup(&ticks)<br/>set next stimecmp
    DI-->>KT: return 2

    alt myproc() != 0
      KT->>P: yield()
      P-->>KT: later resumes inside kerneltrap()
    else no current process
      KT->>KT: do not yield
    end

  else not a recognized interrupt
    DI-->>KT: return 0
    KT->>KT: print scause, sepc, stval
    KT->>P: panic("kerneltrap")
  end

  KT->>CPU: restore sepc
  KT->>CPU: restore sstatus
  KT-->>KV: return to kernelvec

  KV->>KV: restore caller-saved registers<br/>tp is not restored
  KV->>KV: pop stack frame
  KV->>CPU: sret

  CPU->>K: resume interrupted kernel code

  Note over K,CPU: Mode = S<br/>satp = kernel page table