Trap from User Space Sequence

sequenceDiagram
  autonumber

  actor U as User code
  participant CPU as RISC-V hardware / CSRs
  participant UV as uservec<br/>(trampoline.S)
  participant TF as p->trapframe
  participant UT as usertrap()<br/>(trap.c)
  participant H as handler logic<br/>syscall / devintr / vmfault
  participant PR as prepare_return()<br/>(trap.c)
  participant UR as userret<br/>(trampoline.S)

  Note over U,CPU: Mode = U<br/>satp = user page table<br/>stvec = TRAMPOLINE + uservec

  U->>CPU: ecall / exception / interrupt

  CPU->>CPU: save trap state<br/>sepc = faulting/interrupted PC<br/>scause = cause<br/>stval = fault address if any
  CPU->>CPU: switch privilege<br/>U mode -> S mode
  CPU->>UV: set PC = stvec<br/>enter uservec

  Note over UV,CPU: Mode = S<br/>satp still = user page table

  UV->>CPU: csrw sscratch, a0<br/>save original user a0 in CSR
  UV->>UV: li a0, TRAPFRAME
  UV->>TF: save user registers except a0
  UV->>CPU: csrr t0, sscratch
  UV->>TF: save original user a0<br/>trapframe->a0 = t0

  UV->>TF: load kernel_sp
  UV->>TF: load kernel_hartid
  UV->>TF: load kernel_trap = usertrap
  UV->>TF: load kernel_satp

  UV->>CPU: sfence.vma
  UV->>CPU: write satp = kernel_satp
  UV->>CPU: sfence.vma
  UV->>UT: jump to usertrap()

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

  UT->>CPU: set stvec = kernelvec
  UT->>CPU: read scause, sepc, stval
  UT->>TF: trapframe->epc = sepc

  alt scause == 8: system call exception
    UT->>TF: trapframe->epc += 4<br/>skip ecall on return
    UT->>CPU: intr_on()
    UT->>H: syscall()

    H->>TF: read syscall number from a7
    H->>TF: read syscall arguments from a0-a5
    H->>H: validate syscall number
    H->>H: lookup syscalls[num]
    H->>H: call selected sys_* handler
    H->>TF: write return value to a0

  else interrupt recognized by devintr()
    UT->>H: devintr()
    H->>CPU: read scause

    alt supervisor external interrupt
      H->>H: plic_claim()
      H->>H: if UART0_IRQ: uartintr()
      H->>H: if VIRTIO0_IRQ: virtio_disk_intr()
      H->>H: plic_complete(irq)
      H-->>UT: return 1<br/>ordinary device interrupt handled
      UT->>UT: continue return path<br/>no yield

    else supervisor timer interrupt
      H->>H: clockintr()
      H->>H: ticks++
      H->>H: wakeup(&ticks)
      H->>CPU: set next stimecmp
      H-->>UT: return 2<br/>timer interrupt handled
      UT->>UT: yield()<br/>later resume in usertrap()

    else not recognized
      H-->>UT: return 0
    end

  else scause == 13 or 15: page fault
    UT->>CPU: read stval<br/>faulting virtual address
    UT->>H: vmfault(pagetable, stval, read/write)

    alt vmfault succeeds
      H->>H: allocate missing lazy page
      H->>H: map page into user pagetable
      H-->>UT: same instruction can retry

    else vmfault fails
      H-->>UT: fatal user page fault<br/>return path omitted
    end

  else unexpected user exception
    UT->>UT: fatal user exception<br/>return path omitted
  end

  UT->>PR: prepare_return()

  PR->>CPU: intr_off()
  PR->>CPU: set stvec = TRAMPOLINE + uservec
  PR->>TF: fill kernel_satp
  PR->>TF: fill kernel_sp
  PR->>TF: fill kernel_trap = usertrap
  PR->>TF: fill kernel_hartid
  PR->>CPU: set sstatus.SPP = user
  PR->>CPU: set sstatus.SPIE = enabled
  PR->>CPU: set sepc = trapframe->epc

  PR-->>UT: return
  UT-->>UR: return user satp in a0

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

  UR->>CPU: sfence.vma
  UR->>CPU: write satp = user page table
  UR->>CPU: sfence.vma
  UR->>UR: li a0, TRAPFRAME
  UR->>TF: restore user registers except a0
  UR->>TF: restore user a0 from trapframe
  UR->>CPU: sret

  CPU->>U: resume at sepc

  Note over U,CPU: Mode = U<br/>satp = user page table