consoleinit
Sets up the console so the kernel can print and processes can read input.
cons.lockspinlock initialized;consholds the circular input buffer and its lockuartinitprograms the 16550a chip, memory-mapped at0x10000000:- interrupts disabled first so nothing fires mid-setup
- baud rate set to 38400
- word length set to 8 bits, no parity
- FIFOs reset and enabled
- transmit and receive interrupts enabled
tx_lockspinlock initializedtx_lock,tx_busy,tx_chanused to synchronize interrupt-driven transmit
consolereadandconsolewriteregistered intodevsw[CONSOLE]
consoleread requires consoleinit, uartinit, procinit, kvminithart, and a current process
- Console device
readhandler. - Uses
acquire(&cons.lock)andrelease(&cons.lock)to protect console input state. - Waits for
consoleintrto commit input intocons.bufby advancingcons.w. cons.lockprotects the shared input indexesr,w, ande.- If no input is ready, calls
killed(myproc())before sleeping. myproc()comes from the process subsystem; internally it disables interrupts, usesmycpu()to find this hart’sstruct cpu, readsc->proc, and restores the interrupt state.killed(p)checks whether the current process has been marked for termination.- If the process is killed while waiting,
consolereadreleasescons.lockand returns-1. - If no input is ready and the process is still alive,
sleep(&cons.r, &cons.lock)blocks the reader and atomically releasescons.lockwhile sleeping. consoleintrlater callswakeup(&cons.r)after newline, Ctrl-D, or a full buffer, making sleeping readers runnable again.- Copies bytes to the caller with
either_copyout(user_dst, dst, &cbuf, 1). either_copyoutcomes from the process-address-space boundary code; it copies to either a user virtual address or a kernel address depending onuser_dst.- Newline ends the read because console input is line-oriented.
- Ctrl-D acts as EOF.
- A killed process returns
-1while waiting.
consolewrite requires consoleinit, uartinit, kvminithart; user-buffer writes also require a current process page table
- Console device
writehandler. - Copies output from the caller with
either_copyin(buf, user_src, src + i, nn). either_copyincomes from the process/address-space boundary code; it copies from either a user virtual address or a kernel address depending onuser_src.- Uses a 32-byte kernel buffer for batching.
- Sends each batch to
uartwrite(buf, nn). uartwritecomes from the UART driver and may sleep while waiting for transmit-buffer space and UART transmit interrupts.- Returns the number of bytes successfully handed to the UART path.
- Does not interpret characters because output has no line editing, EOF handling, or kill checks.
consputc [requires uartinit]
- Low-level console character output helper.
- Sends one character to the UART with
uartputc_sync. - Uses the synchronous UART path, so it does not sleep and does not depend on UART transmit interrupts.
- Safe for interrupt-time use, which matters because
consoleintruses it to echo typed characters and erase backspaces. - Used by kernel printing through
printfandconsputc, and by console input editing throughconsoleintr. - If the character is
BACKSPACE, sends\b, space, and\bto visually erase the previous character on the terminal. - Otherwise sends the character directly to
uartputc_sync(c).
consoleintr [requires consoleinit, uartinit, procinit, trapinithart, and plicinithart]
- Console input interrupt-side handler.
- Called by
uartintronce for each input character received from the UART. - Uses
acquire(&cons.lock)andrelease(&cons.lock)to protect the console input buffer and its indexes. - Handles Ctrl-P by calling
procdump(), which comes from the process subsystem and prints the current process table. - Handles Ctrl-U by deleting characters back to the previous newline, decrementing
cons.e, and callingconsputc(BACKSPACE)for each erased character. - Handles Ctrl-H and Delete by erasing one pending input character if
cons.e != cons.w. - Converts carriage return
\rinto newline\nso the console treats Enter consistently. - Echoes ordinary accepted input back to the terminal with
consputc(c). - Stores accepted input in the circular buffer at
cons.buf[cons.e++ % INPUT_BUF_SIZE]. - Commits input by setting
cons.w = cons.ewhen a newline, Ctrl-D, or full input buffer is reached. - Calls
wakeup(&cons.r)after committing input, which wakes processes sleeping inconsoleread. - Ignores zero characters and drops input when the circular buffer already holds
INPUT_BUF_SIZEunconsumed bytes.
UART Device Driver
--- config: layout: dagre --- flowchart LR subgraph P["caller side"] direction TB USER_IO["user process<br><br>read(fd)<br>write(fd)"] KERNEL_OUT["kernel output<br><br>printf<br>console echo"] end subgraph C["console boundary"] direction TB DEVSW["devsw[CONSOLE]<br><br>read = consoleread<br>write = consolewrite"] CONSOLE["console.c<br><br>line buffering<br>consoleintr(c)<br>consoleread / consolewrite"] end subgraph U["UART driver interface"] direction TB UARTINIT["uartinit()<br><br>configure 16550a UART<br>enable RX/TX interrupts"] UARTOUT["UART output path<br><br>uartwrite<br>uartputc_sync"] UARTINTR["uartintr()<br><br>handle RX-ready<br>handle TX-ready"] end subgraph S["UART driver state"] direction TB TXSTATE["TX state<br><br>tx_lock<br>tx_busy<br>tx_chan"] end subgraph H["UART hardware source"] direction TB UART_REGS["16550a UART registers<br><br>RHR / THR<br>IER / ISR<br>LSR"] UART_IRQ["UART0_IRQ<br><br>external interrupt"] end subgraph I["interrupt delivery"] direction TB PLIC_PATH["PLIC + devintr()<br><br>claim UART IRQ<br>call uartintr<br>complete IRQ"] end USER_IO -- console fd read/write --> DEVSW DEVSW -- dispatches to --> CONSOLE CONSOLE -- consolewrite sends bytes --> UARTOUT KERNEL_OUT -- direct/synchronous output --> UARTOUT UARTINIT -- configures --> UART_REGS UARTOUT -- writes THR / checks LSR --> UART_REGS UARTOUT -- uses --> TXSTATE UART_REGS -- input ready or TX ready --> UART_IRQ UART_IRQ -- external interrupt --> PLIC_PATH PLIC_PATH -- calls --> UARTINTR UARTINTR -- reads/writes UART registers --> UART_REGS UARTINTR -- updates TX completion --> TXSTATE UARTINTR -- passes input chars upward --> CONSOLE USER_IO:::process KERNEL_OUT:::process DEVSW:::iface CONSOLE:::iface UARTINIT:::iface UARTOUT:::iface UARTINTR:::iface TXSTATE:::source UART_REGS:::source UART_IRQ:::source PLIC_PATH:::backend classDef process fill:#F3EFE2,stroke:#111,stroke-width:2px,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
Virtio Disk Driver
--- config: layout: dagre --- flowchart LR subgraph C["caller side"] direction TB FS["filesystem / log layer<br><br>readi / writei<br>log_write / install_trans"] BCACHE["buffer cache<br><br>bread<br>bwrite"] BUF["struct buf<br><br>dev<br>blockno<br>valid<br>disk<br>data[BSIZE]"] end subgraph VDI["virtio disk interface"] direction TB VINIT["virtio_disk_init()<br><br>verify device<br>negotiate features<br>initialize queue 0"] VRW["virtio_disk_rw(b, write)<br><br>build disk request<br>submit to virtqueue<br>sleep until done"] VINTR["virtio_disk_intr()<br><br>ack interrupt<br>process used ring<br>wake completed buffers"] end subgraph VDS["virtio disk state"] direction TB DISK["struct disk<br><br>vdisk_lock<br>free[]<br>used_idx<br>info[]<br>ops[]"] VQ["virtqueue<br><br>desc[]<br>avail ring<br>used ring"] end subgraph HW["virtio disk hardware source"] direction TB MMIO["virtio MMIO registers<br><br>STATUS<br>QUEUE_NOTIFY<br>INTERRUPT_STATUS<br>INTERRUPT_ACK"] DEVICE["qemu virtio-blk device<br><br>reads / writes fs.img blocks"] IRQ["VIRTIO0_IRQ<br><br>external interrupt"] end subgraph I["interrupt delivery"] direction TB PLIC_PATH["PLIC + devintr()<br><br>claim virtio IRQ<br>call virtio_disk_intr<br>complete IRQ"] end FS -- needs disk block --> BCACHE BCACHE -- uses locked buffer --> BUF BCACHE -- cache miss or writeback --> VRW VINIT -- sets up --> DISK VINIT -- allocates and registers --> VQ VINIT -- configures --> MMIO VRW -- uses buffer data --> BUF VRW -- allocates 3 descriptors<br>header + data + status --> VQ VRW -- "records in-flight request" --> DISK VRW -- notifies queue 0 --> MMIO MMIO -- device sees available request --> DEVICE DEVICE -- DMA reads/writes buffer block --> VQ DEVICE -- completion interrupt --> IRQ IRQ -- external interrupt --> PLIC_PATH PLIC_PATH -- calls --> VINTR VINTR -- acknowledges interrupt --> MMIO VINTR -- reads completed entries --> VQ VINTR -- "clears b->disk and wakeup(b)" --> BUF FS:::process BCACHE:::iface BUF:::file VINIT:::iface VRW:::iface VINTR:::iface DISK:::source VQ:::source MMIO:::source DEVICE:::source IRQ:::source PLIC_PATH:::backend 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
disk is the global virtio driver state.
| Field | Meaning |
|---|---|
desc | Descriptor table used to describe request buffers. |
avail | Ring where the driver publishes descriptor chains for the device. |
used | Ring where the device publishes completed descriptor chains. |
free[NUM] | Tracks which descriptors are free. |
used_idx | Tracks how far the driver has processed the used ring. |
info[NUM] | Maps an in-flight request back to its struct buf and status byte. |
ops[NUM] | Request headers, one per descriptor slot. |
vdisk_lock | Protects virtio disk driver state. |
The virtqueue has three main parts.
| Part | Owner | Purpose |
|---|---|---|
| Descriptor table | Driver writes, device reads | Describes memory buffers for a request. |
| Available ring | Driver writes, device reads | Tells the device which descriptor chain to process. |
| Used ring | Device writes, driver reads | Tells the driver which request finished. |