Skip to content

andrewhinh/browser_os

 
 

Repository files navigation

Browser OS

An OS written in Rust on the web. Inspired by blog_os.

Quickstart

# install dependencies
rustup toolchain install nightly  # at least nightly 2020-07-15
cargo install bootimage
cargo install --locked bacon
npm install
uv venv
uv pip install pre-commit
source .venv/bin/activate
pre-commit install
flyctl auth login

# test and build the kernel
cargo test -p os --target crates/os/x86_64-os.json -Z build-std=core,compiler_builtins,alloc -Z build-std-features=compiler-builtins-mem
cargo bootimage -p os --target crates/os/x86_64-os.json -Z build-std=core,compiler_builtins,alloc -Z build-std-features=compiler-builtins-mem

# run the kernel
qemu-system-x86_64 -drive format=raw,file=target/x86_64-os/debug/bootimage-os.bin  # runs in terminal
cargo run -p os --target crates/os/x86_64-os.json -Z build-std=core,compiler_builtins,alloc -Z build-std-features=compiler-builtins-mem  # runs in separate window

# test the app
cargo test -p app

# serve the app
mprocs

# run pre-commit hooks
uvx pre-commit run --all-files

# deploy to fly.io
flyctl launch  # once
flyctl deploy

Roadmap

  • Kernel Core

    • Bootloader integration: bootable disk image via bootloader crate
    • CPU setup: GDT, TSS (double-fault IST), IDT
    • Interrupts: PIC remap, timer + keyboard interrupts, basic exception handlers
    • RTC: gettimeofday
    • Memory management: 4-level page tables, frame allocator from BootInfo, heap allocator
    • User heap: brk, sbrk
    • Console & serial: VGA text mode, UART, serial-based logging
    • Telemetry: [[SYSINFO …]] and [[PROC …]] markers for frontend stats
  • Async Runtime & Input

    • Cooperative executor: async/await task system with wakers
    • Keyboard pipeline: interrupt → scancode queue → async stream → VGA
    • Spinning mutex: interrupt-safe locking primitive
  • Web App & Browser UX

    • Axum backend: serve static assets, manage shared QEMU bridge state
    • QEMU bridge: spawn QEMU, capture serial stream, decode [[VGA_FRAME]]/[[VGA_CELL]]/[[VGA_SCROLL]]
    • WebSocket VGA stream: push frames/cells to browser, cache latest frame for new clients
    • Browser terminal UI: Tailwind CRT-style 80×25 grid, responsive scaling, auto-reconnect
    • Keyboard bridge: browser keydown → QMP sendkey
    • QEMU controls: reset, pause/resume via QMP
    • Stats & IPC panels: live OS stats and scheduler/IPC views via /api/os/* endpoints
    • Multi-session runtime: multiple isolated QEMU instances (sandboxed containers or microVMs)
  • User Mode & Syscalls

    • Syscall entry: syscall/sysret, MSR setup (IA32_STAR, IA32_LSTAR, IA32_FMASK)
    • Ring 3 transition: enter user mode and return via syscall
    • Console I/O: write(fd=1, …) to VGA, read(fd=0, …) from keyboard
    • Process info: getpid, getppid
    • IPC: channel_send, channel_recv
    • Termination: exit(code)
  • Processes & Scheduling

    • PCB: per-process metadata (PID, parent, stacks, entry, state, saved context)
    • Process table: kernel-managed collection with current-process tracking
    • Preemptive scheduler: timer-driven round-robin
    • Context switching: save/restore registers and CR3
    • Program loading: flat/minimal ELF blobs into heap-backed regions
    • Process lifecycle: wait, waitpid
    • Process blocking: nanosleep
  • IPC

    • Message-passing: per-process mailboxes with send/recv
    • PID-based addressing: target processes by PID
    • Blocking IPC: recv sleeps until message arrives
    • pipe: anonymous unidirectional byte stream between processes
  • File System

    • In-memory FS (ramfs): inode table, directory entries, root directory
    • Per-process FD table: integrate with PCB, FD allocation/deallocation
    • Path resolution: absolute/relative parsing, . and ..
    • On-disk FS: virtio-blk backend or FAT32 via fatfs crate
    • File I/O: open, close, read (file), write (file), lseek
    • File metadata: stat, fstat
    • Directories: mkdir, rmdir, readdir/getdents
    • File management: unlink, dup, dup2
    • Working directory: chdir, getcwd
  • Device Drivers

    • VGA text mode: character output mirrored to browser
    • Serial (UART): logging and VGA frame transport
    • PS/2 keyboard: scancode reading and decoding
    • Timer: tick counter and uptime
    • Block device: virtio-blk over PCI (virtio-spec-rs, pci_types)
  • Program Execution

    • Proper ELF loader: parse ELF headers, load segments, support static executables
    • Simple shell: command interpreter that can run programs
    • Program execution: exec
  • Microkernel Services

    • User-space console server: move VGA/keyboard handling to user space
    • User-space FS server: move ramfs to user space, communicate over IPC
  • Tooling & Testing

    • Kernel tests: custom test framework with QEMU exit codes
    • Syscall tests: open/read/write/close round-trips
    • Scheduler tests: process creation, blocking, wake-up
    • FS tests: file/directory operations, path resolution

Description

System Architecture Diagram

Some interesting features (beyond that covered in the blog post series):

  • The kernel’s vga_buffer module mirrors screen state over the serial port using three compact text formats: a full-screen snapshot ([[VGA_FRAME …]]), single-cell changes ([[VGA_CELL …]]), and scroll events ([[VGA_SCROLL …]]). The HTTP/WebSocket server in the app crate tails QEMU’s stdout/stderr, parses those markers in parse_vga_line, and turns them into typed VgaUpdate messages (Frame, Cell, Scroll) that are broadcast over /ws/vga. The 80×25 grid of <span>s is built once at startup. Since frame messages fully (re)paint the grid, cell messages only touch a single DOM node, and scroll messages reuse and rotate existing row elements, only the handful of cells that actually changed are updated.
  • The timer interrupt uses sysinfo to maintain an uptime/tick counter and periodically emit compact telemetry lines ([[SYSINFO uptime_ms=… usable_mb=… total_mb=… ticks=…]] and [[PROC …]]) alongside the VGA stream. The Axum bridge parses those with parse_sysinfo_line and parse_process_line, caches the latest OsStats/ProcessesSnapshot in AppState, and exposes them over /api/os/stats and /api/os/processes for the OS Stats and Scheduler/IPC panels.
  • The kernel exposes a tiny syscall/sysretq-based ABI: init_syscalls programs the relevant MSRs (STAR/LSTAR/EFER/FM AKS), and the naked syscall_entry stub saves registers, marshals arguments into a SyscallArgs struct, and calls a Rust syscall_handler. The current interface supports three calls: exit(code) to terminate a user program and drop back into a kernel monitor, write(fd, buf, len) to send bytes from user space to the VGA/serial console (supporting fd = 1 only, with safe copying and a 1 KiB cap), and read(fd, buf, len) to synchronously pull bytes from the asynchronous keyboard pipeline into a user buffer until newline or EOF (fd = 0 only).
  • The process subsystem keeps a heap-backed PCB table (pid, parent, kernel stack pointer, entry point, run state, saved CPU context, and owning code/stack buffers) plus a preemptive round-robin scheduler wired to the timer IRQ. Each tick saves the outgoing registers, restores the next ready process (including CR3, RIP/RSP, RFLAGS), and ensures we only context-switch on ring‑3 frames so syscalls can run uninterrupted. The user-mode loader copies flat/minimal ELF blobs into per-process images, records the entry/stack pointers, and hands the PCBs to the scheduler so multiple user binaries (echo/ticker) can time-slice instead of being baked into the kernel.
  • A tiny message-passing subsystem gives each process a kernel-managed mailbox. sys_send(target_pid, buf, len) copies user payloads into the target's bounded queue; sys_recv(buf, len) polls the caller's own mailbox (non-blocking, returns EAGAIN if empty).

The important files are:

crates/
  app/
    src/
      handlers.rs           HTTP + WebSocket handlers for VGA, QEMU control, and OS stats/IPC APIs
      qmp.rs                QMP client for sending sendkey and other monitor commands
    tests/
      handlers.rs           smoke tests for static asset and API endpoints
  os/
    src/
      boot/
        gdt.rs              GDT/TSS setup (kernel + user segments, IST stack)
        interrupts.rs       IDT, PIC, exception and IRQ handlers
        memory.rs           paging setup and BootInfo-based frame allocator
      drivers/
        serial.rs           UART (COM1) initialization and serial_print macros
        vga.rs              VGA text console + serial mirroring (`[[VGA_FRAME]]`, `[[VGA_CELL]]`, `[[VGA_SCROLL]]`)
      process/
        mod.rs              PCB table, PID management, context save/restore
        scheduler.rs        preemptive round-robin scheduler
      programs/
        binaries.rs         baked-in user binaries (ticker + IPC pinger/ponger) as raw assembly
        loader.rs           copies flat/minimal ELF images into per-process text/stack regions
        user_mode.rs        kicks off user space, wires the loader, hands control to scheduler
      task/
        executor.rs         cooperative async executor (task queue + Wakers)
        keyboard.rs         async keyboard pipeline (scancodes → characters)
      allocator.rs          heap allocator wiring + fixed-size block allocator
      context.rs            saved CPU context (registers, stack pointer, etc.)
      ipc.rs                message-passing IPC with per-process mailboxes
      syscall.rs            syscall entry/handler and syscall ABI
      sysinfo.rs            computes memory totals and emits `[[SYSINFO]]`/`[[PROC]]` telemetry for the frontend stats panels
    tests/
      basic_boot.rs         println smoke test
      heap_allocation.rs    heap allocator stress/regression tests
      stack_overflow.rs     double-fault IST coverage
      should_panic.rs       ensures panic path exits QEMU as expected
      program_loader.rs     validates spawn_program stacks + ELF guards
    x86_64-os.json          custom target specification for the kernel
src/
  index.html                CRT-styled layout with `#vga-grid`, OS Stats and Scheduler/IPC panels, and QEMU control buttons
  app.ts                    builds the 80×25 grid, manages `/ws/vga` WebSocket + keyboard → JSON, polls `/api/os/stats` and `/api/os/processes`, and wires QEMU reset/pause controls

About

An OS in the Browser

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 69.0%
  • TypeScript 12.1%
  • JavaScript 11.7%
  • HTML 6.3%
  • Dockerfile 0.9%