Memory Subsystem

Overall Memory Architecture

---
config:
  layout: dagre
---
flowchart LR
    BOOT["boot / kernel VM setup<br><br>kinit<br>kvminit<br>proc_mapstacks<br>kvminithart"] -- initializes --> PMA["physical page allocator<br><br>kalloc<br>kfree<br>kmem.freelist"]
    BOOT -- builds and enables --> KVM["kernel virtual memory<br><br>kernel_pagetable<br>direct map<br>kernel stacks<br>trampoline"]
    PMA -- "supplies 4096-byte pages to" --> KVM & PROCVM["process virtual memory<br><br>p-&gt;pagetable<br>kexec<br>kfork<br>sys_sbrk / growproc<br>p-&gt;sz"]
    PROCVM -- uses --> VMAPI["VM helper functions<br><br>walk<br>mappages<br>uvmalloc<br>uvmunmap<br>copyin / copyout<br>vmfault"]
    VMAPI -- creates / edits mappings to --> PHYS["physical memory + MMIO<br><br>RAM<br>page-table pages<br>user pages<br>trapframe page<br>trampoline page<br>UART / VIRTIO / PLIC"]
    HW["RISC-V translation backend<br><br>satp<br>TLB<br>Sv39 hardware walker<br>page fault"] -- uses active page table to access --> PHYS
    KVM -- selected in kernel mode by satp --> HW
    PROCVM -- selected in user mode by satp --> HW

     BOOT:::iface
     PMA:::iface
     KVM:::source
     PROCVM:::source
     VMAPI:::iface
     HW:::backend
     PHYS:::source
    classDef process fill:#F3EFE2,stroke:#111,stroke-width:2px,color:#111
    classDef file fill:#FFFFFF,stroke:#111,stroke-width:3px,color:#111
    classDef source fill:#E9F1FF,stroke:#111,stroke-width:2px,color:#111
    classDef iface fill:#EDE7D4,stroke:#111,stroke-width:2px,color:#111
    classDef backend fill:#F8F8F8,stroke:#111,stroke-width:2px,color:#111

Physical Page Allocator

---
config:
  layout: dagre
---
flowchart LR
 subgraph INIT["allocator initialization"]
    direction TB
        KINIT["kinit<br><br>starts physical allocator setup"]
        FREERANGE["freerange<br><br>adds pages from end to PHYSTOP"]
  end
 subgraph STATE["allocator state"]
    direction TB
        KMEM["kmem<br><br>spinlock lock<br>freelist"]
        RUN["struct run<br><br>next pointer stored inside free page"]
        FREEPAGES["free 4096-byte physical pages<br><br>user pages<br>page-table pages<br>kernel stacks<br>trapframes<br>pipe buffers"]
  end
 subgraph API["allocator functions"]
    direction TB
        KFREE["kfree<br><br>returns a physical page to freelist"]
        KALLOC["kalloc<br><br>removes one physical page from freelist"]
  end
    KINIT --> FREERANGE
    FREERANGE -- calls repeatedly --> KFREE
    KFREE -- protects and updates --> KMEM
    KMEM -- freelist nodes are --> RUN
    RUN -- each node represents one --> FREEPAGES
    KALLOC -- protects and updates --> KMEM
    KMEM -- hands out --> KALLOC
    KALLOC -- returns page to --> USERS["main users of kalloc<br><br>kernel page table<br>user memory<br>page-table pages<br>kernel stacks<br>trapframes<br>pipe buffers"]

     KINIT:::iface
     FREERANGE:::iface
     KMEM:::source
     RUN:::file
     FREEPAGES:::source
     KFREE:::iface
     KALLOC:::iface
     USERS:::process
    classDef process fill:#F3EFE2,stroke:#111,stroke-width:2px,color:#111
    classDef file fill:#FFFFFF,stroke:#111,stroke-width:3px,color:#111
    classDef source fill:#E9F1FF,stroke:#111,stroke-width:2px,color:#111
    classDef iface fill:#EDE7D4,stroke:#111,stroke-width:2px,color:#111
    classDef backend fill:#F8F8F8,stroke:#111,stroke-width:2px,color:#111

Kernel Virtual Memory

---
config:
  layout: dagre
---
flowchart LR

  classDef process fill:#F3EFE2,stroke:#111,stroke-width:2px,color:#111
  classDef file fill:#FFFFFF,stroke:#111,stroke-width:3px,color:#111
  classDef source fill:#E9F1FF,stroke:#111,stroke-width:2px,color:#111
  classDef iface fill:#EDE7D4,stroke:#111,stroke-width:2px,color:#111
  classDef backend fill:#F8F8F8,stroke:#111,stroke-width:2px,color:#111

  subgraph BUILD["kernel VM construction: software builds mappings"]
    direction TB
    KVMINIT["kvminit<br/><br/>starts kernel VM setup"]:::iface
    KVMMAKE["kvmmake<br/><br/>allocates root page table<br/>creates kernel mappings"]:::iface
    KVMMAP["kvmmap<br/><br/>kernel wrapper for mapping ranges"]:::iface
    MAPPAGES["mappages<br/><br/>install VA to PA mappings"]:::iface
    WALK["walk<br/><br/>software page-table walk<br/>finds or creates lower-level tables"]:::iface
    MAPSTACKS["proc_mapstacks<br/><br/>maps per-process kernel stacks<br/>leaves guard pages invalid"]:::iface
    KVMHART["kvminithart<br/><br/>sfence.vma<br/>w_satp(MAKE_SATP(kernel_pagetable))<br/>sfence.vma"]:::iface
  end

  subgraph KSTATE["kernel page-table state"]
    direction TB
    KPAGETABLE["kernel_pagetable<br/><br/>shared kernel address space"]:::source
    KPTPAGE["kernel page-table pages<br/><br/>4096-byte pages<br/>512 PTEs each"]:::file
    KPTE["kernel PTEs<br/><br/>PTE_V<br/>PTE_R / PTE_W / PTE_X<br/>normally no PTE_U"]:::file
  end

  subgraph VAMAP["virtual to physical address map"]
    direction TB

    subgraph DIRECT_PAIR["direct map"]
      direction LR
      DIRECTMAP["direct map region<br/><br/>kernel VA = physical address<br/>RAM + device MMIO<br/>permissions split by region"]:::source
      RAM["physical RAM<br/><br/>KERNBASE = 0x80000000<br/>PHYSTOP = KERNBASE + 128MB"]:::source
      MMIO["memory-mapped device registers<br/><br/>UART0<br/>VIRTIO0<br/>PLIC"]:::backend
    end

    subgraph TEXT_PAIR["kernel text map"]
      direction LR
      KTEXT["kernel text<br/><br/>readable + executable<br/>PTE_R | PTE_X"]:::source
      KERNELIMG["kernel image in RAM<br/><br/>entry.S<br/>kernel text<br/>kernel data<br/>end symbol"]:::source
    end

    subgraph DATA_PAIR["kernel data map"]
      direction LR
      KDATA["kernel data + usable RAM<br/><br/>readable + writable<br/>PTE_R | PTE_W"]:::source
      RAMDATA["physical RAM<br/><br/>usable RAM region"]:::source
    end

    subgraph STACK_PAIR["kernel stack map"]
      direction LR
      KSTACKS["kernel stacks<br/><br/>one stack per process<br/>mapped high<br/>invalid guard page below"]:::source
      STACKPAGES["kernel stack physical pages<br/><br/>allocated by kalloc"]:::source
    end

    subgraph TRAMP_PAIR["trampoline map"]
      direction LR
      KTRAMP["TRAMPOLINE<br/><br/>trap entry / return code<br/>same VA in kernel and user page tables"]:::source
      TRAMPPAGE["trampoline physical page<br/><br/>trampoline.S code"]:::file
    end

    PGTBLPAGES["kernel page-table physical pages<br/><br/>allocated by kalloc"]:::source
  end

  subgraph ALLOC["physical page allocator functions"]
    direction TB
    KALLOC["kalloc / kfree<br/><br/>allocate or release 4096-byte physical pages"]:::iface
  end

  subgraph RUNTIME["kernel-mode runtime: hardware uses mappings"]
    direction TB
    KERNELCODE["kernel code<br/><br/>load / store / fetch<br/>using kernel virtual addresses"]:::process
    CPU["RISC-V CPU"]:::process
    SATP["satp CSR<br/><br/>active root page table"]:::source
    TLB["TLB<br/><br/>cached VA to PA translations"]:::backend
    HWALKER["Sv39 hardware page-table walker<br/><br/>walks kernel_pagetable on TLB miss"]:::backend
    ACCESS["physical memory / MMIO access"]:::source
    FAULT["kernel page fault<br/><br/>invalid mapping or bad permission"]:::backend
  end

  KVMINIT --> KVMMAKE
  KVMMAKE -->|"allocates root using"| KALLOC
  KVMMAKE --> KVMMAP
  KVMMAKE --> MAPSTACKS
  KVMMAP --> MAPPAGES
  MAPPAGES --> WALK
  WALK -->|"writes / finds PTEs in"| KPAGETABLE
  WALK -->|"may allocate lower-level tables via"| KALLOC
  KALLOC -->|"returns"| PGTBLPAGES

  MAPSTACKS -->|"allocates stack pages via"| KALLOC
  MAPSTACKS -->|"uses"| KVMMAP
  KVMHART -->|"loads root into"| SATP
  SATP -->|"selects"| KPAGETABLE

  KPAGETABLE -->|"contains"| KPTPAGE
  KPTPAGE -->|"contains"| KPTE

  KPAGETABLE -->|"describes"| DIRECTMAP
  KPAGETABLE -->|"describes"| KTEXT
  KPAGETABLE -->|"describes"| KDATA
  KPAGETABLE -->|"describes"| KSTACKS
  KPAGETABLE -->|"describes"| KTRAMP

  DIRECTMAP -->|"maps RAM"| RAM
  DIRECTMAP -->|"maps MMIO"| MMIO
  KTEXT -->|"maps"| KERNELIMG
  KDATA -->|"maps"| RAMDATA
  KSTACKS -->|"maps"| STACKPAGES
  KTRAMP -->|"maps"| TRAMPPAGE

  KERNELCODE --> CPU
  CPU -->|"uses"| SATP
  CPU -->|"checks first"| TLB
  TLB -->|"on miss"| HWALKER
  HWALKER -->|"reads"| KPTPAGE
  HWALKER -->|"valid PTE"| ACCESS
  HWALKER -->|"invalid / bad permission"| FAULT

Process Virtual Memory Construction

---
config:
  layout: dagre
---
flowchart LR

  classDef file fill:#FFFFFF,stroke:#111,stroke-width:3px,color:#111
  classDef source fill:#E9F1FF,stroke:#111,stroke-width:2px,color:#111
  classDef iface fill:#EDE7D4,stroke:#111,stroke-width:2px,color:#111

  subgraph CREATE["process VM creation / initialization entry points"]
    direction TB
    PROCPGTBL["proc_pagetable<br/><br/>creates per-process user page table"]:::iface
    EXEC["kexec<br/><br/>builds fresh program image<br/>from ELF file"]:::iface
    FORK["kfork<br/><br/>creates child process<br/>copies parent address space"]:::iface
  end

  subgraph VMAPI["page-table construction"]
    direction TB
    UVMCREATE["uvmcreate<br/><br/>create empty user page table"]:::iface
    UVMALLOC["uvmalloc<br/><br/>allocate and map user memory"]:::iface
    UVMCOPY["uvmcopy<br/><br/>copy mapped parent pages<br/>skip absent lazy holes"]:::iface
    LOADSEG["loadseg<br/><br/>load ELF segment bytes<br/>into allocated physical pages"]:::iface
    FLAGS2PERM["flags2perm<br/><br/>convert ELF flags<br/>into extra PTE_X / PTE_W permissions"]:::iface
    UVMCLEAR["uvmclear<br/><br/>clear PTE_U<br/>creates inaccessible guard page"]:::iface
    MAPPAGES["mappages<br/><br/>install VA to PA mappings"]:::iface
    WALK["walk<br/><br/>software page-table walk<br/>finds or creates PTE location"]:::iface
  end

  subgraph USTATE["new process VM state"]
    direction TB
    UPAGETABLE["p->pagetable<br/><br/>per-process user address space"]:::source
    PROCSZ["p->sz<br/><br/>declared user memory size<br/>initialized by exec<br/>copied by fork<br/>later changed by sbrk"]:::source
    UPTPAGE["user page-table pages<br/><br/>root / intermediate / leaf levels"]:::file
    UPTE["user PTEs<br/><br/>PTE_V<br/>PTE_R / PTE_W / PTE_X<br/>PTE_U when user-accessible"]:::file
  end

  subgraph MAPBOX["virtual to physical address map"]
    direction TB
    VAMAP["initial user VA to PA map<br/><br/>text, data, bss, stack, guard page,<br/>TRAPFRAME, and TRAMPOLINE"]:::source
  end

  subgraph ALLOC["physical page allocator functions"]
    direction TB
    KALLOC["kalloc / kfree<br/><br/>allocate or release 4096-byte physical pages"]:::iface
  end

  PROCPGTBL -->|"uses"| UVMCREATE
  UVMCREATE -->|"allocates root page-table page via"| KALLOC
  UVMCREATE -->|"creates"| UPAGETABLE

  PROCPGTBL -->|"describes TRAMPOLINE mapping to trampoline.S page"| VAMAP
  PROCPGTBL -->|"describes TRAPFRAME mapping to p->trapframe page"| VAMAP
  PROCPGTBL -->|"installs special mappings using"| MAPPAGES

  EXEC -->|"creates fresh page table with"| PROCPGTBL
  EXEC -->|"allocates text, data, bss, and stack pages with"| UVMALLOC
  EXEC -->|"loads ELF bytes into mapped physical pages with"| LOADSEG
  EXEC -->|"sets text/data permissions with"| FLAGS2PERM
  EXEC -->|"describes initial text, data, bss, and stack mappings"| VAMAP
  EXEC -->|"sets final process size"| PROCSZ
  EXEC -->|"creates stack guard page with"| UVMCLEAR

  FORK -->|"creates child page table with"| PROCPGTBL
  FORK -->|"copies parent mapped pages through"| UVMCOPY
  FORK -->|"copies parent logical size into child"| PROCSZ
  UVMCOPY -->|"allocates child physical pages via"| KALLOC
  UVMCOPY -->|"copies parent text, data, heap, and stack mappings into child"| VAMAP
  UVMCOPY -->|"leaves absent lazy heap holes absent; p->sz keeps them logically valid"| PROCSZ
  UVMCOPY -->|"maps copied pages with"| MAPPAGES

  UVMALLOC -->|"gets physical pages from"| KALLOC
  UVMALLOC -->|"adds text, data, bss, or stack mappings to"| VAMAP
  UVMALLOC -->|"maps allocated pages with"| MAPPAGES

  LOADSEG -->|"fills physical pages behind text and data mappings"| VAMAP
  FLAGS2PERM -->|"adds ELF-derived PTE_X / PTE_W bits; uvmalloc adds PTE_R and PTE_U"| VAMAP
  UVMCLEAR -->|"describes stack guard page by clearing PTE_U"| VAMAP

  MAPPAGES -->|"uses"| WALK
  WALK -->|"writes or finds PTEs in"| UPAGETABLE
  WALK -->|"may allocate page-table pages via"| KALLOC
  WALK -->|"creates PTE path for this VA-to-PA map"| VAMAP

  UPAGETABLE -->|"contains"| UPTPAGE
  UPTPAGE -->|"contains"| UPTE
  UPTE -->|"leaf PTEs describe user VA to physical page mappings"| VAMAP
  UPTE -->|"non-leaf PTEs point to lower-level page-table pages"| UPTPAGE

  UPAGETABLE -->|"describes complete initial user virtual-to-physical map"| VAMAP
  KALLOC -->|"allocates physical backing pages for mapped user pages"| VAMAP
  KALLOC -->|"allocates user page-table pages"| UPTPAGE

User Virtual Address Translation

---
config:
  layout: dagre
---
flowchart LR

  classDef process fill:#F3EFE2,stroke:#111,stroke-width:2px,color:#111
  classDef file fill:#FFFFFF,stroke:#111,stroke-width:3px,color:#111
  classDef source fill:#E9F1FF,stroke:#111,stroke-width:2px,color:#111
  classDef iface fill:#EDE7D4,stroke:#111,stroke-width:2px,color:#111
  classDef backend fill:#F8F8F8,stroke:#111,stroke-width:2px,color:#111

  subgraph USTATE["running user VM state"]
    direction TB
    UPAGETABLE["p->pagetable<br/><br/>active user address space"]:::source
    PROCSZ["p->sz<br/><br/>declared valid user memory size<br/>used by vmfault"]:::source
    UPTPAGE["user page-table pages<br/><br/>root / intermediate / leaf levels"]:::file
    UPTE["user PTEs<br/><br/>PTE_V<br/>PTE_R / PTE_W / PTE_X<br/>PTE_U"]:::file
  end

  subgraph MAPBOX["virtual to physical address map"]
    direction TB
    VAMAP["runtime user VA to PA map<br/><br/>text, data, heap, stack,<br/>valid mappings, and lazy holes"]:::source
  end

  subgraph HWPATH["normal user-mode hardware translation"]
    direction TB
    USERACCESS["user instruction<br/><br/>load / store / fetch<br/>uses virtual address"]:::process
    CPU["RISC-V CPU"]:::process
    SATPUSER["satp CSR<br/><br/>points to p->pagetable<br/>while running user code"]:::source
    TLB["TLB<br/><br/>cached user VA to PA translations"]:::backend
    HWALKER["Sv39 hardware page-table walker<br/><br/>walks p->pagetable<br/>on TLB miss"]:::backend
    PTECHECK["PTE check<br/><br/>valid bit<br/>permission bits<br/>user-access bit"]:::backend
    ACCESSOK["physical page access<br/><br/>read / write / execute succeeds"]:::source
    PAGEFAULT["user page fault<br/><br/>invalid PTE<br/>bad permission<br/>missing lazy page"]:::backend
  end

  subgraph FAULTPATH["fault resolution / lazy allocation path"]
    direction TB
    USERTRAP["usertrap<br/><br/>handles user-mode exception<br/>load/store fault scause 13 or 15"]:::process
    VMFAULT["vmfault<br/><br/>accepts VA below p->sz<br/>rounds down<br/>requires currently unmapped page"]:::iface
    KALLOC["kalloc<br/><br/>allocate one 4096-byte physical page"]:::iface
    ZERO["zero-filled physical page<br/><br/>new backing page for lazy VA"]:::source
    MAPPAGES["mappages<br/><br/>install new VA to PA mapping"]:::iface
    WALK["walk<br/><br/>software page-table walk<br/>finds or creates PTE location"]:::iface
    RESUME["resume user process<br/><br/>faulting instruction can be retried"]:::process
    VMFAULTFAIL["vmfault returns 0<br/><br/>VA >= p->sz<br/>already mapped page<br/>allocation or mapping failure"]:::backend
    KILL["kill process<br/><br/>usertrap handles failed user fault"]:::process
  end

  subgraph COPYPATH["kernel access to user buffers"]
    direction TB
    SYSCALL["system call path<br/><br/>user passes pointer to kernel"]:::process
    COPY["copyin / copyout<br/><br/>safe kernel-user copy helpers<br/>may allocate lazy pages"]:::iface
    COPYINSTR["copyinstr<br/><br/>copy null-terminated user string<br/>does not allocate lazy pages"]:::iface
    WALKADDR["walkaddr<br/><br/>copyin / copyout translation<br/>requires valid PTE and PTE_U"]:::iface
    WALKADDRSTR["walkaddr<br/><br/>copyinstr translation<br/>requires valid PTE and PTE_U"]:::iface
    WRITECHK["copyout write check<br/><br/>requires PTE_W<br/>rejects read-only user text"]:::backend
    MEMMOVE["copy bytes after VA becomes PA<br/><br/>copyin / copyout use memmove<br/>copyinstr uses byte loop"]:::process
    COPYFAIL["copy helper returns -1<br/><br/>missing page<br/>copyout non-writable PTE<br/>invalid address"]:::backend
  end

  HEAPPOLICY["lazy heap holes are created by sbrk policy<br/><br/>detailed in heap allocation and growth"]:::iface

  UPAGETABLE -->|"contains"| UPTPAGE
  UPTPAGE -->|"contains"| UPTE
  UPTE -->|"leaf PTEs describe valid text, data, heap, and stack mappings"| VAMAP
  UPTE -->|"missing or invalid leaf PTEs mark lazy holes that may fault"| VAMAP
  UPAGETABLE -->|"describes complete runtime user VA-to-PA map"| VAMAP
  HEAPPOLICY -->|"creates logical heap range with missing PTEs"| VAMAP
  HEAPPOLICY -->|"updates valid boundary"| PROCSZ

  USERACCESS --> CPU
  CPU -->|"uses active root from"| SATPUSER
  SATPUSER -->|"selects"| UPAGETABLE
  CPU -->|"checks cached translation in"| TLB
  TLB -->|"cache hit gives PA from"| VAMAP
  TLB -->|"on TLB miss"| HWALKER
  HWALKER -->|"reads page-table pages from"| UPTPAGE
  HWALKER --> PTECHECK
  PTECHECK -->|"valid and permitted PTE translates VA through"| VAMAP
  PTECHECK -->|"access allowed"| ACCESSOK
  ACCESSOK -->|"accesses physical backing described by"| VAMAP
  PTECHECK -->|"invalid PTE or bad permission"| PAGEFAULT

  PAGEFAULT --> USERTRAP
  USERTRAP -->|"load/store fault calls"| VMFAULT
  VMFAULT -->|"checks faulting VA against"| PROCSZ
  VMFAULT -->|"VA below p->sz and unmapped"| KALLOC
  VMFAULT -->|"VA >= p->sz or already mapped"| VMFAULTFAIL
  KALLOC -->|"allocation fails"| VMFAULTFAIL
  KALLOC -->|"allocation succeeds"| ZERO
  ZERO -->|"new physical backing page for"| VAMAP
  ZERO -->|"mapped by"| MAPPAGES
  MAPPAGES -->|"uses"| WALK
  WALK -->|"writes or finds PTE in"| UPAGETABLE
  WALK -->|"updates user PTEs"| UPTE
  MAPPAGES -->|"installs new valid VA-to-PA mapping into"| VAMAP
  MAPPAGES -->|"mapping failure"| VMFAULTFAIL
  MAPPAGES -->|"usertrap path"| RESUME
  MAPPAGES -->|"vmfault returns new PA to copyin path"| MEMMOVE
  MAPPAGES -->|"vmfault returns new PA before copyout PTE check"| WRITECHK
  VMFAULTFAIL -->|"usertrap path"| KILL
  VMFAULTFAIL -->|"copyin / copyout path"| COPYFAIL

  SYSCALL --> COPY
  SYSCALL --> COPYINSTR
  COPY -->|"translates user VA using"| WALKADDR
  COPYINSTR -->|"translates user VA using"| WALKADDRSTR
  WALKADDR -->|"walks"| UPAGETABLE
  WALKADDR -->|"reads"| UPTE
  WALKADDR -->|"valid mapping returns physical address from"| VAMAP
  WALKADDR -->|"missing page returns 0; copyin / copyout then call"| VMFAULT
  WALKADDRSTR -->|"walks"| UPAGETABLE
  WALKADDRSTR -->|"reads"| UPTE
  WALKADDRSTR -->|"valid mapping returns physical address from"| VAMAP
  WALKADDRSTR -->|"missing page makes copyinstr fail"| COPYFAIL
  COPY -->|"copyout verifies destination PTE"| WRITECHK
  WRITECHK -->|"writable mapping"| MEMMOVE
  WRITECHK -->|"not writable"| COPYFAIL
  COPY -->|"copyin copies user bytes through"| MEMMOVE
  COPYINSTR -->|"valid mapped string page copies through"| MEMMOVE
  MEMMOVE -->|"reads or writes physical backing described by"| VAMAP

Heap Allocation and Growth

flowchart LR

  classDef process fill:#F3EFE2,stroke:#111,stroke-width:2px,color:#111
  classDef file fill:#FFFFFF,stroke:#111,stroke-width:3px,color:#111
  classDef source fill:#E9F1FF,stroke:#111,stroke-width:2px,color:#111
  classDef iface fill:#EDE7D4,stroke:#111,stroke-width:2px,color:#111
  classDef backend fill:#F8F8F8,stroke:#111,stroke-width:2px,color:#111

  subgraph HEAPSTATE["heap-related process VM state"]
    direction TB
    UPAGETABLE["p->pagetable<br/><br/>user page table edited by heap operations"]:::source
    PROCSZ["p->sz<br/><br/>declared user memory size<br/>heap validity boundary"]:::source
    UPTE["heap mapping state<br/><br/>valid PTE = mapped heap page<br/>missing or invalid PTE = lazy heap hole"]:::file
  end

  subgraph MAPBOX["virtual to physical address map"]
    direction TB
    VAMAP["heap VA-to-PA map<br/><br/>old heap mappings,<br/>new eager mappings,<br/>lazy unmapped heap range,<br/>and removed mappings after shrink"]:::source
  end

  subgraph SBRKPATH["heap growth / shrink request"]
    direction TB
    SBRK["sys_sbrk<br/><br/>user asks to grow or shrink heap"]:::iface
    GROWPROC["growproc<br/><br/>handles eager growth<br/>and all shrinking"]:::iface
    EAGER["eager positive growth<br/><br/>allocate and map pages immediately"]:::iface
    LAZY["lazy non-negative growth<br/><br/>sys_sbrk increases p->sz only<br/>do not allocate pages yet"]:::iface
    SHRINK["negative growth<br/><br/>remove mappings above new size"]:::iface
    NOCHANGE["zero eager change<br/><br/>growproc leaves p->sz unchanged"]:::iface
  end

  subgraph SWMAP["software mapping / unmapping helpers"]
    direction TB
    UVMALLOC["uvmalloc<br/><br/>allocate and map heap pages"]:::iface
    UVMDEALLOC["uvmdealloc<br/><br/>reduce user memory range"]:::iface
    UVMUNMAP["uvmunmap<br/><br/>remove heap mappings<br/>optionally free physical pages"]:::iface
    MAPPAGES["mappages<br/><br/>install heap VA to PA mapping"]:::iface
    WALK["walk<br/><br/>software page-table walk<br/>finds or creates PTE location"]:::iface
  end

  subgraph ALLOC["physical page allocator functions"]
    direction TB
    KALLOC["kalloc<br/><br/>allocate 4096-byte physical page"]:::iface
    KFREE["kfree<br/><br/>return physical page to allocator"]:::iface
    HEAPPAGE["heap physical page<br/><br/>backing page for eager heap mapping"]:::source
    PGTBLPAGE["page-table physical page<br/><br/>allocated if walk needs lower-level table"]:::source
  end

  FAULTREF["first access to lazy heap hole<br/><br/>fault resolution continues in user VA translation"]:::backend

  UPAGETABLE -->|"contains heap-related PTEs"| UPTE
  UPTE -->|"describes mapped heap pages; missing or invalid PTEs mark lazy holes"| VAMAP

  SBRK -->|"t == SBRK_EAGER or n < 0"| GROWPROC
  SBRK -->|"t != SBRK_EAGER and n >= 0"| LAZY
  GROWPROC -->|"n > 0"| EAGER
  GROWPROC -->|"n < 0"| SHRINK
  GROWPROC -->|"n == 0"| NOCHANGE

  EAGER --> UVMALLOC
  UVMALLOC -->|"requests physical heap pages from"| KALLOC
  KALLOC -->|"returns"| HEAPPAGE
  UVMALLOC -->|"maps new heap pages with"| MAPPAGES
  MAPPAGES -->|"uses"| WALK
  WALK -->|"writes or finds heap PTEs in"| UPAGETABLE
  WALK -->|"updates"| UPTE
  WALK -->|"may allocate lower-level page table via"| KALLOC
  KALLOC -->|"may return"| PGTBLPAGE
  MAPPAGES -->|"adds immediate heap VA-to-PA mapping into"| VAMAP
  HEAPPAGE -->|"becomes physical backing for eager heap VA"| VAMAP

  LAZY -->|"after overflow and TRAPFRAME checks, adjusts"| PROCSZ
  LAZY -->|"adds logical heap VA range without physical backing"| VAMAP
  LAZY -->|"leaves PTE invalid or absent"| UPTE
  VAMAP -->|"later user load/store to lazy hole causes"| FAULTREF

  SHRINK --> UVMDEALLOC
  UVMDEALLOC -->|"uses"| UVMUNMAP
  UVMUNMAP -->|"uses"| WALK
  WALK -->|"finds existing heap PTEs in"| UPAGETABLE
  UVMUNMAP -->|"removes heap VA-to-PA mappings from"| VAMAP
  UVMUNMAP -->|"clears valid heap PTEs; skips missing or invalid PTEs"| UPTE
  UVMUNMAP -->|"frees mapped heap pages through"| KFREE
  KFREE -->|"receives old heap physical pages from"| VAMAP
  SHRINK -->|"sets p->sz to uvmdealloc result"| PROCSZ

Process VM Cleanup

---
config:
  layout: dagre
---
flowchart LR

  classDef process fill:#F3EFE2,stroke:#111,stroke-width:2px,color:#111
  classDef file fill:#FFFFFF,stroke:#111,stroke-width:3px,color:#111
  classDef source fill:#E9F1FF,stroke:#111,stroke-width:2px,color:#111
  classDef iface fill:#EDE7D4,stroke:#111,stroke-width:2px,color:#111
  classDef backend fill:#F8F8F8,stroke:#111,stroke-width:2px,color:#111

  subgraph EXITENTRY["process VM cleanup entry"]
    direction TB
    EXIT["exec replaces old image<br/>or freeproc destroys process"]:::process
    FREEPROC["freeproc<br/><br/>process destruction only<br/>frees p-&gt;trapframe separately"]:::iface
    FREEPGTBL["proc_freepagetable<br/><br/>unmap special mappings<br/>free user memory<br/>free page-table pages"]:::iface
  end

  subgraph OLDSTATE["old process VM state"]
    direction TB
    UPAGETABLE["old p->pagetable<br/><br/>address space being destroyed"]:::source
    PROCSZ["old p->sz<br/><br/>size used to know user memory range"]:::source
    UPTPAGE["old user page-table pages<br/><br/>root / intermediate / leaf levels"]:::file
    UPTE["old user PTEs<br/><br/>leaf PTEs map physical pages<br/>non-leaf PTEs point to lower tables"]:::file
  end

  subgraph MAPBOX["virtual to physical address map"]
    direction TB
    VAMAP["old user VA to PA map<br/><br/>text, data, heap, stack,<br/>TRAPFRAME, TRAMPOLINE,<br/>and mapped physical pages"]:::source
  end

  subgraph FREECHAIN["software freeing chain"]
    direction TB
    UVMFREE["uvmfree<br/><br/>free user memory<br/>then free page-table pages"]:::iface
    UVMUNMAP["uvmunmap<br/><br/>remove leaf mappings<br/>optionally free physical pages"]:::iface
    FREEWALK["freewalk<br/><br/>recursively free page-table pages<br/>after leaf mappings are gone"]:::iface
    WALK["walk<br/><br/>find PTEs during unmapping"]:::iface
  end

  subgraph SPECIAL["special mapping cleanup"]
    direction TB
    UNMAPTRAMP["unmap TRAMPOLINE<br/><br/>remove special mapping<br/>without freeing shared trampoline code"]:::iface
    UNMAPTRAPFRAME["unmap TRAPFRAME<br/><br/>remove special mapping only<br/>do not free trapframe page here"]:::iface
  end

  subgraph ALLOC["physical page allocator functions"]
    direction TB
    KFREE["kfree<br/><br/>return 4096-byte physical page<br/>to allocator freelist"]:::iface
    USERPAGES["old user physical pages<br/><br/>text / data / heap / stack"]:::source
    PGTBLPAGES["old page-table physical pages<br/><br/>root / intermediate / leaf table pages"]:::source
    TRAPFRAMEPAGE["old trapframe physical page<br/><br/>per-process page<br/>freed by freeproc, not exec cleanup"]:::file
    TRAMPPAGE["trampoline physical page<br/><br/>shared trampoline.S code<br/>not freed here"]:::file
  end

  EXIT -->|"process destruction only"| FREEPROC
  EXIT -->|"old address-space cleanup"| FREEPGTBL
  FREEPROC -->|"then calls proc_freepagetable if p->pagetable exists"| FREEPGTBL
  FREEPGTBL -->|"uses old size"| PROCSZ
  FREEPGTBL -->|"starts cleanup of"| UPAGETABLE

  UPAGETABLE -->|"contains"| UPTPAGE
  UPTPAGE -->|"contains"| UPTE
  UPTE -->|"leaf PTEs describe old user VA-to-PA mappings"| VAMAP
  UPAGETABLE -->|"describes old complete map"| VAMAP

  FREEPGTBL -->|"unmaps special trampoline mapping"| UNMAPTRAMP
  FREEPGTBL -->|"unmaps trapframe mapping without freeing page"| UNMAPTRAPFRAME
  FREEPGTBL --> UVMFREE

  UVMFREE -->|"free mapped user memory first"| UVMUNMAP
  UVMUNMAP -->|"uses"| WALK
  WALK -->|"finds leaf PTEs in"| UPAGETABLE
  UVMUNMAP -->|"removes text/data/heap/stack mappings from"| VAMAP
  UVMUNMAP -->|"returns mapped physical pages through"| KFREE
  KFREE -->|"receives"| USERPAGES

  FREEPROC -->|"returns p->trapframe page through"| KFREE
  KFREE -->|"receives"| TRAPFRAMEPAGE
  UNMAPTRAPFRAME -->|"removes mapping to per-process trapframe page"| VAMAP

  UNMAPTRAMP -->|"removes mapping to shared trampoline page"| VAMAP
  UNMAPTRAMP -->|"does not free shared page"| TRAMPPAGE

  UVMFREE -->|"after leaf mappings are gone"| FREEWALK
  FREEWALK -->|"recursively releases page-table pages from"| UPTPAGE
  FREEWALK -->|"returns page-table pages through"| KFREE
  KFREE -->|"receives"| PGTBLPAGES