├── .gdbinit ├── grub.cfg ├── .gitignore ├── src ├── sh.zig ├── picirq.zig ├── initcode.S ├── memlayout.zig ├── trapasm.S ├── swtch.S ├── util.zig ├── param.zig ├── sleeplock.zig ├── kernel.ld ├── entry.zig ├── uart.zig ├── file.zig ├── kalloc.zig ├── ioapic.zig ├── main.zig ├── trap.zig ├── lapic.zig ├── spinlock.zig ├── ide.zig ├── x86.zig ├── bio.zig ├── log.zig ├── mp.zig ├── kbd.zig ├── vm.zig ├── mmu.zig ├── console.zig ├── fs.zig ├── proc.zig └── vector.S ├── README.md ├── .devcontainer ├── devcontainer.json └── Dockerfile └── scripts └── build_initcode.sh /.gdbinit: -------------------------------------------------------------------------------- 1 | target remote localhost:1234 2 | file zig-out/bin/kernel.elf 3 | -------------------------------------------------------------------------------- /grub.cfg: -------------------------------------------------------------------------------- 1 | menuentry "Zig Bare Bones" { 2 | multiboot /boot/kernel.elf 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache/ 2 | zig-out/ 3 | /release/ 4 | /debug/ 5 | /build/ 6 | /build-*/ 7 | /docgen_tmp/ 8 | -------------------------------------------------------------------------------- /src/sh.zig: -------------------------------------------------------------------------------- 1 | const console = @import("console.zig"); 2 | 3 | pub fn panic(s: []const u8) noreturn { 4 | console.puts(s); 5 | while (true) {} 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xv6-zig 2 | 3 | This is a reimplementation of xv6 (x86 ver) in Zig. 4 | 5 | ## Requirements 6 | 7 | - latest Zig 8 | - QEMU 9 | - GRUB 10 | - xorriso 11 | 12 | ## Build and Run 13 | 14 | ```sh 15 | zig build run 16 | ``` 17 | -------------------------------------------------------------------------------- /src/picirq.zig: -------------------------------------------------------------------------------- 1 | const x86 = @import("x86.zig"); 2 | 3 | const IO_PIC1 = 0x20; // Master (IRQs 0-7) 4 | const IO_PIC2 = 0xA0; // Slave (IRQs 8-15) 5 | 6 | pub fn picinit() void { 7 | x86.out(IO_PIC1 + 1, @as(u8, 0xff)); 8 | x86.out(IO_PIC2 + 1, @as(u8, 0xff)); 9 | } 10 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ubuntu", 3 | "build": { 4 | "dockerfile": "Dockerfile", 5 | "args": { "VARIANT": "ubuntu-22.04" } 6 | }, 7 | 8 | "remoteUser": "vscode", 9 | "runArgs": [], 10 | 11 | "customizations": { 12 | "vscode": { 13 | "extensions": [ 14 | "tiehuis.zig", 15 | "AugusteRame.zls-vscode" 16 | ] 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /scripts/build_initcode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p zig-out/bin 4 | zig cc -c -target x86-freestanding-eabi -o zig-out/bin/initcode.o src/initcode.S 5 | ld -m elf_i386 -N -e start -Ttext 0 -o zig-out/bin/initcode.out zig-out/bin/initcode.o 6 | objcopy -S -Obinary zig-out/bin/initcode.out zig-out/bin/initcode 7 | objcopy -Ibinary -Oelf32-i386 zig-out/bin/initcode zig-out/bin/initcode.o 8 | -------------------------------------------------------------------------------- /src/initcode.S: -------------------------------------------------------------------------------- 1 | # Initial process execs /init. 2 | # This code runs in user space. 3 | 4 | 5 | # exec(init, argv) 6 | .globl start 7 | start: 8 | pushl $argv 9 | pushl $init 10 | pushl $0 // where caller pc would be 11 | movl $7, %eax // SYS_exec = 7 12 | int $64 // T_SYSCALL = 64 13 | 14 | # for(;;) exit(); 15 | exit: 16 | movl $2, %eax // SYS_exit = 2 17 | int $64 // T_SYSCALL = 64 18 | jmp exit 19 | 20 | # char init[] = "/init\0"; 21 | init: 22 | .string "/init\0" 23 | 24 | # char *argv[] = { init, 0 }; 25 | .p2align 2 26 | argv: 27 | .long init 28 | .long 0 29 | 30 | -------------------------------------------------------------------------------- /src/memlayout.zig: -------------------------------------------------------------------------------- 1 | // Memoty layout 2 | 3 | pub const EXTMEM: usize = 0x100000; // Start of extended memmory 4 | pub const PHYSTOP: usize = 0xE000000; // Top physical memory 5 | pub const DEVSPACE: usize = 0xFE000000; // Other devices are at high addresses 6 | 7 | // Key addresses for address space layout (see kemap in vm.zig for layout) 8 | pub const KERNBASE: usize = 0x80000000; // First Kernel virtual address 9 | pub const KERNLINK: usize = KERNBASE + EXTMEM; // Address where kernel is linked 10 | 11 | pub fn v2p(v: usize) usize { 12 | return v - KERNBASE; 13 | } 14 | 15 | pub fn p2v(p: usize) usize { 16 | return p + KERNBASE; 17 | } 18 | -------------------------------------------------------------------------------- /src/trapasm.S: -------------------------------------------------------------------------------- 1 | # TODO: Can we write it in Zig? 2 | 3 | # vectors.S sends all traps here. 4 | .globl alltraps 5 | alltraps: 6 | # Build trap frame. 7 | pushl %ds 8 | pushl %es 9 | pushl %fs 10 | pushl %gs 11 | pushal 12 | 13 | # Set up data segments. 14 | movw $(2<<3), %ax 15 | movw %ax, %ds 16 | movw %ax, %es 17 | 18 | # Call trap(tf), where tf=%esp 19 | pushl %esp 20 | call trap 21 | addl $4, %esp 22 | 23 | # Return falls through to trapret... 24 | .globl trapret 25 | trapret: 26 | popal 27 | popl %gs 28 | popl %fs 29 | popl %es 30 | popl %ds 31 | addl $0x8, %esp # trapno and errcode 32 | iret 33 | 34 | loop: 35 | jmp loop 36 | -------------------------------------------------------------------------------- /src/swtch.S: -------------------------------------------------------------------------------- 1 | # Context switch 2 | # 3 | # void swtch(struct context **old, struct context *new); 4 | # 5 | # Save the current registers on the stack, creating 6 | # a struct context, and save its address in *old. 7 | # Switch stacks to new and pop previously-saved registers. 8 | 9 | .globl swtch 10 | swtch: 11 | movl 4(%esp), %eax 12 | movl 8(%esp), %edx 13 | 14 | # Save old callee-saved registers 15 | pushl %ebp 16 | pushl %ebx 17 | pushl %esi 18 | pushl %edi 19 | 20 | # Switch stacks 21 | movl %esp, (%eax) 22 | movl %edx, %esp 23 | 24 | # Load new callee-saved registers 25 | popl %edi 26 | popl %esi 27 | popl %ebx 28 | popl %ebp 29 | ret 30 | -------------------------------------------------------------------------------- /src/util.zig: -------------------------------------------------------------------------------- 1 | pub fn memmov(dst: [*]u8, src: [*]const u8, n: usize) void { 2 | const s = src; 3 | var d = dst; 4 | 5 | const sAddr = @intFromPtr(s); 6 | const dAddr = @intFromPtr(d); 7 | if (sAddr < dAddr and sAddr + n > dAddr) { 8 | var i = n; 9 | while (i > 0) : (i -= 1) { 10 | d[i] = s[i]; 11 | } 12 | } else { 13 | var i: usize = 0; 14 | while (i < n) : (i += 1) { 15 | d[i] = s[i]; 16 | } 17 | } 18 | } 19 | 20 | pub fn safestrcpy(dst: *[16]u8, src: []const u8) void { 21 | const len = @min(src.len, dst.len - 1); 22 | @memcpy(dst[0..len], src[0..len]); 23 | dst[len + 1] = 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/param.zig: -------------------------------------------------------------------------------- 1 | pub const NPROC = 64; // maximum number of processes 2 | pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack 3 | pub const NCPU = 8; // maximum number of CPUs 4 | pub const NOFILE: u32 = 16; // open files per process 5 | pub const NFILE = 100; // open files per system 6 | pub const NINODE = 50; // maximum number of active i-nodes 7 | pub const NDEV = 10; // maximum major device number 8 | pub const ROOTDEV = 1; // device number of file system root disk 9 | pub const MAXARG = 32; // max exec arguments 10 | pub const MAXOPBLOCKS = 10; // max # of blocks any FS op writes 11 | pub const LOGSIZE = (MAXOPBLOCKS * 3); // max data blocks in on-disk log 12 | pub const NBUF = (MAXOPBLOCKS * 3); // size of disk block cache 13 | pub const FSSIZE = 1000; // size of file system in blocks 14 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/ubuntu/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Ubuntu version (use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon): ubuntu-22.04, ubuntu-20.04, ubuntu-18.04 4 | ARG VARIANT="jammy" 5 | FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} 6 | 7 | ARG ZIG_VERSION="0.12.0-dev.3142+9d500bda2" 8 | 9 | # [Optional] Uncomment this section to install additional OS packages. 10 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 11 | && apt-get -y install --no-install-recommends git nasm build-essential qemu qemu-system qemu-system-common qemu-utils qemu-system-gui xorriso grub2 gdb 12 | 13 | RUN curl -L https://ziglang.org/builds/zig-linux-x86_64-${ZIG_VERSION}.tar.xz -o zig.tar.xz \ 14 | && mkdir -p /usr/src \ 15 | && tar xf zig.tar.xz -C /usr/src \ 16 | && mv /usr/src/zig-linux-x86_64-${ZIG_VERSION} /usr/src/zig 17 | 18 | ENV PATH /usr/src/zig:$PATH 19 | -------------------------------------------------------------------------------- /src/sleeplock.zig: -------------------------------------------------------------------------------- 1 | const proc = @import("proc.zig"); 2 | const spinlock = @import("spinlock.zig"); 3 | 4 | // Long-term locks for processes 5 | pub const sleeplock = struct { 6 | locked: bool, // Is the lock held? 7 | lk: spinlock.spinlock, // spinlock protecting this sleep lock 8 | 9 | // For debugging: 10 | name: []const u8, // Name of lock 11 | pid: u32, // Process hoding lock 12 | 13 | const Self = @This(); 14 | 15 | pub fn init(name: []const u8) Self { 16 | return Self{ 17 | .locked = false, 18 | .lk = spinlock.spinlock.init("sleep lock"), 19 | .name = name, 20 | .pid = 0, 21 | }; 22 | } 23 | 24 | pub fn acquire(self: *Self) void { 25 | self.lk.acquire(); 26 | defer self.lk.release(); 27 | while (self.locked) { 28 | proc.sleep(self.locked, &self.lk); 29 | } 30 | self.locked = true; 31 | self.pid = proc.myproc().pid; 32 | } 33 | 34 | pub fn release(self: *Self) void { 35 | self.lk.acquire(); 36 | defer self.lk.release(); 37 | self.locked = false; 38 | self.pid = 0; 39 | proc.wakeup(@intFromPtr(self)); 40 | } 41 | 42 | pub fn holding(self: *Self) bool { 43 | self.lk.acquire(); 44 | defer self.lk.release(); 45 | const r = self.locked and (self.pid == proc.myproc().pid); 46 | return r; 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /src/kernel.ld: -------------------------------------------------------------------------------- 1 | /* Simple linker script for the JOS kernel. 2 | See the GNU ld 'info' manual ("info ld") to learn the syntax. */ 3 | 4 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") 5 | OUTPUT_ARCH(i386) 6 | ENTRY(_start) 7 | 8 | SECTIONS 9 | { 10 | /* Link the kernel at this address: "." means the current address */ 11 | /* Must be equal to KERNLINK */ 12 | . = 0x80100000; 13 | 14 | .text : AT(0x100000) { 15 | KEEP(*(.multiboot)) 16 | *(.text .stub .text.* .gnu.linkonce.t.*) 17 | } 18 | 19 | PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ 20 | 21 | .rodata : { 22 | *(.rodata .rodata.* .gnu.linkonce.r.*) 23 | } 24 | 25 | /* Include debugging information in kernel memory */ 26 | .stab : { 27 | PROVIDE(__STAB_BEGIN__ = .); 28 | *(.stab); 29 | PROVIDE(__STAB_END__ = .); 30 | } 31 | 32 | .stabstr : { 33 | PROVIDE(__STABSTR_BEGIN__ = .); 34 | *(.stabstr); 35 | PROVIDE(__STABSTR_END__ = .); 36 | } 37 | 38 | /* Adjust the address for the data segment to the next page */ 39 | . = ALIGN(0x1000); 40 | 41 | /* Conventionally, Unix linkers provide pseudo-symbols 42 | * etext, edata, and end, at the end of the text, data, and bss. 43 | * For the kernel mapping, we need the address at the beginning 44 | * of the data section, but that's not one of the conventional 45 | * symbols, because the convention started before there was a 46 | * read-only rodata section between text and data. */ 47 | PROVIDE(data = .); 48 | 49 | /* The data segment */ 50 | .data : { 51 | *(.data) 52 | } 53 | 54 | PROVIDE(edata = .); 55 | 56 | .bss : { 57 | *(.bss) 58 | } 59 | 60 | PROVIDE(end = .); 61 | 62 | /DISCARD/ : { 63 | *(.eh_frame .note.GNU-stack) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/entry.zig: -------------------------------------------------------------------------------- 1 | const mmu = @import("mmu.zig"); 2 | const param = @import("param.zig"); 3 | 4 | comptime { 5 | asm ( 6 | \\.align 4 7 | \\.section ".multiboot" 8 | \\multiboot_header: 9 | \\ .long 0x1badb002 10 | \\ .long 0 11 | \\ .long (0 - 0x1badb002) 12 | ); 13 | } 14 | 15 | comptime { 16 | asm ( 17 | \\.globl _start 18 | \\_start = start - 0x80000000 19 | \\.comm stack, 4096 // KSTACKSIZE 20 | ); 21 | } 22 | 23 | export fn start() align(16) callconv(.Naked) noreturn { 24 | asm volatile ( 25 | // Turn on page size extension for 4Mbyte pages 26 | \\movl %%cr4, %%eax 27 | \\orl %[cr4_pse], %%eax 28 | \\movl %%eax, %%cr4 29 | : 30 | : [cr4_pse] "{ecx}" (mmu.CR4_PSE), 31 | ); 32 | asm volatile ( 33 | // Set page directory 34 | \\movl $(entrypgdir - 0x80000000), %%eax 35 | \\movl %%eax, %%cr3 36 | ); 37 | asm volatile ( 38 | // Turn on paging 39 | \\movl %%cr0, %%eax 40 | \\orl %[cr0_bits], %%eax 41 | \\movl %%eax, %%cr0 42 | : 43 | : [cr0_bits] "{ecx}" (mmu.CR0_PG | mmu.CR0_WP), 44 | ); 45 | asm volatile ( 46 | // Set up the stack pointer 47 | \\movl $stack, %%eax 48 | \\addl %[kstacksize], %%eax 49 | \\movl %%eax, %%esp 50 | 51 | // Jump to main(), and switch to executing at 52 | // high addresses. The indirect call is needed because 53 | // the assembler produces a PC-relative instruction 54 | // for a direct jump. 55 | \\mov $main, %%eax 56 | \\jmp *%%eax 57 | : 58 | : [kstacksize] "{ecx}" (param.KSTACKSIZE), 59 | ); 60 | while (true) {} 61 | } 62 | -------------------------------------------------------------------------------- /src/uart.zig: -------------------------------------------------------------------------------- 1 | const console = @import("console.zig"); 2 | const ioapic = @import("ioapic.zig"); 3 | const lapic = @import("lapic.zig"); 4 | const trap = @import("trap.zig"); 5 | const x86 = @import("x86.zig"); 6 | 7 | const COM1 = 0x3f8; 8 | 9 | var uart: bool = false; 10 | 11 | pub fn uartinit() void { 12 | // Turn off the FIFO 13 | x86.out(COM1 + 2, @as(u8, 0)); 14 | 15 | // 9600 baud, 8 data bits, 1 stop bit, parity off. 16 | x86.out(COM1 + 3, @as(u8, 0x80)); // Unlock divisor 17 | x86.out(COM1 + 0, @as(u8, 115200 / 9600)); 18 | x86.out(COM1 + 1, @as(u8, 0)); 19 | x86.out(COM1 + 3, @as(u8, 0x03)); // Lock divisor, 8 data bits. 20 | x86.out(COM1 + 4, @as(u8, 0)); 21 | x86.out(COM1 + 1, @as(u8, 0x01)); // Enable receive interrupts. 22 | 23 | // If status if 0xFF, no serial port. 24 | if (x86.in(u8, COM1 + 5) == 0xff) { 25 | return; 26 | } 27 | uart = true; 28 | 29 | // Acknowledge pre-existing interrupt conditions; 30 | // enable interrupts. 31 | _ = x86.in(u8, COM1 + 2); 32 | _ = x86.in(u8, COM1 + 0); 33 | ioapic.ioapicenable(trap.IRQ_COM1, 0); 34 | 35 | // Announce that we're here. 36 | const str = "xv6..."; 37 | for (str) |c| { 38 | putc(c); 39 | } 40 | } 41 | 42 | pub fn putc(c: u8) void { 43 | if (!uart) { 44 | return; 45 | } 46 | var i: u32 = 0; 47 | while (i < 128 and (x86.in(u8, COM1 + 5)) & 0x20 == 0) : (i += 1) { 48 | lapic.microdelay(10); 49 | } 50 | x86.out(COM1 + 0, @as(u8, c)); 51 | } 52 | 53 | fn getc() ?u8 { 54 | if (!uart) { 55 | return null; 56 | } 57 | if ((x86.in(u8, COM1 + 5)) & 0x01 == 0) { 58 | return null; 59 | } 60 | return x86.in(u8, COM1 + 0); 61 | } 62 | 63 | pub fn uartintr() void { 64 | console.consoleintr(getc); 65 | } 66 | -------------------------------------------------------------------------------- /src/file.zig: -------------------------------------------------------------------------------- 1 | const console = @import("console.zig"); 2 | const sleeplock = @import("sleeplock.zig"); 3 | const spinlock = @import("spinlock.zig"); 4 | const param = @import("param.zig"); 5 | 6 | pub const file = struct { 7 | typ: enum { FD_NONE, FD_PIPE, FD_INODE }, 8 | ref: u32, // reference count, 9 | readable: bool, 10 | writable: bool, 11 | // pipe: pipe, 12 | // ip: inode, 13 | // off: u32, 14 | }; 15 | 16 | // in-memory copy of an inode 17 | pub const inode = struct { 18 | dev: u32, // Device number 19 | inum: u32, // // Inode number 20 | ref: u32, // Reference count 21 | lock: sleeplock.sleeplock, // protects everything below here 22 | valid: bool, // inode has been read from disk? 23 | 24 | typ: u16, // copy of disk inode 25 | major: u16, 26 | minor: u16, 27 | nlink: u16, 28 | size: u32, 29 | addrs: [13]u32, 30 | 31 | const Self = @This(); 32 | 33 | pub fn lock(self: *Self) void { 34 | _ = self; 35 | // TODO: implement 36 | } 37 | 38 | pub fn unlock(self: *Self) void { 39 | _ = self; 40 | // TODO: implement 41 | } 42 | }; 43 | 44 | pub const devsw_t = struct { 45 | read: *const fn (ip: *inode, dst: [*]u8, n: u32) ?u32, 46 | write: *const fn (ip: *inode, buf: []const u8, n: u32) u32, 47 | }; 48 | 49 | pub var devsw: [param.NDEV]devsw_t = init: { 50 | const initial_value: [param.NDEV]devsw_t = undefined; 51 | for (initial_value, 0..) |*pt, i| { 52 | if (i == CONSOLE) { 53 | pt.* = devsw_t{ 54 | .read = console.consoleread, 55 | .write = console.consolewrite, 56 | }; 57 | } else { 58 | pt.* = undefined; 59 | } 60 | } 61 | break :init initial_value; 62 | }; 63 | 64 | var ftable = struct { 65 | lock: spinlock.spinlock, 66 | file: [param.NFILE]file, 67 | }{ 68 | .lock = spinlock.spinlock.init("file"), 69 | .file = undefined, 70 | }; 71 | 72 | pub const CONSOLE = 1; 73 | -------------------------------------------------------------------------------- /src/kalloc.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mmu = @import("mmu.zig"); 3 | const spinlock = @import("spinlock.zig"); 4 | 5 | extern const end: *u8; 6 | 7 | pub const run = struct { 8 | next: ?*run, 9 | }; 10 | 11 | pub var kmem = struct { 12 | lock: spinlock.spinlock, 13 | use_lock: bool, 14 | freelist: ?*run, 15 | }{ 16 | .lock = spinlock.spinlock.init("kmem"), 17 | .use_lock = false, 18 | .freelist = null, 19 | }; 20 | 21 | pub fn kinit1(vstart: usize, vend: usize) void { 22 | freerange(vstart, vend); 23 | } 24 | 25 | pub fn kinit2(vstart: usize, vend: usize) void { 26 | freerange(vstart, vend); 27 | kmem.use_lock = true; 28 | } 29 | 30 | fn freerange(vstart: usize, vend: usize) void { 31 | var p = mmu.pgroundup(vstart); 32 | while (p + mmu.PGSIZE <= vend) : (p += mmu.PGSIZE) { 33 | kfree(p); 34 | } 35 | } 36 | 37 | // Free the page of physical memory pointed at by v, 38 | // which normally should have been returned by a 39 | // call to kalloc(). (The exception is when 40 | // initializing the allocator; see kinit above.) 41 | // TODO: implement lacked logic 42 | fn kfree(v: usize) void { 43 | var r: *run = undefined; 44 | 45 | //if((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP) 46 | //panic("kfree"); 47 | 48 | // Fill with junk to catch dangling refs. 49 | for (@as([*]u8, @ptrFromInt(v))[0..mmu.PGSIZE]) |*b| { 50 | b.* = 1; 51 | } 52 | 53 | //if(kmem.use_lock) 54 | // acquire(&kmem.lock); 55 | r = @as(*run, @ptrFromInt(v)); 56 | r.*.next = kmem.freelist; 57 | kmem.freelist = r; 58 | 59 | //if(kmem.use_lock) 60 | // release(&kmem.lock); 61 | } 62 | 63 | // Allocate one 4096-byte page of physical memory. 64 | // Returns a pointer that the kernel can use. 65 | // TODO: lock 66 | pub fn kalloc() ?usize { 67 | var x = kmem.freelist; 68 | var len: u32 = 0; 69 | while (true) { 70 | if (x) |y| { 71 | x = y.next; 72 | len += 1; 73 | } else { 74 | break; 75 | } 76 | } 77 | //if(kmem.use_lock) 78 | // acquire(&kmem.lock); 79 | const opt = kmem.freelist; 80 | if (opt) |r| { 81 | kmem.freelist = r.next; 82 | return @intFromPtr(r); 83 | } 84 | 85 | return null; 86 | } 87 | -------------------------------------------------------------------------------- /src/ioapic.zig: -------------------------------------------------------------------------------- 1 | // The I/O APIC manages hardware interrupts for an SMP system. 2 | // http://www.intel.com/design/chipsets/datashts/29056601.pdf 3 | // See also picirq.c. 4 | 5 | const mp = @import("mp.zig"); 6 | const trap = @import("trap.zig"); 7 | 8 | const IOAPIC = 0xFEC00000; // Default physical address of IO APIC 9 | 10 | const REG_ID = 0x00; // Register index: ID 11 | const REG_VER = 0x01; // Register index: version 12 | const REG_TABLE = 0x10; // Redirection table base 13 | 14 | // The redirection table starts at REG_TABLE and uses 15 | // two registers to configure each interrupt. 16 | // The first (low) register in a pair contains configuration bits. 17 | // The second (high) register contains a bitmask telling which 18 | const INT_DISABLED = 0x00010000; // Interrupt disabled 19 | const INT_LEVEL = 0x00008000; // Level-triggered (vs edge-) 20 | const INT_ACTIVELOW = 0x00002000; // Active low (vs high) 21 | const INT_LOGICAL = 0x00000800; // Destination is CPU id (vs APIC ID) 22 | 23 | var ioapic: *ioapic_t = undefined; 24 | 25 | const ioapic_t = packed struct { 26 | reg: u32, 27 | pad: u96, 28 | data: u32, 29 | 30 | const Self = @This(); 31 | 32 | fn read(self: *Self, reg: u32) u32 { 33 | self.*.reg = reg; 34 | return self.*.data; 35 | } 36 | 37 | fn write(self: *Self, reg: u32, data: u32) void { 38 | self.*.reg = reg; 39 | self.*.data = data; 40 | } 41 | }; 42 | 43 | pub fn ioapicinit() void { 44 | ioapic = @as(*ioapic_t, @ptrFromInt(IOAPIC)); 45 | const maxintr = (ioapic.read(REG_VER) >> 16) & 0xff; 46 | const id = ioapic.read(REG_ID) >> 24; 47 | if (id != mp.ioapicid) { 48 | asm volatile ("1: jmp 1b"); 49 | } 50 | 51 | // Mark all interrupts edge-triggered, active high, disabled, 52 | // and not routed to any CPUs. 53 | var i: u32 = 0; 54 | while (i <= maxintr) : (i += 1) { 55 | ioapic.write(REG_TABLE + 2 * i, INT_DISABLED | (trap.T_IRQ0 + i)); 56 | ioapic.write(REG_TABLE + 2 * i + 1, 0); 57 | } 58 | } 59 | 60 | pub fn ioapicenable(irq: u32, cpunum: u8) void { 61 | // Mark interrupt edge-triggered, active high, 62 | // enabled, and routed to the given cpunum, 63 | // which happens to be that cpu's APIC ID. 64 | ioapic.write(REG_TABLE + 2 * irq, trap.T_IRQ0 + irq); 65 | ioapic.write(REG_TABLE + 2 * irq + 1, @as(u32, @intCast(cpunum)) << 24); 66 | } 67 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const bio = @import("bio.zig"); 3 | const console = @import("console.zig"); 4 | const ide = @import("ide.zig"); 5 | const ioapic = @import("ioapic.zig"); 6 | const kalloc = @import("kalloc.zig"); 7 | const lapic = @import("lapic.zig"); 8 | const memlayout = @import("memlayout.zig"); 9 | const mmu = @import("mmu.zig"); 10 | const mp = @import("mp.zig"); 11 | const picirq = @import("picirq.zig"); 12 | const proc = @import("proc.zig"); 13 | const spinlock = @import("spinlock.zig"); 14 | const trap = @import("trap.zig"); 15 | const uart = @import("uart.zig"); 16 | const vm = @import("vm.zig"); 17 | 18 | extern const end: u8; 19 | 20 | export fn main() noreturn { 21 | const end_addr = @intFromPtr(&end); 22 | kalloc.kinit1(end_addr, memlayout.p2v(4 * 1024 * 1024)); 23 | 24 | vm.kvmalloc() orelse asm volatile ("1: jmp 1b"); 25 | mp.mpinit(); 26 | lapic.lapicinit(); 27 | vm.seginit(); 28 | picirq.picinit(); 29 | ioapic.ioapicinit(); 30 | console.consoleinit(); 31 | uart.uartinit(); 32 | trap.tvinit(); 33 | bio.binit(); 34 | // ide.ideinit(); 35 | // TODO: startothers() 36 | kalloc.kinit2(memlayout.p2v(4 * 1024 * 1024), memlayout.p2v(memlayout.PHYSTOP)); 37 | 38 | console.initialize(); 39 | 40 | proc.userinit(); 41 | 42 | locktest(); 43 | 44 | mpmain(); 45 | 46 | unreachable; 47 | } 48 | 49 | fn mpmain() void { 50 | console.printf("cpu{}: starting {}\n", .{ proc.cpuid(), proc.cpuid() }); 51 | trap.idtinit(); 52 | @atomicStore(bool, &proc.mycpu().started, true, std.builtin.AtomicOrder.SeqCst); 53 | proc.scheduler(); 54 | } 55 | 56 | // The boot page table used in entry.S and entryother.S. 57 | // Page directories (and page tables) must start on page boundaries, 58 | // hence the __aligned__ attribute. 59 | // PTE_PS in a page directory entry enables 4Mbyte pages. 60 | export var entrypgdir: [mmu.NPDENTRIES]u32 align(mmu.PGSIZE) = init: { 61 | var dir: [mmu.NPDENTRIES]u32 = undefined; 62 | // Map VA's [0, 4MB) to PA's [0, 4MB) 63 | dir[0] = (0) | mmu.PTE_P | mmu.PTE_W | mmu.PTE_PS; 64 | // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB) 65 | dir[memlayout.KERNBASE >> mmu.PDXSHIFT] = (0) | mmu.PTE_P | mmu.PTE_W | mmu.PTE_PS; 66 | break :init dir; 67 | }; 68 | 69 | fn locktest() void { 70 | var l = spinlock.spinlock.init("hoge"); 71 | 72 | l.acquire(); 73 | l.release(); 74 | l.acquire(); 75 | l.release(); 76 | l.acquire(); 77 | l.release(); 78 | } 79 | -------------------------------------------------------------------------------- /src/trap.zig: -------------------------------------------------------------------------------- 1 | // x86 trap and interrupt constants. 2 | 3 | const console = @import("console.zig"); 4 | const kbd = @import("kbd.zig"); 5 | const lapic = @import("lapic.zig"); 6 | const mmu = @import("mmu.zig"); 7 | const proc = @import("proc.zig"); 8 | const spinlock = @import("spinlock.zig"); 9 | const uart = @import("uart.zig"); 10 | const x86 = @import("x86.zig"); 11 | 12 | // Processor-defined 13 | pub const T_DIVIDE = 0; 14 | pub const T_DEBUG = 1; 15 | pub const T_NMI = 2; 16 | pub const T_BRKPT = 3; 17 | pub const T_OFLOW = 4; 18 | pub const T_BOUND = 5; 19 | pub const T_ILLOP = 6; 20 | pub const T_DEVICE = 7; 21 | pub const T_DBLFLT = 8; 22 | pub const T_TSS = 10; 23 | pub const T_SEGNP = 11; 24 | pub const T_STACK = 12; 25 | pub const T_GPFLT = 13; 26 | pub const T_PGFLT = 14; 27 | pub const T_FPERR = 16; 28 | pub const T_ALIGN = 17; 29 | pub const T_MCHK = 18; 30 | pub const T_SIMDERR = 19; 31 | pub const T_SYSCALL = 64; 32 | pub const T_DEFAULT = 500; 33 | pub const T_IRQ0 = 32; 34 | pub const IRQ_TIMER = 0; 35 | pub const IRQ_KBD = 1; 36 | pub const IRQ_COM1 = 4; 37 | pub const IRQ_IDE = 14; 38 | pub const IRQ_ERROR = 19; 39 | pub const IRQ_SPURIOUS = 31; 40 | 41 | var idt: [256]mmu.gatedesc = undefined; 42 | extern const vectors: u32; 43 | var tickslock = spinlock.spinlock.init("time"); 44 | pub var ticks: u32 = 0; 45 | 46 | pub fn tvinit() void { 47 | const v = @as([*]u32, @ptrCast(&vectors)); 48 | 49 | var i: u32 = 0; 50 | while (i < 256) : (i += 1) { 51 | idt[i] = mmu.gatedesc.new(false, mmu.SEG_KCODE << 3, v[i], 0); 52 | } 53 | idt[T_SYSCALL] = mmu.gatedesc.new(true, mmu.SEG_KCODE << 3, v[T_SYSCALL], mmu.DPL_USER); 54 | } 55 | 56 | pub fn idtinit() void { 57 | x86.lidt(@intFromPtr(&idt), @as(u16, @intCast(@sizeOf(@TypeOf(idt))))); 58 | } 59 | 60 | export fn trap(tf: *x86.trapframe) void { 61 | if (tf.trapno == T_SYSCALL) { 62 | // TODO: system call 63 | console.printf("syscall\n", .{}); 64 | } 65 | 66 | switch (tf.trapno) { 67 | T_IRQ0 + IRQ_TIMER => { 68 | tickslock.acquire(); 69 | ticks += 1; 70 | if (ticks == 10) { 71 | console.printf("ticks = {}", .{ticks}); 72 | } 73 | proc.wakeup(@intFromPtr(&ticks)); 74 | tickslock.release(); 75 | lapic.lapiceoi(); 76 | }, 77 | T_IRQ0 + IRQ_IDE => { 78 | // TODO: implement 79 | }, 80 | T_IRQ0 + IRQ_IDE + 1 => { 81 | // Bochs generates spurious IDE1 interrupts. 82 | }, 83 | T_IRQ0 + IRQ_KBD => { 84 | kbd.kbdintr(); 85 | lapic.lapiceoi(); 86 | // TODO: implement 87 | }, 88 | T_IRQ0 + IRQ_COM1 => { 89 | uart.uartintr(); 90 | lapic.lapiceoi(); 91 | // TODO: implement 92 | }, 93 | else => { 94 | asm volatile ("movl %[eip], %%eax" 95 | : 96 | : [eip] "r" (tf.eip), 97 | ); 98 | asm volatile ("1: jmp 1b"); 99 | }, 100 | } 101 | 102 | // TODO: implement 103 | } 104 | 105 | fn panicHandler(tf: *x86.trapframe) void { 106 | const base = tf.ebp; 107 | const message = @as([*]u8, @ptrFromInt(base + 8))[0..1000]; 108 | console.printf("hogehoge", .{}); 109 | 110 | for (message) |c| { 111 | if (c == 0) { 112 | break; 113 | } 114 | console.putChar(c); 115 | } 116 | console.putChar('\n'); 117 | } 118 | -------------------------------------------------------------------------------- /src/lapic.zig: -------------------------------------------------------------------------------- 1 | const trap = @import("trap.zig"); 2 | 3 | // Local APIC registers, divided by 4 for use as []u32 indices. 4 | const ID = 0x0020 / @sizeOf(u32); // ID 5 | const VER = 0x0030 / @sizeOf(u32); // VERSION 6 | const TPR = 0x0080 / @sizeOf(u32); // Task Priority 7 | const EOI = 0x00B0 / @sizeOf(u32); // EOI 8 | const SVR = 0x00F0 / @sizeOf(u32); // Spurious Interrupt Vector 9 | const ENABLE = 0x00000100; // Unit Enable 10 | const ESR = 0x0280 / @sizeOf(u32); // Error Status 11 | const ICRLO = 0x0300 / @sizeOf(u32); // Interrupt Command 12 | const INIT = 0x00000500; // INIT/RESET 13 | const STARTUP = 0x00000600; // Startup IPI 14 | const DELIVS = 0x00001000; // Delivery status 15 | const ASSERT = 0x00004000; // Assert interrupt (vs deassert) 16 | const DEASSERT = 0x00000000; 17 | const LEVEL = 0x00008000; // Level triggered 18 | const BCAST = 0x00080000; // Send to all APICs, including self 19 | const BUSY = 0x00001000; 20 | const FIXED = 0x00000000; 21 | const ICRHI = 0x0310 / @sizeOf(u32); // Interrupt Command [63:32] 22 | const TIMER = 0x0320 / @sizeOf(u32); // Local Vector Table 0 (TIMER) 23 | const X1 = 0x0000000B; // divide counts by 1 24 | const PERIODIC = 0x00020000; // Periodic 25 | const PCINT = 0x0340 / @sizeOf(u32); // Performance Counter LVT 26 | const LINT0 = 0x0350 / @sizeOf(u32); // Local Vector Table 1 (LINT0) 27 | const LINT1 = 0x0360 / @sizeOf(u32); // Local Vector Table 2 (LINT1) 28 | const ERROR = 0x0370 / @sizeOf(u32); // Local Vector Table 3 (ERROR) 29 | const MASKED = 0x00010000; // Interrupt masked 30 | const TICR = 0x0380 / @sizeOf(u32); // Timer Initial Count 31 | const TCCR = 0x0390 / @sizeOf(u32); // Timer Current Count 32 | const TDCR = 0x03E0 / @sizeOf(u32); // Timer Divide Configuration 33 | 34 | pub var lapic: [*]u32 = undefined; // initialized in mp.zig 35 | 36 | fn lapicw(index: u32, value: u32) void { 37 | lapic[index] = value; 38 | _ = lapic[ID]; // wait for write to finish 39 | } 40 | 41 | pub fn lapicinit() void { 42 | // Enable local APIC; set spurious interrupt vector. 43 | lapicw(SVR, ENABLE | (trap.T_IRQ0 + trap.IRQ_SPURIOUS)); 44 | 45 | // The timer repeatedly counts down at bus frequency 46 | // from lapic[TICR] and then issues an interrupt. 47 | // If xv6 cared more about precise timekeeping, 48 | // TICR would be calibrated using an external time source. 49 | lapicw(TDCR, X1); 50 | lapicw(TIMER, PERIODIC | (trap.T_IRQ0 + trap.IRQ_TIMER)); 51 | lapicw(TICR, 10000000); 52 | 53 | // Disable logical interrupt lines. 54 | lapicw(LINT0, MASKED); 55 | lapicw(LINT1, MASKED); 56 | 57 | // Disable performance counter overflow interrupts 58 | // on machines that provide that interrupt entry. 59 | 60 | if (((lapic[VER] >> 16) & 0xFF) >= 4) { 61 | lapicw(PCINT, MASKED); 62 | } 63 | 64 | // Map error interrupt to IRQ_ERROR. 65 | lapicw(ERROR, trap.T_IRQ0 + trap.IRQ_ERROR); 66 | 67 | // Clear error status register (requires back-to-back writes). 68 | lapicw(ESR, 0); 69 | lapicw(ESR, 0); 70 | 71 | // Ack any outstainding interrupts. 72 | lapicw(EOI, 0); 73 | 74 | // Send an Init Level De-Assert to synchronise arbitration ID's. 75 | lapicw(ICRHI, 0); 76 | lapicw(ICRLO, BCAST | INIT | LEVEL); 77 | while (lapic[ICRLO] & DELIVS != 0) {} 78 | 79 | // Enable interrupts on the APIC (but not on the processor). 80 | lapicw(TPR, 0); 81 | } 82 | 83 | pub fn lapicid() u32 { 84 | return lapic[ID] >> 24; 85 | } 86 | 87 | pub fn lapiceoi() void { 88 | lapicw(EOI, 0); 89 | } 90 | 91 | // Spin for a given number of microseconds. 92 | // On real hardware would want to tune this dynamically. 93 | pub fn microdelay(_: u32) void {} 94 | -------------------------------------------------------------------------------- /src/spinlock.zig: -------------------------------------------------------------------------------- 1 | const memlayout = @import("memlayout.zig"); 2 | const mmu = @import("mmu.zig"); 3 | const param = @import("param.zig"); 4 | const proc = @import("proc.zig"); 5 | const x86 = @import("x86.zig"); 6 | 7 | // Mutual exclusion lock. 8 | pub const spinlock = struct { 9 | locked: u32, // Is the lock held? 10 | 11 | // For debugging: 12 | name: []const u8, 13 | cpu: ?*proc.cpu, 14 | pcs: [10]usize, // The call stack (an aray of program counters) that locked the lock. 15 | 16 | const Self = @This(); 17 | 18 | pub fn init(name: []const u8) Self { 19 | return Self{ 20 | .name = name, 21 | .locked = 0, 22 | .cpu = null, 23 | .pcs = [10]usize{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 24 | }; 25 | } 26 | 27 | // Acquire the lock. 28 | // Loops (spins) until the lock is acquired. 29 | // Holding a lock for a long time may cause 30 | // other CPUs to waste time spinning to acquire it. 31 | pub fn acquire(self: *Self) void { 32 | pushcli(); // disable interrupts to avoid deadlock 33 | if (self.hoding()) { 34 | asm volatile ("1: jmp 1b"); // TODO: handle error 35 | } 36 | 37 | // The xchg is atomic 38 | // TODO: is locked value correctly changed? 39 | while (x86.xchg(&self.locked, 1) != 0) {} 40 | 41 | self.cpu = proc.mycpu(); 42 | // self.getcallerpcs(); 43 | } 44 | 45 | pub fn release(self: *Self) void { 46 | if (!self.hoding()) { 47 | asm volatile ("1: jmp 1b"); // TODO: handle error 48 | } 49 | 50 | self.pcs[0] = 0; 51 | self.cpu = null; 52 | 53 | asm volatile ("movl $0, (%[addr])" 54 | : 55 | : [addr] "r" (&self.locked), 56 | : "memory" 57 | ); 58 | 59 | popcli(); 60 | } 61 | 62 | // Check whether this cpu is holding the lock. 63 | pub fn hoding(self: *Self) bool { 64 | var r: bool = undefined; 65 | pushcli(); 66 | r = self.locked != 0 and self.cpu == proc.mycpu(); 67 | popcli(); 68 | return r; 69 | } 70 | 71 | // TODO: This function might be broken. 72 | // When file.devsw is set in consoleinit or use kbdintr, 73 | // this function causes fault. 74 | // (It might fail when we handle function pointer.) 75 | fn getcallerpcs(self: *Self) void { 76 | var ebp_addr = asm ("mov %%ebp, %%eax" 77 | : [ret] "={eax}" (-> usize), 78 | ); 79 | var i: u32 = 0; 80 | while (i < 10) : (i += 1) { 81 | if (ebp_addr == 0 or ebp_addr < memlayout.KERNBASE or ebp_addr == 0xffffffff) { 82 | break; 83 | } 84 | const ebp = @as([*]usize, @ptrFromInt(ebp_addr)); // fault here 85 | self.*.pcs[i] = ebp[1]; // saved %eip 86 | ebp_addr = ebp[0]; // saved %ebp 87 | } 88 | while (i < 10) : (i += 1) { 89 | self.*.pcs[i] = 0; 90 | } 91 | } 92 | }; 93 | 94 | pub fn pushcli() void { 95 | const eflags = x86.readeflags(); 96 | x86.cli(); 97 | const mycpu = proc.mycpu(); 98 | if (mycpu.ncli == 0) { 99 | mycpu.*.intena = eflags & mmu.FL_IF != 0; 100 | } 101 | mycpu.*.ncli += 1; 102 | } 103 | 104 | pub fn popcli() void { 105 | if (x86.readeflags() & mmu.FL_IF != 0) { 106 | asm volatile ("1: jmp 1b"); // TODO: handle error 107 | } 108 | const mycpu = proc.mycpu(); 109 | mycpu.*.ncli -= 1; 110 | if (mycpu.ncli < 0) { 111 | asm volatile ("1: jmp 1b"); // TODO: handle error 112 | } 113 | if (mycpu.ncli == 0 and mycpu.intena) { 114 | x86.sti(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/ide.zig: -------------------------------------------------------------------------------- 1 | // Simple PIO-based (non-DMA) IDE driver code. 2 | 3 | const bio = @import("bio.zig"); 4 | const fs = @import("fs.zig"); 5 | const ioapic = @import("ioapic.zig"); 6 | const mp = @import("mp.zig"); 7 | const param = @import("param.zig"); 8 | const proc = @import("proc.zig"); 9 | const spinlock = @import("spinlock.zig"); 10 | const trap = @import("trap.zig"); 11 | const x86 = @import("x86.zig"); 12 | 13 | const SECTOR_SIZE = 512; 14 | const IDE_BSY = 0x80; 15 | const IDE_DRDY = 0x40; 16 | const IDE_DF = 0x20; 17 | const IDE_ERR = 0x01; 18 | 19 | const IDE_CMD_READ = 0x20; 20 | const IDE_CMD_WRITE = 0x30; 21 | const IDE_CMD_RDMUL = 0xc4; 22 | const IDE_CMD_WRMUL = 0xc5; 23 | 24 | var idelock = spinlock.spinlock.init("ide"); 25 | var idequeue: ?*bio.buf = undefined; 26 | 27 | var havedisk1: bool = false; 28 | 29 | fn idewait(checkerr: bool) ?void { 30 | var r: u8 = undefined; 31 | while (true) { 32 | r = x86.in(u8, 0x1f7); 33 | if ((r & (IDE_BSY | IDE_DRDY)) == IDE_DRDY) { 34 | break; 35 | } 36 | } 37 | 38 | if (checkerr and (r & (IDE_DF | IDE_ERR)) != 0) { 39 | return null; 40 | } 41 | } 42 | 43 | pub fn ideinit() void { 44 | ioapic.ioapicenable(trap.IRQ_IDE, mp.ncpu); 45 | idewait(false) orelse unreachable; 46 | 47 | // Check if disk 1 is present 48 | x86.out(0x1f6, @as(u8, 0xe0 | @as(u8, 1 << 4))); 49 | var i: u32 = 0; 50 | while (i < 1000) : (i += 1) { 51 | if (x86.in(u8, 0x1f7) != 0) { 52 | havedisk1 = true; 53 | break; 54 | } 55 | } 56 | 57 | // Switch back to disk 0. 58 | x86.out(0x1f6, @as(u8, 0xe0 | @as(u8, 0 << 4))); 59 | } 60 | 61 | // Start request for b. Caller must hold idelock. 62 | fn idestart(b: *bio.buf) void { 63 | if (b.blockno >= param.FSSIZE) { 64 | asm volatile ("1: jmp 1b"); // TODO: error handling 65 | } 66 | 67 | const sector_per_block: u8 = fs.BSIZE / fs.SECTOR_SIZE; 68 | const sector = b.blockno * sector_per_block; 69 | const readcmd: u8 = if (sector_per_block == 1) IDE_CMD_READ else IDE_CMD_RDMUL; 70 | const writecmd: u8 = if (sector_per_block == 1) IDE_CMD_WRITE else IDE_CMD_WRMUL; 71 | 72 | if (sector_per_block > 7) { 73 | asm volatile ("1: jmp 1b"); 74 | } 75 | 76 | idewait(false) orelse unreachable; 77 | x86.out(0x3f6, @as(u8, 0)); // generate interrupt 78 | x86.out(0x1f2, sector_per_block); // number of sectors 79 | x86.out(0x1f3, @as(u8, sector & 0xff)); 80 | x86.out(0x1f4, @as(u8, (sector >> 8) & 0xff)); 81 | x86.out(0x1f5, @as(u8, (sector >> 16) & 0xff)); 82 | x86.out(0x1f6, @as(u8, 0xe0) | @as(u8, (b.dev & 1) << 4) | @as(u8, (sector >> 24) & 0x0f)); 83 | if (b.flags & bio.B_DIRTY != 0) { 84 | x86.out(0x1f7, writecmd); 85 | x86.outsl(0x1f0, @intFromPtr(&b.data), fs.BSIZE / 4); 86 | } else { 87 | x86.out(0x1f7, readcmd); 88 | } 89 | } 90 | 91 | pub fn ideintr() void { 92 | idelock.acquire(); 93 | defer idelock.release(); 94 | 95 | const b = idequeue orelse return; 96 | idequeue = b.qnext; 97 | 98 | // Read data if needed 99 | if ((b.flags & bio.B_DIRTY) == 0 and idewait(true)) { 100 | x86.insl(0x1f0, @intFromPtr(&b.data), fs.BSIZE / 4); 101 | } 102 | 103 | b.*.flags |= bio.B_VALID; 104 | b.*.flags &= ~bio.B_DIRTY; 105 | proc.wakeup(@intFromPtr(b)); 106 | 107 | if (idequeue != null) { 108 | idestart(idequeue); 109 | } 110 | } 111 | 112 | pub fn iderw(b_arg: *bio.buf) void { 113 | var b = b_arg; 114 | // TODO: error handling 115 | if (!b.lock.hoding()) { 116 | asm volatile ("1: jmp 1b"); 117 | } 118 | if ((b.flags & (bio.B_VALID | bio.B_DIRTY)) == bio.B_VALID) { 119 | asm volatile ("1: jmp 1b"); 120 | } 121 | if (b.dev != 0 and !havedisk1) { 122 | asm volatile ("1: jmp 1b"); 123 | } 124 | 125 | // Append b to idequeue. 126 | b.qnext = null; 127 | var pp: **bio.buf = &idequeue; 128 | while (*pp) { 129 | pp = &pp.*.*.qnext; 130 | } 131 | 132 | pp.* = b; 133 | 134 | if (idequeue == b) { 135 | idestart(b); 136 | } 137 | 138 | while ((b.flags & (bio.B_VALID | bio.B_DIRTY)) != bio.B_VALID) { 139 | proc.sleep(@intFromPtr(b), &idelock); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/x86.zig: -------------------------------------------------------------------------------- 1 | const proc = @import("proc.zig"); 2 | 3 | pub extern fn swtch(old: **proc.context, new: *proc.context) callconv(.C) void; 4 | 5 | pub fn in(comptime Type: type, port: u16) Type { 6 | return switch (Type) { 7 | u8 => asm volatile ("inb %[port], %[result]" 8 | : [result] "={al}" (-> Type), 9 | : [port] "N{dx}" (port), 10 | ), 11 | u16 => asm volatile ("inw %[port], %[result]" 12 | : [result] "={ax}" (-> Type), 13 | : [port] "N{dx}" (port), 14 | ), 15 | u32 => asm volatile ("inl %[port], %[result]" 16 | : [result] "={eax}" (-> Type), 17 | : [port] "N{dx}" (port), 18 | ), 19 | else => @compileError("Invalid data type. Only u8, u16 or u32, found: " ++ @typeName(Type)), 20 | }; 21 | } 22 | 23 | pub fn insl(port: u16, addr: usize, cnt: u32) void { 24 | asm volatile ("cld; rep insl" 25 | : [a] "=D" (addr), 26 | [b] "=c" (cnt), 27 | : [c] "d" (port), 28 | [d] "0" (addr), 29 | [e] "1" (cnt), 30 | : "memory", "cc" 31 | ); 32 | } 33 | 34 | pub fn out(port: u16, data: anytype) void { 35 | switch (@TypeOf(data)) { 36 | u8 => asm volatile ("outb %[data], %[port]" 37 | : 38 | : [port] "{dx}" (port), 39 | [data] "{al}" (data), 40 | ), 41 | u16 => asm volatile ("outw %[data], %[port]" 42 | : 43 | : [port] "{dx}" (port), 44 | [data] "{ax}" (data), 45 | ), 46 | u32 => asm volatile ("outl %[data], %[port]" 47 | : 48 | : [port] "{dx}" (port), 49 | [data] "{eax}" (data), 50 | ), 51 | else => @compileError("Invalid data type. Only u8, u16 or u32, found: " ++ @typeName(@TypeOf(data))), 52 | } 53 | } 54 | 55 | pub fn outsl(port: u16, addr: usize, cnt: u32) void { 56 | asm volatile ("cld; rep outsl" 57 | : [a] "=S" (addr), 58 | [b] "=c" (cnt), 59 | : [c] "d" (port), 60 | [d] "0" (addr), 61 | [f] "1" (cnt), 62 | : "cc" 63 | ); 64 | } 65 | 66 | pub fn lgdt(p: usize, size: u16) void { 67 | const pd = [3]u16{ 68 | size - 1, @as(u16, @intCast(p & 0xffff)), @as(u16, @intCast(p >> 16)), 69 | }; 70 | 71 | asm volatile ("lgdt (%%eax)" 72 | : 73 | : [pd] "{eax}" (@intFromPtr(&pd)), 74 | ); 75 | } 76 | 77 | pub fn lidt(p: usize, size: u16) void { 78 | const pd = [3]u16{ 79 | size - 1, 80 | @as(u16, @intCast(p & 0xffff)), 81 | @as(u16, @intCast(p >> 16)), 82 | }; 83 | 84 | asm volatile ("lidt (%%eax)" 85 | : 86 | : [pd] "{eax}" (@intFromPtr(&pd)), 87 | ); 88 | } 89 | 90 | pub fn lcr3(addr: usize) void { 91 | asm volatile ("movl %[addr], %%cr3" 92 | : 93 | : [addr] "{eax}" (addr), 94 | ); 95 | } 96 | 97 | pub fn ltr(sel: u16) void { 98 | asm volatile ("ltr %[sel]" 99 | : 100 | : [sel] "r" (sel), 101 | ); 102 | } 103 | 104 | pub fn readeflags() u32 { 105 | return asm volatile ("pushfl; popl %[eflags]" 106 | : [eflags] "={eax}" (-> u32), 107 | ); 108 | } 109 | 110 | pub fn cli() void { 111 | asm volatile ("cli"); 112 | } 113 | 114 | pub fn sti() void { 115 | asm volatile ("sti"); 116 | } 117 | 118 | pub fn xchg(addr: *u32, newval: u32) u32 { 119 | return asm volatile ("lock; xchgl (%[addr]), %[newval]" 120 | : [result] "={eax}" (-> u32), 121 | : [addr] "r" (addr), 122 | [newval] "{eax}" (newval), 123 | : "memory" 124 | ); 125 | } 126 | 127 | // Layout of the trap frame built on the stack by the 128 | // hardware and by trapasm.S, and passed to trap(). 129 | pub const trapframe = packed struct { 130 | edi: u32, 131 | esi: u32, 132 | ebp: u32, 133 | oesp: u32, // useless & ignored 134 | ebx: u32, 135 | edx: u32, 136 | ecx: u32, 137 | eax: u32, 138 | 139 | // rest of trap frame 140 | gs: u16, 141 | padding1: u16, 142 | fs: u16, 143 | padding2: u16, 144 | es: u16, 145 | padding3: u16, 146 | ds: u16, 147 | padding4: u16, 148 | trapno: u32, 149 | 150 | // below here defined by x86 hardware 151 | err: u32, 152 | eip: u32, 153 | cs: u16, 154 | padding5: u16, 155 | eflags: u32, 156 | 157 | // below here only when crossing rings, such as from user to kernel 158 | esp: u32, 159 | ss: u16, 160 | padding6: u16, 161 | }; 162 | -------------------------------------------------------------------------------- /src/bio.zig: -------------------------------------------------------------------------------- 1 | // Buffer cache. 2 | // 3 | // The buffer cache is a linked list of buf structures holding 4 | // cached copies of disk block contents. Caching disk blocks 5 | // in memory reduces the number of disk reads and also provides 6 | // a synchronization point for disk blocks used by multiple processes. 7 | // 8 | // Interface: 9 | // * To get a buffer for a particular disk block, call bread. 10 | // * After changing buffer data, call bwrite to write it to disk. 11 | // * When done with the buffer, call brelse. 12 | // * Do not use the buffer after calling brelse. 13 | // * Only one process at a time can use a buffer, 14 | // so do not keep them longer than necessary. 15 | // 16 | // The implementation uses two state flags internally: 17 | // * B_VALID: the buffer data has been read from the disk. 18 | // * B_DIRTY: the buffer data has been modified 19 | // and needs to be written to disk. 20 | 21 | const fs = @import("fs.zig"); 22 | const ide = @import("ide.zig"); 23 | const param = @import("param.zig"); 24 | const sleeplock = @import("sleeplock.zig"); 25 | const spinlock = @import("spinlock.zig"); 26 | 27 | pub const buf = struct { 28 | flags: u32, 29 | dev: u32, 30 | blockno: u32, 31 | lock: sleeplock.sleeplock, 32 | refcnt: u32, 33 | prev: *buf, // LRU cache list 34 | next: *buf, 35 | qnext: ?*buf, // disk queue 36 | data: [fs.BSIZE]u8, 37 | 38 | const Self = @This(); 39 | 40 | fn is_used(self: *Self) bool { 41 | return self.refcnt != 0 or (self.flags & B_DIRTY) != 0; 42 | } 43 | 44 | pub fn read(dev: u32, blockno: u32) *Self { 45 | const b = bget(dev, blockno); 46 | if ((b.flags & B_VALID) == 0) { 47 | ide.iderw(b); 48 | } 49 | return b; 50 | } 51 | 52 | pub fn write(self: *Self) void { 53 | if (!self.lock.holding()) { 54 | asm volatile ("1: jmp 1b"); // TODO: error handling 55 | } 56 | self.flags |= B_DIRTY; 57 | ide.iderw(self); 58 | } 59 | 60 | pub fn release(self: *Self) void { 61 | if (!self.lock.holding()) { 62 | asm volatile ("1: jmp 1b"); // TODO: error handling 63 | } 64 | 65 | self.lock.release(); 66 | 67 | bcache.lock.acquire(); 68 | defer bcache.lock.release(); 69 | self.*.refcnt -= 1; 70 | if (self.refcnt == 0) { 71 | // no onw is wating for it. 72 | self.*.next.*.prev = self.prev; 73 | self.*.prev.*.next = self.next; 74 | self.*.next = bcache.head.next; 75 | self.*.prev = &bcache.head; 76 | bcache.head.next.*.prev = self; 77 | bcache.head.next = self; 78 | } 79 | } 80 | 81 | pub fn data_ptr(self: *Self) [*]u8 { 82 | return @as([*]u8, @ptrCast(&self.data)); 83 | } 84 | }; 85 | 86 | pub const B_VALID = 0x2; // buffer has been read from disk 87 | pub const B_DIRTY = 0x4; // buffer needs to be written to disk 88 | 89 | var bcache = struct { 90 | lock: spinlock.spinlock, 91 | buf: [param.NBUF]buf, 92 | head: buf, 93 | }{ 94 | .lock = spinlock.spinlock.init("bcache"), 95 | .buf = undefined, 96 | .head = undefined, 97 | }; 98 | 99 | pub fn binit() void { 100 | //Create list of buffers 101 | bcache.head.prev = &bcache.head; 102 | bcache.head.next = &bcache.head; 103 | for (&bcache.buf) |*b| { 104 | b.*.next = bcache.head.next; 105 | b.*.prev = &bcache.head; 106 | b.*.lock = sleeplock.sleeplock.init("buffer"); 107 | bcache.head.next.*.prev = b; 108 | bcache.head.next = b; 109 | } 110 | } 111 | 112 | // Look through buffer cache for block on device dev. 113 | // If not found, allocate a buffer. 114 | // In either case, return locked buffer. 115 | fn bget(dev: u32, blockno: u32) *buf { 116 | bcache.lock.acquire(); 117 | defer bcache.lock.release(); 118 | 119 | // Is the block already cached? 120 | var b = bcache.head.next; 121 | while (b != &bcache.head) : (b = b.next) { 122 | if (b.dev == dev and b.blockno == blockno) { 123 | b.*.refcnt += 1; 124 | return b; 125 | } 126 | } 127 | 128 | // Not cached; recycle an unused buffer. 129 | // Even if refcnt==0, B_DIRTY indicates a buffer is in use 130 | // because log.c has modified it but not yet committed it. 131 | b = bcache.head.prev; 132 | while (b != &bcache.head) : (b = b.prev) { 133 | if (!b.is_used()) { 134 | b.*.dev = dev; 135 | b.*.blockno = blockno; 136 | b.*.flags = 0; 137 | b.*.refcnt = 1; 138 | b.lock.acquire(); 139 | return b; 140 | } 141 | } 142 | 143 | asm volatile ("1: jmp 1b"); // TODO: error handling 144 | unreachable; 145 | } 146 | -------------------------------------------------------------------------------- /src/log.zig: -------------------------------------------------------------------------------- 1 | const bio = @import("bio.zig"); 2 | const fs = @import("fs.zig"); 3 | const param = @import("param.zig"); 4 | const proc = @import("proc.zig"); 5 | const spinlock = @import("spinlock.zig"); 6 | 7 | // Simple logging that allows concurrent FS system calls. 8 | // 9 | // A log transaction contains the updates of multiple FS system 10 | // calls. The logging system only commits when there are 11 | // no FS system calls active. Thus there is never 12 | // any reasoning required about whether a commit might 13 | // write an uncommitted system call's updates to disk. 14 | // 15 | // A system call should call begin_op()/end_op() to mark 16 | // its start and end. Usually begin_op() just increments 17 | // the count of in-progress FS system calls and returns. 18 | // But if it thinks the log is close to running out, it 19 | // sleeps until the last outstanding end_op() commits. 20 | // 21 | // The log is a physical re-do log containing disk blocks. 22 | // The on-disk log format: 23 | // header block, containing block #s for block A, B, C, ... 24 | // block A 25 | // block B 26 | // block C 27 | // ... 28 | // Log appends are synchronous. 29 | 30 | const LogHeader = struct { 31 | n: i32, // number of blocks 32 | block: [param.LOGSIZE]i32, // disk block numbers 33 | }; 34 | 35 | const Log = struct { 36 | lock: spinlock.spinlock, 37 | start: i32, // block number of first log block 38 | size: i32, // number of log blocks 39 | outstanding: i32, // how many FS sys calls are executing. 40 | committing: bool = false, // in commit(), please wait. 41 | dev: i32, 42 | lh: LogHeader, 43 | }; 44 | 45 | var log: Log = undefined; 46 | 47 | pub fn initlog(dev: i32) void { 48 | if (@sizeOf(LogHeader) >= fs.BSIZE) { 49 | bio.panic("initlog: too big log"); 50 | } 51 | 52 | var sb = fs.superblock{}; 53 | log.lock = spinlock.init("log"); 54 | fs.readsb(dev, &sb); 55 | log.start = sb.logstart; 56 | log.size = sb.nlog; 57 | log.dev = dev; 58 | 59 | // recover_from_log(); 60 | } 61 | 62 | // Copy committed blocks from log to their home location 63 | fn install_trans() void { 64 | for (0..log.lh.n) |tail| { 65 | const log_buf = bio.buf.read(log.dev, log.start + tail + 1); 66 | var dst_buf = bio.buf.read(log.dev, log.lh.block[tail]); 67 | @memcpy(dst_buf.data, log_buf.data); 68 | dst_buf.write(); 69 | dst_buf.release(); 70 | log_buf.release(); 71 | } 72 | } 73 | 74 | // Read the log header from disk into the in-memory log header 75 | fn read_head() void { 76 | var buf = bio.buf.read(log.dev, log.start); 77 | var lh = @as(*LogHeader, @ptrCast(&buf.data)); 78 | log.lh.n = lh.n; 79 | for (&log.lh.block, 0..) |*b, i| { 80 | b.* = lh.block[i]; 81 | } 82 | buf.release(); 83 | } 84 | 85 | // Write in-memory log header to disk. 86 | // This is the true point at which the 87 | // current transaction commits. 88 | fn write_head() void { 89 | var buf = bio.buf.read(log.dev, log.start); 90 | var hb = @as(*LogHeader, @ptrCast(&buf.data)); 91 | hb.n = log.lh.n; 92 | for (&hb.block, 0..) |*b, i| { 93 | b.* = log.lh.block[i]; 94 | } 95 | buf.write(); 96 | buf.release(); 97 | } 98 | 99 | pub fn recover_from_log() void { 100 | read_head(); 101 | install_trans(); // if commited, copy from log to disk 102 | log.lh.n = 0; 103 | write_head(); // clear the log 104 | } 105 | 106 | // called at the start of each FS system call. 107 | pub fn begin_op() void { 108 | log.lock.acquire(); 109 | while (true) { 110 | if (log.committing) { 111 | proc.sleep(@intFromPtr(log), &log.lock); 112 | } else if (log.lh.n + (log.outstanding + 1) * param.MAXOPBLOCKS > param.LOGSIZE) { 113 | // this op might exhaust log space; wait for commit. 114 | proc.sleep(@intFromPtr(log), &log.lock); 115 | } else { 116 | log.outstanding += 1; 117 | log.lock.release(); 118 | break; 119 | } 120 | } 121 | } 122 | 123 | // called at the end of each FS system call. 124 | // commits if this was the last outstanding operation. 125 | pub fn end_op() void { 126 | var do_commit = false; 127 | log.lock.acquire(); 128 | if (log.committing) { 129 | @panic("log.commiting"); 130 | } 131 | if (log.outstanding == 0) { 132 | do_commit = true; 133 | log.committing = true; 134 | } else { 135 | proc.wakeup(@intFromPtr(&log)); 136 | } 137 | log.lock.release(); 138 | 139 | if (do_commit) { 140 | // commit() 141 | log.lock.acquire(); 142 | log.committing = false; 143 | proc.wakeup(@intFromPtr(&log)); 144 | log.lock.release(); 145 | } 146 | } 147 | 148 | // Copy modified blocks from cache to log. 149 | fn write_log() void { 150 | for (0..log.lh.n) |tail| { 151 | var to = bio.buf.read(log.dev, log.start + tail + 1); // log block 152 | var from = bio.buf.read(log.dev, log.lh.block[tail]); // cache block 153 | @memcpy(to.data, from.data); 154 | to.write(); 155 | to.release(); 156 | from.release(); 157 | } 158 | } 159 | 160 | fn commit() void { 161 | if (log.lh.n > 0) { 162 | write_log(); 163 | write_head(); 164 | install_trans(); 165 | log.lh.n = 0; 166 | write_head(); 167 | } 168 | } 169 | 170 | // Caller has modified b->data and is done with the buffer. 171 | // Record the block number and pin in the cache with B_DIRTY. 172 | // commit()/write_log() will do the disk write. 173 | // 174 | // log_write() replaces bwrite(); a typical use is: 175 | // bp = bread(...) 176 | // modify bp->data[] 177 | // log_write(bp) 178 | // brelse(bp) 179 | pub fn log_write(b: *bio.buf) void { 180 | if (log.lh.n >= param.LOGSIZE or log.lh.n >= log.size - 1) { 181 | @panic("too big transaction"); 182 | } 183 | if (log.outstanding < 1) { 184 | @panic("log_write outside of trans"); 185 | } 186 | 187 | log.lock.acquire(); 188 | defer log.lock.release(); 189 | var idx: usize = 0; 190 | while (idx < log.lh.n) : (idx += 1) { 191 | if (log.lh.block[idx] == b.blockno) { 192 | break; 193 | } 194 | } 195 | 196 | log.lh.block[idx] = b.blockno; 197 | if (idx == log.lh.n) { 198 | log.lh.n += 1; 199 | } 200 | 201 | b.flags |= bio.B_DIRTY; 202 | } 203 | -------------------------------------------------------------------------------- /src/mp.zig: -------------------------------------------------------------------------------- 1 | const lapic = @import("lapic.zig"); 2 | const memlayout = @import("memlayout.zig"); 3 | const mmu = @import("mmu.zig"); 4 | const param = @import("param.zig"); 5 | const proc = @import("proc.zig"); 6 | const x86 = @import("x86.zig"); 7 | 8 | pub var cpus: [param.NCPU]proc.cpu = undefined; // TODO: properly initialize 9 | pub var ncpu: u8 = 0; 10 | pub var ioapicid: u8 = 0; 11 | 12 | const mp = packed struct { 13 | signature1: u8 = 0, // "_" 14 | signature2: u8 = 0, // "M" 15 | signature3: u8 = 0, // "P" 16 | signature4: u8 = 0, // "_" 17 | physaddr: u32 = 0, // phys addr of MP config table 18 | length: u8 = 0, // 1 19 | specrev: u8 = 0, // [14] 20 | checksum: u8 = 0, // all bytes must add up to 0 21 | typ: u8 = 0, // MP system config type 22 | imcrp: u8 = 0, 23 | reserved: u24 = 0, 24 | 25 | const Self = @This(); 26 | 27 | fn isValid(self: *Self) bool { 28 | // check signature 29 | if (self.signature1 != '_' or 30 | self.signature2 != 'M' or 31 | self.signature3 != 'P' or 32 | self.signature4 != '_') 33 | { 34 | return false; 35 | } 36 | 37 | // checksum 38 | const bytes = @as([*]const u8, @ptrCast(self))[0..@sizeOf(Self)]; 39 | var sum: u8 = 0; 40 | for (bytes) |*b| { 41 | sum = sum +% b.*; 42 | } 43 | 44 | return sum == 0; 45 | } 46 | }; 47 | 48 | const mpconf = packed struct { 49 | signature1: u8, 50 | signature2: u8, 51 | signature3: u8, 52 | signature4: u8, 53 | length: u16, 54 | version: u8, 55 | checksum: u8, 56 | product: u160, 57 | oemtable: *u32, 58 | oemlength: u16, 59 | entry: u16, 60 | lapicaddr: [*]u32, 61 | xlength: u16, 62 | xchecksum: u8, 63 | reserved: u8, 64 | 65 | const Self = @This(); 66 | 67 | fn isValid(self: *Self) bool { 68 | if (self.signature1 != 'P' or 69 | self.signature2 != 'C' or 70 | self.signature3 != 'M' or 71 | self.signature4 != 'P') 72 | { 73 | return false; 74 | } 75 | 76 | if (self.version != 1 and self.version != 4) { 77 | return false; 78 | } 79 | 80 | // checksum 81 | const bytes = @as([*]const u8, @ptrCast(self))[0..self.length]; 82 | var sum: u8 = 0; 83 | for (bytes) |*b| { 84 | sum = sum +% b.*; 85 | } 86 | 87 | return sum == 0; 88 | } 89 | }; 90 | 91 | // processor table entry 92 | const mpproc = packed struct { 93 | typ: entry, // entry type (0) 94 | apicid: u8, // local APIC id 95 | version: u8, // local APIC version 96 | flags: u8, // CPU flags 97 | signature: u32, // CPU signature 98 | feature: u32, // feature flags from CPUID instruction 99 | reserved: u64, 100 | }; 101 | 102 | // I/O APIC table entry 103 | const mpioapic = packed struct { 104 | typ: entry, // entry type (2) 105 | apicno: u8, // I/O APIC id 106 | version: u8, // I/O APIC version 107 | flags: u8, // I/O APIC flags 108 | addr: *u32, // I/O APIC address 109 | }; 110 | 111 | // Non-exhaustive enum may be better 112 | const entry = enum(u8) { 113 | MPPROC = 0x00, 114 | MPBUS = 0x01, 115 | MPIOAPIC = 0x02, 116 | MPIOINTR = 0x03, 117 | MPLINTR = 0x04, 118 | }; 119 | 120 | // Look for an MP structure in the len bytes at addr 121 | fn mpsearch1(a: usize, len: usize) ?*mp { 122 | const addr = memlayout.p2v(a); 123 | const slice = @as([*]mp, @ptrFromInt(addr))[0 .. len / @sizeOf(mp)]; 124 | for (slice) |*p| { 125 | if (p.isValid()) { 126 | return p; 127 | } 128 | } 129 | 130 | return null; 131 | } 132 | 133 | // Search for the MP Floating Pointer Structure, which according to the 134 | // spec is in one of the following three locations: 135 | // 1) in the first KB of the EBDA; 136 | // 2) in the last KB of system base memory; 137 | // 3) in the BIOS ROM between 0xE0000 and 0xFFFFF. 138 | fn mpsearch() ?*mp { 139 | const bda = @as([*]u8, @ptrFromInt(memlayout.p2v(0x400))); 140 | 141 | var p: usize = ((@as(usize, @intCast(bda[0x0F])) << 8) | @as(usize, @intCast(bda[0x0E]))) << 4; 142 | var result = mpsearch1(p, 1024); 143 | if (result) |m| { 144 | return m; 145 | } 146 | 147 | p = ((@as(usize, @intCast(bda[0x14])) << 8) | @as(usize, @intCast(bda[0x13]))) * 1024; 148 | result = mpsearch1(p - 1024, 1024); 149 | if (result) |m| { 150 | return m; 151 | } 152 | 153 | return mpsearch1(0xF0000, 0x10000); 154 | } 155 | 156 | // Search for an MP configuration table. For now, 157 | // don't accept the default configurations (physaddr == 0). 158 | // Check for correct signature, calculate the checksum and, 159 | // if correct, check the version. 160 | // To do: check extended table checksum. 161 | fn mpconfig(p: **mp) ?*mpconf { 162 | const pmp = mpsearch() orelse return null; 163 | if (pmp.physaddr == 0) { 164 | return null; 165 | } 166 | 167 | var conf = @as(*mpconf, @ptrFromInt(memlayout.p2v(pmp.physaddr))); 168 | if (!conf.isValid()) { 169 | return null; 170 | } 171 | 172 | const ppmp = p; 173 | ppmp.* = pmp; 174 | 175 | return conf; 176 | } 177 | 178 | pub fn mpinit() void { 179 | var pmp: *mp = undefined; 180 | const conf = mpconfig(&pmp) orelse return; // TODO: panic if null 181 | 182 | lapic.lapic = conf.lapicaddr; 183 | 184 | var p = @intFromPtr(conf) + @sizeOf(mpconf); 185 | const e = @intFromPtr(conf) + conf.length; 186 | while (p < e) { 187 | const typ = @as(entry, @enumFromInt(@as(*u8, @ptrFromInt(p)).*)); 188 | switch (typ) { 189 | .MPPROC => { 190 | const proc_entry = @as(*mpproc, @ptrFromInt(p)); 191 | if (ncpu < param.NCPU) { 192 | cpus[ncpu].apicid = proc_entry.apicid; 193 | ncpu += 1; 194 | } 195 | p += @sizeOf(mpproc); 196 | }, 197 | .MPIOAPIC => { 198 | const ioapic_entry = @as(*mpioapic, @ptrFromInt(p)); 199 | ioapicid = ioapic_entry.apicno; 200 | p += @sizeOf(mpioapic); 201 | }, 202 | .MPBUS, .MPIOINTR, .MPLINTR => { 203 | p += 8; 204 | }, 205 | } 206 | } 207 | 208 | if (pmp.imcrp != 0) { 209 | // Bochs doesn't support IMCR, so this doesn't run on Bochs. 210 | // But it would on real hardware. 211 | x86.out(0x22, @as(u8, 0x70)); // Select IMCR 212 | x86.out(0x23, x86.in(u8, 0x23) | 1); // Mask external interrupts 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/kbd.zig: -------------------------------------------------------------------------------- 1 | const ascii = @import("std").ascii; 2 | const ctrl_code = @import("std").ascii.control_code; 3 | const console = @import("console.zig"); 4 | const x86 = @import("x86.zig"); 5 | 6 | // Control characters 7 | const bs = ctrl_code.bs; // '\b' 8 | const ht = ctrl_code.ht; // '\t' 9 | 10 | // PC keyboard interface constants 11 | 12 | const KBSTATP = 0x64; // kbd controller status port(I) 13 | const KBS_DIB = 0x01; // kbd data in buffer 14 | const KBDATAP = 0x60; // kbd data port(I) 15 | 16 | const NO = 0; 17 | 18 | const SHIFT: u8 = (1 << 0); 19 | const CTL: u8 = (1 << 1); 20 | const ALT: u8 = (1 << 2); 21 | 22 | const CAPSLOCK: u8 = (1 << 3); 23 | const NUMLOCK: u8 = (1 << 4); 24 | const SCROLLLOCK: u8 = (1 << 5); 25 | 26 | const E0ESC: u8 = (1 << 6); 27 | 28 | // Special keycodes 29 | const KEY_HOME = 0xE0; 30 | const KEY_END = 0xE1; 31 | const KEY_UP = 0xE2; 32 | const KEY_DN = 0xE3; 33 | const KEY_LF = 0xE4; 34 | const KEY_RT = 0xE5; 35 | const KEY_PGUP = 0xE6; 36 | const KEY_PGDN = 0xE7; 37 | const KEY_INS = 0xE8; 38 | const KEY_DEL = 0xE9; 39 | 40 | const shiftcode: [256]u8 = init: { 41 | var initial_value = [1]u8{NO} ** 256; 42 | initial_value[0x1D] = CTL; 43 | initial_value[0x2A] = SHIFT; 44 | initial_value[0x36] = SHIFT; 45 | initial_value[0x38] = ALT; 46 | initial_value[0x9D] = CTL; 47 | initial_value[0xB8] = ALT; 48 | break :init initial_value; 49 | }; 50 | 51 | const togglecode: [256]u8 = init: { 52 | var initial_value = [1]u8{NO} ** 256; 53 | initial_value[0x3A] = CAPSLOCK; 54 | initial_value[0x45] = NUMLOCK; 55 | initial_value[0x46] = SCROLLLOCK; 56 | break :init initial_value; 57 | }; 58 | 59 | const normalmap: [256]u8 = init: { 60 | var initial_value = [0x58]u8{ 61 | NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00 62 | '7', '8', '9', '0', '-', '=', bs, ht, 63 | 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10 64 | 'o', 'p', '[', ']', '\n', NO, 'a', 's', 65 | 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20 66 | '\'', '`', NO, '\\', 'z', 'x', 'c', 'v', 67 | 'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30 68 | NO, ' ', NO, NO, NO, NO, NO, NO, 69 | NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 70 | '8', '9', '-', '4', '5', '6', '+', '1', 71 | '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 72 | } ++ ([1]u8{NO} ** (256 - 0x58)); 73 | initial_value[0x9C] = '\n'; // KP_Enter 74 | initial_value[0xB5] = '/'; // KP_Div 75 | initial_value[0xC8] = KEY_UP; 76 | initial_value[0xD0] = KEY_DN; 77 | initial_value[0xC9] = KEY_PGUP; 78 | initial_value[0xD1] = KEY_PGDN; 79 | initial_value[0xCB] = KEY_LF; 80 | initial_value[0xCD] = KEY_RT; 81 | initial_value[0x97] = KEY_HOME; 82 | initial_value[0xCF] = KEY_END; 83 | initial_value[0xD2] = KEY_INS; 84 | initial_value[0xD3] = KEY_DEL; 85 | break :init initial_value; 86 | }; 87 | 88 | const shiftmap: [256]u8 = init: { 89 | var initial_value = [0x58]u8{ 90 | NO, 0o33, '!', '@', '#', '$', '%', '^', // 0x00 91 | '&', '*', '(', ')', '_', '+', bs, ht, 92 | 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10 93 | 'O', 'P', '{', '}', '\n', NO, 'A', 'S', 94 | 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20 95 | '"', '~', NO, '|', 'Z', 'X', 'C', 'V', 96 | 'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30 97 | NO, ' ', NO, NO, NO, NO, NO, NO, 98 | NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 99 | '8', '9', '-', '4', '5', '6', '+', '1', 100 | '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 101 | } ++ ([1]u8{NO} ** (256 - 0x58)); 102 | initial_value[0x9C] = '\n'; // KP_Enter 103 | initial_value[0xB5] = '/'; // KP_Div 104 | initial_value[0xC8] = KEY_UP; 105 | initial_value[0xD0] = KEY_DN; 106 | initial_value[0xC9] = KEY_PGUP; 107 | initial_value[0xD1] = KEY_PGDN; 108 | initial_value[0xCB] = KEY_LF; 109 | initial_value[0xCD] = KEY_RT; 110 | initial_value[0x97] = KEY_HOME; 111 | initial_value[0xCF] = KEY_END; 112 | initial_value[0xD2] = KEY_INS; 113 | initial_value[0xD3] = KEY_DEL; 114 | break :init initial_value; 115 | }; 116 | 117 | const ctlmap: [256]u8 = init: { 118 | var initial_value = [0x38]u8{ 119 | NO, NO, NO, NO, NO, NO, NO, NO, 120 | NO, NO, NO, NO, NO, NO, NO, NO, 121 | ctrl('Q'), ctrl('W'), ctrl('E'), ctrl('R'), ctrl('T'), ctrl('Y'), ctrl('U'), ctrl('I'), 122 | ctrl('O'), ctrl('P'), NO, NO, '\r', NO, ctrl('A'), ctrl('S'), 123 | ctrl('D'), ctrl('F'), ctrl('G'), ctrl('H'), ctrl('J'), ctrl('K'), ctrl('L'), NO, 124 | NO, NO, NO, ctrl('\\'), ctrl('Z'), ctrl('X'), ctrl('C'), ctrl('V'), 125 | ctrl('B'), ctrl('N'), ctrl('M'), NO, NO, ctrl('/'), NO, NO, 126 | } ++ ([1]u8{NO} ** (256 - 0x38)); 127 | initial_value[0x9C] = '\r'; // KP_Enter 128 | initial_value[0xB5] = ctrl('/'); // KP_Div 129 | initial_value[0xC8] = KEY_UP; 130 | initial_value[0xD0] = KEY_DN; 131 | initial_value[0xC9] = KEY_PGUP; 132 | initial_value[0xD1] = KEY_PGDN; 133 | initial_value[0xCB] = KEY_LF; 134 | initial_value[0xCD] = KEY_RT; 135 | initial_value[0x97] = KEY_HOME; 136 | initial_value[0xCF] = KEY_END; 137 | initial_value[0xD2] = KEY_INS; 138 | initial_value[0xD3] = KEY_DEL; 139 | break :init initial_value; 140 | }; 141 | 142 | const charcode = [4][256]u8{ 143 | normalmap, shiftmap, ctlmap, ctlmap, 144 | }; 145 | 146 | pub fn ctrl(x: u8) u8 { 147 | return x -% '@'; 148 | } 149 | 150 | // We could use std.ascii. 151 | fn getc() ?u8 { 152 | const static = struct { 153 | var shift: u8 = 0; 154 | }; 155 | 156 | const st = x86.in(u8, KBSTATP); 157 | if ((st & KBS_DIB) == 0) { 158 | return null; 159 | } 160 | var data = x86.in(u8, KBDATAP); 161 | 162 | if (data == 0xE0) { 163 | static.shift |= E0ESC; 164 | return 0; 165 | } else if (data & 0x80 != 0) { 166 | // Key released 167 | data = if (static.shift & E0ESC != 0) data else data & @as(u8, 0x7f); 168 | static.shift &= ~(shiftcode[data] | E0ESC); 169 | return 0; 170 | } else if (static.shift & E0ESC != 0) { 171 | data |= @as(u8, 0x80); 172 | static.shift &= ~E0ESC; 173 | } 174 | 175 | static.shift |= shiftcode[data]; 176 | static.shift ^= togglecode[data]; 177 | var c = charcode[static.shift & (CTL | SHIFT)][data]; 178 | if (static.shift & CAPSLOCK != 0) { 179 | if (ascii.isLower(c)) { 180 | c = ascii.toUpper(c); 181 | } else if (ascii.isUpper(c)) { 182 | c = ascii.toLower(c); 183 | } 184 | } 185 | return c; 186 | } 187 | 188 | pub fn kbdintr() void { 189 | console.consoleintr(getc); 190 | } 191 | -------------------------------------------------------------------------------- /src/vm.zig: -------------------------------------------------------------------------------- 1 | const kalloc = @import("kalloc.zig"); 2 | const memlayout = @import("memlayout.zig"); 3 | const mmu = @import("mmu.zig"); 4 | const mp = @import("mp.zig"); 5 | const sh = @import("sh.zig"); 6 | const spinlock = @import("spinlock.zig"); 7 | const param = @import("param.zig"); 8 | const proc = @import("proc.zig"); 9 | const util = @import("util.zig"); 10 | const x86 = @import("x86.zig"); 11 | 12 | extern const data: u8; // defined by kernel.ld 13 | pub var kpgdir: [*]mmu.pde_t = undefined; 14 | var count: u32 = 0; 15 | 16 | comptime { 17 | asm ( 18 | \\.global set_segreg; 19 | \\.type set_segreg, @function; 20 | \\set_segreg: 21 | \\ movl $0x10, %eax 22 | \\ movw %ax, %es 23 | \\ movw %ax, %ss 24 | \\ movw %ax, %ds 25 | \\ movw %ax, %fs 26 | \\ movw %ax, %gs 27 | \\ movl $0x08, %eax 28 | \\ movl $.next, %ecx 29 | \\ pushw %ax 30 | \\ pushl %ecx 31 | \\ lret 32 | \\.next: 33 | \\ movl %ebp, %esp 34 | \\ popl %ebp 35 | \\ ret 36 | ); 37 | } 38 | 39 | extern fn set_segreg() void; 40 | 41 | pub fn seginit() void { 42 | const c = &mp.cpus[proc.cpuid()]; 43 | if (proc.cpuid() == 1) { 44 | asm volatile ("1: jmp 1b"); 45 | } 46 | c.*.gdt[mmu.SEG_KCODE] = mmu.segdesc.new(mmu.STA_X | mmu.STA_R, 0, 0xffffffff, mmu.DPL_KERNEL); 47 | c.*.gdt[mmu.SEG_KDATA] = mmu.segdesc.new(mmu.STA_W, 0, 0xffffffff, mmu.DPL_KERNEL); 48 | c.*.gdt[mmu.SEG_UCODE] = mmu.segdesc.new(mmu.STA_X | mmu.STA_R, 0, 0xffffffff, mmu.DPL_USER); 49 | c.*.gdt[mmu.SEG_UDATA] = mmu.segdesc.new(mmu.STA_W, 0, 0xffffffff, mmu.DPL_USER); 50 | x86.lgdt(@intFromPtr(&c.*.gdt), @sizeOf(@TypeOf(c.*.gdt))); 51 | 52 | set_segreg(); 53 | } 54 | 55 | // Return the address of the PTE in page table pgdir 56 | // that corresponds to virtual address va. If alloc is true, 57 | // create any required page table pages. 58 | fn walkpgdir(pgdir: [*]mmu.pde_t, va: usize, alloc: bool) ?*mmu.pte_t { 59 | const pde = &pgdir[mmu.pdx(va)]; 60 | var pgtab: [*]mmu.pte_t = undefined; 61 | if (pde.* & mmu.PTE_P != 0) { 62 | pgtab = @as([*]mmu.pte_t, @ptrFromInt(memlayout.p2v(mmu.pteAddr(pde.*)))); 63 | } else { 64 | if (!alloc) { 65 | return null; 66 | } 67 | pgtab = @as([*]mmu.pte_t, @ptrFromInt(kalloc.kalloc() orelse return null)); 68 | // Make sure all those PTE_P bits are zero. 69 | for (@as([*]u8, @ptrCast(pgtab))[0..mmu.PGSIZE]) |*b| { 70 | b.* = 0; 71 | } 72 | // The permissions here are overly generous, but they can 73 | // be further restricted by the permissions in the page table 74 | // entries, if necessary. 75 | pde.* = memlayout.v2p(@intFromPtr(pgtab)) | mmu.PTE_P | mmu.PTE_W | mmu.PTE_U; 76 | } 77 | return &pgtab[mmu.ptx(va)]; 78 | } 79 | 80 | // Create PTEs for virtual addresses starting at va that refer to 81 | // physical addresses starting at pa. va and size might not 82 | // be page-aligned. 83 | fn mappages(pgdir: [*]mmu.pde_t, va: usize, size: usize, pa: usize, perm: usize) bool { 84 | var virt_addr = mmu.pgrounddown(va); 85 | var phys_addr = pa; 86 | const last = mmu.pgrounddown(va +% size -% 1); 87 | while (true) { 88 | const pte = walkpgdir(pgdir, virt_addr, true) orelse return false; 89 | if (pte.* & mmu.PTE_P != 0) { 90 | sh.panic("remap"); 91 | } 92 | pte.* = phys_addr | perm | mmu.PTE_P; 93 | if (virt_addr == last) { 94 | break; 95 | } 96 | virt_addr = virt_addr +% mmu.PGSIZE; 97 | phys_addr = phys_addr +% mmu.PGSIZE; 98 | } 99 | return true; 100 | } 101 | 102 | // Set up kernel part of a page table. 103 | pub fn setupkvm() ?[*]mmu.pde_t { 104 | const pgdir = @as([*]mmu.pde_t, @ptrFromInt(kalloc.kalloc() orelse return null)); 105 | for (@as([*]u8, @ptrCast(pgdir))[0..mmu.PGSIZE]) |*b| { 106 | b.* = 0; 107 | } 108 | if (memlayout.p2v(memlayout.PHYSTOP) > memlayout.DEVSPACE) { 109 | sh.panic("PHYSTOP too high"); 110 | } 111 | 112 | const data_addr = @intFromPtr(&data); 113 | 114 | // This table defines the kernel's mappings, which are present in 115 | // every process's page table. 116 | const kmap_t = struct { 117 | virt: usize, 118 | phys_start: usize, 119 | phys_end: usize, 120 | perm: usize, 121 | }; 122 | const kmap = [4]kmap_t{ 123 | kmap_t{ 124 | .virt = memlayout.KERNBASE, 125 | .phys_start = 0, 126 | .phys_end = memlayout.EXTMEM, 127 | .perm = mmu.PTE_W, 128 | }, 129 | kmap_t{ 130 | .virt = memlayout.KERNLINK, 131 | .phys_start = memlayout.v2p(memlayout.KERNLINK), 132 | .phys_end = memlayout.v2p(data_addr), 133 | .perm = 0, 134 | }, 135 | kmap_t{ 136 | .virt = data_addr, 137 | .phys_start = memlayout.v2p(data_addr), 138 | .phys_end = memlayout.PHYSTOP, 139 | .perm = mmu.PTE_W, 140 | }, 141 | kmap_t{ 142 | .virt = memlayout.DEVSPACE, 143 | .phys_start = memlayout.DEVSPACE, 144 | .phys_end = 0, 145 | .perm = mmu.PTE_W, 146 | }, 147 | }; 148 | 149 | for (&kmap) |*k| { 150 | const ok = mappages(pgdir, k.virt, k.phys_end -% k.phys_start, k.phys_start, k.perm); 151 | count += 1; 152 | if (ok == false) { 153 | // TODO: freevm(pgdir) 154 | return null; 155 | } 156 | } 157 | return pgdir; 158 | } 159 | 160 | // Allocate one page table for the machine for the kernel address 161 | // space for scheduler processes. 162 | pub fn kvmalloc() ?void { 163 | kpgdir = setupkvm() orelse return null; 164 | switchkvm(); 165 | } 166 | 167 | pub fn switchkvm() void { 168 | x86.lcr3(memlayout.v2p(@intFromPtr(kpgdir))); 169 | } 170 | 171 | pub fn switchuvm(p: *proc.proc) void { 172 | if (p.kstack == 0) { 173 | sh.panic("switchuvm: no kstack"); 174 | } 175 | 176 | spinlock.pushcli(); 177 | defer spinlock.popcli(); 178 | 179 | const cpu = proc.mycpu(); 180 | cpu.gdt[mmu.SEG_TSS] = mmu.segdesc.new16(mmu.STS_T32A, @as(u32, @intCast(@intFromPtr(&cpu.ts))), @sizeOf(mmu.taskstate) - 1, 0); 181 | cpu.gdt[mmu.SEG_TSS].s = 0; 182 | cpu.ts.ss0 = mmu.SEG_KDATA << 3; 183 | cpu.ts.esp0 = p.kstack + param.KSTACKSIZE; 184 | // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit 185 | // forbids I/O instructions (e.g., inb and outb) from user space 186 | cpu.ts.iomb = 0xFFFF; 187 | x86.ltr(mmu.SEG_TSS << 3); 188 | x86.lcr3(memlayout.v2p(@intFromPtr(p.pgdir))); 189 | } 190 | 191 | pub fn inituvm(pgdir: [*]mmu.pde_t, src: [*]u8, sz: usize) void { 192 | if (sz > mmu.PGSIZE) { 193 | sh.panic("inituvm: more than a page"); 194 | } 195 | 196 | const mem = kalloc.kalloc() orelse unreachable; 197 | @memset(@as([*]u8, @ptrFromInt(mem))[0..mmu.PGSIZE], 0); 198 | _ = mappages(pgdir, 0, mmu.PGSIZE, memlayout.v2p(mem), mmu.PTE_W | mmu.PTE_U); 199 | util.memmov(@as([*]u8, @ptrFromInt(mem)), src, sz); 200 | } 201 | -------------------------------------------------------------------------------- /src/mmu.zig: -------------------------------------------------------------------------------- 1 | // This file contains definitions for the 2 | // x86 memory management unit (MMU). 3 | 4 | // Eflags register 5 | pub const FL_IF = 0x00000200; // Interrupt Enable 6 | 7 | // Control Register flags 8 | pub const CR0_PE: usize = 0x00000001; // Protection Enable 9 | pub const CR0_WP: usize = 0x00010000; // Write Protect 10 | pub const CR0_PG: usize = 0x80000000; // Paging 11 | 12 | pub const CR4_PSE: usize = 0x00000010; // Page size extension 13 | 14 | // various segment selectors. 15 | pub const SEG_KCODE = 1; // kernel code 16 | pub const SEG_KDATA = 2; // kernel data+stack 17 | pub const SEG_UCODE = 3; // user code 18 | pub const SEG_UDATA = 4; // user data+stack 19 | pub const SEG_TSS = 5; // this process's task state 20 | 21 | // cpu->gdt[NSEGS] holds the above segments. 22 | pub const NSEGS = 6; 23 | 24 | // Segment Descriptor 25 | pub const segdesc = packed struct { 26 | lim_15_0: u16, // Low bits of segment limit 27 | base15_0: u16, // Low bits of segment base address 28 | base_23_16: u8, // Middle bits of segment base address 29 | typ: u4, // Segment type (see STS_ constants) 30 | s: u1, // 0 = system, 1 = application 31 | dpl: u2, // Descriptor Privilege Level 32 | p: u1, // Present 33 | lim_19_16: u4, // High bits of segment limit 34 | avl: u1, // Unused (available for software use) 35 | rsv1: u1, // Reserved 36 | db: u1, // 0 = 16-bit segment, 1 = 32-bit segment 37 | g: u1, // Granularity: limit scaled by 4K when set 38 | base_31_24: u8, // High bits of segment base address 39 | 40 | const Self = @This(); 41 | 42 | pub fn new(typ: u4, base: u32, lim: u32, dpl: u2) Self { 43 | return Self{ 44 | .lim_15_0 = @as(u16, @intCast((lim >> 12) & 0xffff)), 45 | .base15_0 = @as(u16, @intCast(base & 0xffff)), 46 | .base_23_16 = @as(u8, @intCast((base >> 16) & 0xff)), 47 | .typ = typ, 48 | .s = 1, 49 | .dpl = dpl, 50 | .p = 1, 51 | .lim_19_16 = @as(u4, @intCast((lim >> 28))), 52 | .avl = 0, 53 | .rsv1 = 0, 54 | .db = 1, 55 | .g = 1, 56 | .base_31_24 = @as(u8, @intCast(base >> 24)), 57 | }; 58 | } 59 | 60 | pub fn new16(typ: u4, base: u32, lim: u32, dpl: u2) Self { 61 | return Self{ 62 | .lim_15_0 = @as(u16, @intCast(lim & 0xffff)), 63 | .base15_0 = @as(u16, @intCast(base & 0xffff)), 64 | .base_23_16 = @as(u8, @intCast((base >> 16) & 0xff)), 65 | .typ = typ, 66 | .s = 1, 67 | .dpl = dpl, 68 | .p = 1, 69 | .lim_19_16 = @as(u4, @intCast((lim >> 16) & 0xf)), 70 | .avl = 0, 71 | .rsv1 = 0, 72 | .db = 1, 73 | .g = 0, 74 | .base_31_24 = @as(u8, @intCast(base >> 24)), 75 | }; 76 | } 77 | }; 78 | 79 | pub const DPL_KERNEL = 0x0; // Kernel DPL 80 | pub const DPL_USER = 0x3; // User DPL 81 | 82 | // Application segment type bits 83 | pub const STA_X: u4 = 0x8; // Executable segment 84 | pub const STA_W: u4 = 0x2; // Writeable (non-executable segments) 85 | pub const STA_R: u4 = 0x2; // Readable (executable segments) 86 | 87 | // System segment type bits 88 | pub const STS_T32A = 0x9; // Available 32-bit TSS 89 | pub const STS_IG32 = 0xE; // 32-bit Interrupt Gate 90 | pub const STS_TG32 = 0xF; // 32-bit Trap Gate 91 | 92 | // A virtual address 'la' has a three-part structure as follows: 93 | // 94 | // +--------10------+-------10-------+---------12----------+ 95 | // | Page Directory | Page Table | Offset within Page | 96 | // | Index | Index | | 97 | // +----------------+----------------+---------------------+ 98 | // \--- PDX(va) --/ \--- PTX(va) --/ 99 | 100 | // page directory index 101 | pub fn pdx(va: usize) usize { 102 | return (va >> PDXSHIFT) & 0x3FF; 103 | } 104 | 105 | // page table index 106 | pub fn ptx(va: usize) usize { 107 | return (va >> PTXSHIFT) & 0x3FF; 108 | } 109 | 110 | // Page directory and page table pub constants. 111 | pub const NPDENTRIES = 1024; // # directory entries per page directory 112 | pub const NPTENTRIES = 1024; // # PTEs per page table 113 | pub const PGSIZE: usize = 4096; // bytes mapped by a page 114 | 115 | pub const PTXSHIFT = 12; // offset of PTX in a linear address 116 | pub const PDXSHIFT = 22; // offset of PDX in a linear address 117 | 118 | pub fn pgroundup(sz: usize) usize { 119 | return ((sz) + PGSIZE - 1) & ~(PGSIZE - 1); 120 | } 121 | 122 | pub fn pgrounddown(sz: usize) usize { 123 | return (sz) & ~(PGSIZE - 1); 124 | } 125 | 126 | // Address in page table or page directory entry 127 | pub fn pteAddr(pte: usize) usize { 128 | return pte & ~@as(usize, 0xFFF); 129 | } 130 | 131 | pub fn pteFlags(pte: usize) usize { 132 | return pte & @as(usize, 0xFFF); 133 | } 134 | 135 | // Page table/directory entry flags. 136 | pub const PTE_P = 0x001; // Present 137 | pub const PTE_W = 0x002; // Writeable 138 | pub const PTE_U = 0x004; // User 139 | pub const PTE_PS = 0x080; // Page Size 140 | 141 | pub const pte_t = usize; 142 | pub const pde_t = usize; 143 | 144 | pub const taskstate = extern struct { 145 | link: u32, // Old ts selector 146 | esp0: u32, // Stack pointers and segment selectors 147 | ss0: u16, // after an increase in privilege level 148 | padding1: u16, 149 | esp1: *u32, 150 | ss1: u16, 151 | padding2: u16, 152 | esp2: *u32, 153 | ss2: u16, 154 | padding3: u16, 155 | cr3: *anyopaque, // Page directory base 156 | eip: *u32, // Saved state from last task switch 157 | eflags: u32, 158 | eax: u32, // More saved state (registers) 159 | ecx: u32, 160 | edx: u32, 161 | ebx: u32, 162 | esp: *u32, 163 | ebp: *u32, 164 | esi: u32, 165 | edi: u32, 166 | es: u16, // Even more saved state (segment selectors) 167 | padding4: u16, 168 | cs: u16, 169 | padding5: u16, 170 | ss: u16, 171 | padding6: u16, 172 | ds: u16, 173 | padding7: u16, 174 | fs: u16, 175 | padding8: u16, 176 | gs: u16, 177 | padding9: u16, 178 | ldt: u16, 179 | padding10: u16, 180 | t: u16, // Trap on task switch 181 | iomb: u16, // I/O map base address 182 | }; 183 | 184 | pub const gatedesc = packed struct { 185 | off_15_0: u16, // low 16 bits of offset in segment 186 | cs: u16, // code segment selector 187 | args: u5, // # args, 0 for interrupt/trap gates 188 | rsv1: u3, // reserved (should be zero I guess) 189 | typ: u4, // type (STS_{IG32, TG32}) 190 | s: u1, // must be 0 (system) 191 | dpl: u2, // descriptor (meaning new) privilege level 192 | p: u1, // Present 193 | off_31_16: u16, // high bits of offset in segment 194 | 195 | const Self = @This(); 196 | 197 | pub fn new(isTrap: bool, sel: u16, off: u32, d: u2) Self { 198 | return Self{ 199 | .off_15_0 = @as(u16, @intCast(off & 0xffff)), 200 | .cs = sel, 201 | .args = 0, 202 | .rsv1 = 0, 203 | .typ = if (isTrap) STS_TG32 else STS_IG32, 204 | .s = 0, 205 | .dpl = d, 206 | .p = 1, 207 | .off_31_16 = @as(u16, @intCast(off >> 16)), 208 | }; 209 | } 210 | }; 211 | -------------------------------------------------------------------------------- /src/console.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const ioapic = @import("ioapic.zig"); 3 | const file = @import("file.zig"); 4 | const kbd = @import("kbd.zig"); 5 | const proc = @import("proc.zig"); 6 | const spinlock = @import("spinlock.zig"); 7 | const trap = @import("trap.zig"); 8 | const uart = @import("uart.zig"); 9 | const x86 = @import("x86.zig"); 10 | const fmt = @import("std").fmt; 11 | const mem = @import("std").mem; 12 | const memlayout = @import("memlayout.zig"); 13 | 14 | var panicked: bool = false; 15 | 16 | pub var cons = struct { 17 | lock: spinlock.spinlock, 18 | locking: bool, 19 | }{ 20 | .lock = spinlock.spinlock.init("console"), 21 | .locking = false, 22 | }; 23 | 24 | const BACKSPACE = std.ascii.control_code.bs; 25 | const CRTPORT = 0x3d4; 26 | var crt = @as([*]u16, @ptrFromInt(memlayout.p2v(0xb8000))); // CGA memory 27 | 28 | fn cgaputc(c: u32) void { 29 | x86.out(CRTPORT, @as(u8, 14)); 30 | var pos = @as(u16, @intCast(x86.in(u8, CRTPORT + 1))) << 8; 31 | x86.out(CRTPORT, @as(u8, 15)); 32 | pos |= @as(u16, @intCast(x86.in(u8, CRTPORT + 1))); 33 | 34 | if (c == '\n') { 35 | pos += 80 - pos % 80; 36 | } else if (c == BACKSPACE) { 37 | if (pos > 0) { 38 | pos -= 1; 39 | } 40 | } else { 41 | crt[pos] = (@as(u16, @intCast(c & 0xff))) | 0x0700; // black on white 42 | pos += 1; 43 | } 44 | 45 | if (pos < 0 or pos > 25 * 80) { 46 | asm volatile ("1: jmp 1b"); // TODO: handle error 47 | } 48 | 49 | if (pos / 80 >= 24) { // Scroll up. 50 | for (crt[80 .. 24 * 80], 0..) |b, i| { 51 | crt[i] = b; 52 | } 53 | pos -= 80; 54 | for (crt[pos .. pos + 24 * 80 - pos]) |*b| { 55 | b.* = 0; 56 | } 57 | } 58 | 59 | x86.out(CRTPORT, @as(u8, 14)); 60 | x86.out(CRTPORT + 1, @as(u8, @intCast(pos >> 8))); 61 | x86.out(CRTPORT, @as(u8, 15)); 62 | x86.out(CRTPORT + 1, @as(u8, @intCast(pos & 0xff))); 63 | crt[pos] = ' ' | 0x0700; 64 | } 65 | 66 | fn consputc(c: u8) void { 67 | if (panicked) { 68 | x86.cli(); 69 | while (true) {} 70 | } 71 | 72 | if (c == BACKSPACE) { 73 | uart.putc(BACKSPACE); 74 | uart.putc(' '); 75 | uart.putc(BACKSPACE); 76 | } else { 77 | uart.putc(@as(u8, @intCast(c & 0xff))); 78 | } 79 | cgaputc(c); 80 | } 81 | 82 | const INPUT_BUF = 128; 83 | var input = struct { 84 | buf: [INPUT_BUF]u8, 85 | r: usize, // Read index 86 | w: usize, // Write index 87 | e: usize, // Edit index 88 | }{ 89 | .buf = undefined, 90 | .r = 0, 91 | .w = 0, 92 | .e = 0, 93 | }; 94 | 95 | // TODO: we could make getc fn() ?u8 96 | pub fn consoleintr(getc: *const fn () ?u8) void { 97 | var doprocdump: bool = false; 98 | 99 | cons.lock.acquire(); 100 | while (true) { 101 | var c = getc() orelse break; 102 | 103 | switch (c) { 104 | kbd.ctrl('P') => { // Process listing. 105 | doprocdump = true; 106 | }, 107 | 108 | kbd.ctrl('U') => { // Kill line. 109 | while (input.e != input.w and 110 | input.buf[(input.e - 1) % INPUT_BUF] != '\n') 111 | { 112 | input.e -%= 1; 113 | consputc(BACKSPACE); 114 | } 115 | }, 116 | 117 | kbd.ctrl('H'), '\x7f' => { //Backspace 118 | if (input.e != input.w) { 119 | input.e -%= 1; 120 | consputc(BACKSPACE); 121 | } 122 | }, 123 | 124 | else => { 125 | if (c != 0 and input.e -% input.r < INPUT_BUF) { 126 | c = if (c == '\r') '\n' else c; 127 | input.buf[input.e % INPUT_BUF] = c; 128 | input.e +%= 1; 129 | consputc(c); 130 | if (c == '\n' or c == kbd.ctrl('D') or input.e == input.r +% INPUT_BUF) { 131 | input.w = input.e; 132 | proc.wakeup(@intFromPtr(&input.r)); 133 | } 134 | } 135 | }, 136 | } 137 | } 138 | cons.lock.release(); 139 | if (doprocdump) { 140 | // TODO: procdump(); 141 | } 142 | } 143 | 144 | pub fn consoleread(ip: *file.inode, dst: [*]u8, n: u32) ?u32 { 145 | var read_size: u32 = 0; 146 | ip.unlock(); 147 | cons.lock.acquire(); 148 | defer { 149 | ip.lock(); 150 | cons.lock.release(); 151 | } 152 | 153 | while (read_size < n) { 154 | while (input.r == input.w) { 155 | if (proc.myproc().killed) { 156 | return null; 157 | } 158 | proc.sleep(@intFromPtr(&input.r), &cons.lock); 159 | } 160 | input.r = (input.r + 1) % INPUT_BUF; 161 | const c = input.buf[input.r]; 162 | if (c == kbd.ctrl('D')) { 163 | if (read_size > 0) { 164 | // Save ^D for next time, to make sure 165 | // caller gets a 0-byte result. 166 | input.r = (input.r - 1 + INPUT_BUF) % INPUT_BUF; 167 | } 168 | break; 169 | } 170 | dst[read_size] = c; 171 | read_size += 1; 172 | if (c == '\n') { 173 | break; 174 | } 175 | } 176 | return read_size; 177 | } 178 | 179 | pub fn consoleinit() void { 180 | cons.locking = true; 181 | 182 | ioapic.ioapicenable(trap.IRQ_KBD, 0); 183 | } 184 | 185 | pub fn consolewrite(ip: *file.inode, buf: []const u8, n: u32) u32 { 186 | ip.unlock(); 187 | cons.lock.acquire(); 188 | 189 | var i: u32 = 0; 190 | while (i < n) : (i += 1) { 191 | consputc(buf[i]); 192 | } 193 | cons.lock.release(); 194 | ip.lock(); 195 | 196 | return n; 197 | } 198 | 199 | const VGA_WIDTH = 80; 200 | const VGA_HEIGHT = 25; 201 | const VGA_SIZE = VGA_WIDTH * VGA_HEIGHT; 202 | 203 | pub const ConsoleColors = enum(u8) { 204 | Black = 0, 205 | Blue = 1, 206 | Green = 2, 207 | Cyan = 3, 208 | Red = 4, 209 | Magenta = 5, 210 | Brown = 6, 211 | LightGray = 7, 212 | DarkGray = 8, 213 | LightBlue = 9, 214 | LightGreen = 10, 215 | LightCyan = 11, 216 | LightRed = 12, 217 | LightMagenta = 13, 218 | LightBrown = 14, 219 | White = 15, 220 | }; 221 | 222 | var row: usize = 0; 223 | var column: usize = 0; 224 | var color = vgaEntryColor(ConsoleColors.LightGray, ConsoleColors.Black); 225 | var buffer = @as([*]u16, @ptrFromInt(memlayout.p2v(0xB8000))); 226 | 227 | fn vgaEntryColor(fg: ConsoleColors, bg: ConsoleColors) u8 { 228 | return @intFromEnum(fg) | (@intFromEnum(bg) << 4); 229 | } 230 | 231 | fn vgaEntry(uc: u8, new_color: u8) u16 { 232 | const c: u16 = new_color; 233 | 234 | return uc | (c << 8); 235 | } 236 | 237 | pub fn initialize() void { 238 | clear(); 239 | } 240 | 241 | pub fn setColor(new_color: u8) void { 242 | color = new_color; 243 | } 244 | 245 | pub fn clear() void { 246 | @memset(buffer[0..VGA_SIZE], vgaEntry(' ', color)); 247 | } 248 | 249 | pub fn putCharAt(c: u8, new_color: u8, x: usize, y: usize) void { 250 | const index = y * VGA_WIDTH + x; 251 | buffer[index] = vgaEntry(c, new_color); 252 | } 253 | 254 | pub fn putChar(c: u8) void { 255 | putCharAt(c, color, column, row); 256 | column += 1; 257 | if (column == VGA_WIDTH) { 258 | column = 0; 259 | row += 1; 260 | if (row == VGA_HEIGHT) 261 | row = 0; 262 | } 263 | } 264 | 265 | pub fn puts(data: []const u8) void { 266 | for (data) |c| { 267 | putChar(c); 268 | } 269 | } 270 | 271 | pub const writer = Writer(void, error{}, callback){ .context = {} }; 272 | 273 | fn callback(_: void, string: []const u8) error{}!usize { 274 | puts(string); 275 | return string.len; 276 | } 277 | 278 | /// The errors that can occur when logging 279 | const LoggingError = error{}; 280 | 281 | /// The Writer for the format function 282 | const Writer = std.io.Writer(void, LoggingError, logCallback); 283 | 284 | pub fn printf(comptime format: []const u8, args: anytype) void { 285 | fmt.format(Writer{ .context = {} }, format, args) catch unreachable; 286 | } 287 | 288 | fn logCallback(context: void, str: []const u8) LoggingError!usize { 289 | // Suppress unused var warning 290 | _ = context; 291 | puts(str); 292 | for (str) |c| { 293 | uart.putc(c); 294 | } 295 | return str.len; 296 | } 297 | -------------------------------------------------------------------------------- /src/fs.zig: -------------------------------------------------------------------------------- 1 | // File system implementation. Five layers: 2 | // + Blocks: allocator for raw disk blocks. 3 | // + Log: crash recovery for multi-step updates. 4 | // + Files: inode allocator, reading, writing, metadata. 5 | // + Directories: inode with special contents (list of other inodes!) 6 | // + Names: paths like /usr/rtm/xv6/fs.c for convenient naming. 7 | // 8 | // This file contains the low-level file system manipulation 9 | // routines. The (higher-level) system call implementations 10 | // are in sysfile.c. 11 | 12 | const bio = @import("bio.zig"); 13 | const file = @import("file.zig"); 14 | const param = @import("param.zig"); 15 | const sleeplock = @import("sleeplock.zig"); 16 | const spinlock = @import("spinlock.zig"); 17 | const util = @import("util.zig"); 18 | 19 | const inode = file.inode; 20 | 21 | pub const BSIZE = 512; 22 | 23 | pub const superblock = struct { 24 | size: u32 = 0, // Size of file system image (blocks) 25 | nblocks: u32 = 0, // Number of data blocks 26 | ninodes: u32 = 0, // Number of inodes. 27 | nlog: u32 = 0, // Number of log blocks 28 | logstart: u32 = 0, // Block number of first log block 29 | inodestart: u32 = 0, // Block number of first inode block 30 | bmapstart: u32 = 0, // Block number of first free map block 31 | }; 32 | 33 | pub var sb: superblock = undefined; 34 | 35 | pub const NDIRECT = 12; 36 | pub const NINDIRECT = (BSIZE / @sizeOf(u32)); 37 | pub const MAXFILE = NDIRECT + NINDIRECT; 38 | 39 | // On-disk inode structure 40 | const dinode = struct { 41 | typ: u16, // File type 42 | major: u16, // Major device number (T_DEV only) 43 | minor: u16, // Minor device number (T_DEV only) 44 | nlink: u16, // Number of links to inode in file system 45 | size: u32, // Size of file (bytes) 46 | addrs: [NDIRECT + 1]u32, 47 | }; 48 | 49 | // Inodes per block. 50 | pub const IPB = (BSIZE / @sizeOf(dinode)); 51 | 52 | // Bitmap bites per block 53 | pub const BPB = BSIZE * 8; 54 | 55 | // Directory is a file containing a sequence of dirent structures. 56 | const DIRSIZ = 14; 57 | 58 | pub const dirent = struct { 59 | inum: u16, 60 | name: [DIRSIZ]u8, 61 | }; 62 | 63 | // Block containing inode i 64 | pub fn iblock(i: u32, s: superblock) void { 65 | return i / IPB + s.inodestart; 66 | } 67 | 68 | // Block of free map containing bit for block b 69 | pub fn bblock(b: u32, s: *superblock) u32 { 70 | return b / BPB + s.bmapstart; 71 | } 72 | 73 | // Read the super block. 74 | pub fn readsb(dev: u32, s: *superblock) void { 75 | var bp = bio.buf.read(dev, 1); 76 | defer bp.release(); 77 | util.memmov(@as([*]u8, @ptrCast(&s)), @as([*]u8, @ptrCast(&bp.data)), @sizeOf(superblock)); 78 | } 79 | 80 | // Zero a block. 81 | fn bzero(dev: u32, bno: u32) void { 82 | var bp = bio.buf.read(dev, bno); 83 | defer bp.release(); 84 | @memset(@as([*]u8, @ptrCast(&bp.data))[0..BSIZE], 0); 85 | // TODO: log_write(); 86 | } 87 | 88 | // Blocks. 89 | 90 | // Allocate a zeroed disk block 91 | fn balloc(dev: u32) u32 { 92 | var bp: *bio.buf = undefined; 93 | var b: u32 = 0; 94 | while (b < sb.size) : (b += BPB) { 95 | bp = bio.buf.read(dev, bblock(b, sb)); 96 | var bi: u32 = 0; 97 | while (bi < BPB and b + bi < sb.size) : (bi += 1) { 98 | const m: u32 = 1 << (bi % 8); 99 | if ((bp.data[bi / 8] & m) == 0) { // Is block free? 100 | bp.*.data[bi / 8] |= m; // Mark block in use. 101 | // TODO: log_write 102 | bp.release(); 103 | bzero(dev, b + bi); 104 | return b + bi; 105 | } 106 | } 107 | bp.release(); 108 | } 109 | asm volatile ("1: jmp 1b"); // TODO: error handling 110 | } 111 | 112 | fn bfree(dev: u32, b: u32) void { 113 | var bp = bio.buf.read(dev, bblock(b, sb)); 114 | defer bp.release(); 115 | const bi: u32 = b % BPB; 116 | const m = 1 << (bi % 8); 117 | if ((bp.data[bi / 8] & m) == 0) { 118 | asm volatile ("1: jmp 1b"); // TODO: error handling 119 | } 120 | bp.*.data[bi / 8] &= ~m; 121 | // TODO: log_write 122 | } 123 | 124 | // Inodes. 125 | // 126 | // An inode describes a single unnamed file. 127 | // The inode disk structure holds metadata: the file's type, 128 | // its size, the number of links referring to it, and the 129 | // list of blocks holding the file's content. 130 | // 131 | // The inodes are laid out sequentially on disk at 132 | // sb.startinode. Each inode has a number, indicating its 133 | // position on the disk. 134 | // 135 | // The kernel keeps a cache of in-use inodes in memory 136 | // to provide a place for synchronizing access 137 | // to inodes used by multiple processes. The cached 138 | // inodes include book-keeping information that is 139 | // not stored on disk: ip->ref and ip->valid. 140 | // 141 | // An inode and its in-memory representation go through a 142 | // sequence of states before they can be used by the 143 | // rest of the file system code. 144 | // 145 | // * Allocation: an inode is allocated if its type (on disk) 146 | // is non-zero. ialloc() allocates, and iput() frees if 147 | // the reference and link counts have fallen to zero. 148 | // 149 | // * Referencing in cache: an entry in the inode cache 150 | // is free if ip->ref is zero. Otherwise ip->ref tracks 151 | // the number of in-memory pointers to the entry (open 152 | // files and current directories). iget() finds or 153 | // creates a cache entry and increments its ref; iput() 154 | // decrements ref. 155 | // 156 | // * Valid: the information (type, size, &c) in an inode 157 | // cache entry is only correct when ip->valid is 1. 158 | // ilock() reads the inode from 159 | // the disk and sets ip->valid, while iput() clears 160 | // ip->valid if ip->ref has fallen to zero. 161 | // 162 | // * Locked: file system code may only examine and modify 163 | // the information in an inode and its content if it 164 | // has first locked the inode. 165 | // 166 | // Thus a typical sequence is: 167 | // ip = iget(dev, inum) 168 | // ilock(ip) 169 | // ... examine and modify ip->xxx ... 170 | // iunlock(ip) 171 | // iput(ip) 172 | // 173 | // ilock() is separate from iget() so that system calls can 174 | // get a long-term reference to an inode (as for an open file) 175 | // and only lock it for short periods (e.g., in read()). 176 | // The separation also helps avoid deadlock and races during 177 | // pathname lookup. iget() increments ip->ref so that the inode 178 | // stays cached and pointers to it remain valid. 179 | // 180 | // Many internal file system functions expect the caller to 181 | // have locked the inodes involved; this lets callers create 182 | // multi-step atomic operations. 183 | // 184 | // The icache.lock spin-lock protects the allocation of icache 185 | // entries. Since ip->ref indicates whether an entry is free, 186 | // and ip->dev and ip->inum indicate which i-node an entry 187 | // holds, one must hold icache.lock while using any of those fields. 188 | // 189 | // An ip->lock sleep-lock protects all ip-> fields other than ref, 190 | // dev, and inum. One must hold ip->lock in order to 191 | // read or write that inode's ip->valid, ip->size, ip->type, &c. 192 | 193 | var icache = struct { 194 | lock: spinlock.spinlock, 195 | inode: [param.INODE]inode, 196 | 197 | const Self = @This(); 198 | 199 | fn iget(self: *Self, dev: u32, inum: u32) *inode { 200 | self.lock.acquire(); 201 | defer self.lock.release(); 202 | 203 | var ip: *inode = undefined; 204 | for (self.inode) |*i| { 205 | if (i.ref > 0 and i.dev == dev and i.inum == inum) { 206 | i.ref += 1; 207 | return i; 208 | } 209 | if (i.ref == 0) { 210 | ip = i; 211 | } 212 | } 213 | 214 | if (ip == undefined) { 215 | asm volatile ("1: jmp 1b"); // TODO: error handling 216 | } 217 | 218 | ip.dev = dev; 219 | ip.inum = inum; 220 | ip.ref = 1; 221 | ip.valid = false; 222 | 223 | return ip; 224 | } 225 | }{ 226 | .lock = spinlock.spinlock.init("icache"), 227 | .inode = init: { 228 | const initial_value: [param.INODE]inode = undefined; 229 | for (initial_value) |*i| { 230 | i.lock = sleeplock.sleeplock.init("inode"); 231 | } 232 | break :init initial_value; 233 | }, 234 | }; 235 | 236 | // Allocate an inode on device dev. 237 | // Mark it as allocated by giving it type type. 238 | // Returns an unlocked but allocated and referenced inode. 239 | fn ialloc(dev: u32, typ: u16) *inode { 240 | var inum: u32 = 1; 241 | while (inum < sb.ninodes) : (inum += 1) { 242 | var bp = bio.buf.read(dev, iblock(inum, sb)); 243 | var dip = @as([*]dinode, @ptrCast(&bp.data))[inum % IPB]; 244 | if (dip.typ == 0) { // a free inode 245 | @memset(@as([*]u8, @ptrCast(&dip))[0..@sizeOf(dinode)], 0); 246 | dip.typ = typ; 247 | // TODO: log_write() 248 | bp.release(); 249 | // TODO: return iget 250 | } 251 | bp.release(); 252 | } 253 | asm volatile ("1: jmp 1b"); // TODO: error handling 254 | } 255 | -------------------------------------------------------------------------------- /src/proc.zig: -------------------------------------------------------------------------------- 1 | const console = @import("console.zig"); 2 | const file = @import("file.zig"); 3 | const kalloc = @import("kalloc.zig"); 4 | const lapic = @import("lapic.zig"); 5 | const memlayout = @import("memlayout.zig"); 6 | const mmu = @import("mmu.zig"); 7 | const mp = @import("mp.zig"); 8 | const param = @import("param.zig"); 9 | const spinlock = @import("spinlock.zig"); 10 | const util = @import("util.zig"); 11 | const vm = @import("vm.zig"); 12 | const x86 = @import("x86.zig"); 13 | 14 | extern fn trapret() void; 15 | 16 | extern const _binary_zig_out_bin_initcode_start: u8; 17 | // Note: This value of a symbol is its address, 18 | // and the address is the size of initcode. 19 | extern const _binary_zig_out_bin_initcode_size: u8; 20 | 21 | var initproc: *proc = undefined; 22 | 23 | // Per-CPU state 24 | pub const cpu = struct { 25 | apicid: u16, // Local APIC ID 26 | scheduler: *context, // swtch() here to enter scheduler 27 | ts: mmu.taskstate, // Used by x86 to find stack for interrupt 28 | gdt: [mmu.NSEGS]mmu.segdesc, // x86 global descripter table 29 | started: bool, // Has the CPU started? 30 | ncli: u32, // Depth of pushcli nesting. 31 | intena: bool, // Were interrupts enabled before pushcli? 32 | proc: ?*proc, 33 | }; 34 | 35 | pub const procstate = enum { 36 | UNUSED, 37 | EMBRYO, 38 | SLEEPING, 39 | RUNNABLE, 40 | RUNNING, 41 | ZOMBIE, 42 | }; 43 | 44 | // Saved registers for kernel context switches. 45 | // Don't need to save all the segment registers (%cs, etc), 46 | // because they are constant across kernel contexts. 47 | // Don't need to save %eax, %ecx, %edx, because the 48 | // x86 convention is that the caller has saved them. 49 | // Contexts are stored at the bottom of the stack they 50 | // describe; the stack pointer is the address of the context. 51 | // The layout of the context matches the layout of the stack in swtch.zig 52 | // at the "Switch stacks" comment. Switch doesn't save eip explicitly, 53 | // but it is on the stack and allocproc() manipulates it. 54 | pub const context = extern struct { 55 | edi: u32 = 0, 56 | esi: u32 = 0, 57 | ebx: u32 = 0, 58 | ebp: u32 = 0, 59 | eip: u32 = 0, 60 | }; 61 | 62 | // Per-process state 63 | pub const proc = struct { 64 | sz: usize, // Size of process memory (bytes) 65 | pgdir: [*]mmu.pde_t, // Page table 66 | kstack: usize, // Bottom of kernel stack for this process 67 | state: procstate, // Process state 68 | pid: u32, // Process ID 69 | parent: ?*proc, // Parent process 70 | tf: *x86.trapframe, // Trap frame for current syscall 71 | context: *context, // swtch() here to run process 72 | chan: usize, // If non-zero, sleeping on chan 73 | killed: bool, // Whether it's been killed 74 | ofile: *[param.NOFILE]file.file, // Open files 75 | cwd: ?*file.inode, // Current directory 76 | name: [16]u8, // Process name (debugging) 77 | }; 78 | 79 | var ptable = struct { 80 | lock: spinlock.spinlock, 81 | proc: [param.NPROC]proc, 82 | }{ 83 | .lock = spinlock.spinlock.init("ptable"), 84 | .proc = undefined, // TODO: initialize 85 | }; 86 | 87 | var nextpid: usize = 1; 88 | 89 | // Process memory is laid out contiguously, low addresses first: 90 | // text 91 | // original data and bss 92 | // fixed-size stack 93 | // expandable heap 94 | 95 | pub fn cpuid() u32 { 96 | return (@intFromPtr(&mp.cpus) - @intFromPtr(mycpu())) / @sizeOf(cpu); 97 | } 98 | 99 | pub fn mycpu() *cpu { 100 | if (x86.readeflags() & mmu.FL_IF != 0) { 101 | asm volatile ("1: jmp 1b"); // TODO: handle error 102 | } 103 | const apicid = lapic.lapicid(); 104 | // APIC IDs are not guaranteed to be contiguous. Maybe we should have 105 | // a reverse map, or reserve a register to store &cpus[i]. 106 | for (&mp.cpus) |*c| { 107 | if (c.apicid == apicid) { 108 | return c; 109 | } 110 | } 111 | asm volatile ("1: jmp 1b"); // TODO: handle error 112 | unreachable; 113 | } 114 | 115 | // Disable interrupts so that we are not rescheduled 116 | // while reading proc from the cpu structure 117 | pub fn myproc() *proc { 118 | spinlock.pushcli(); 119 | const c = mycpu(); 120 | const p = c.proc; 121 | spinlock.popcli(); 122 | return p; 123 | } 124 | 125 | // Look in the process table for an UNUSED proc. 126 | // If found, change state to EMBRYO and initialize 127 | // state required to run in the kernel. 128 | // Otherwise return null. 129 | pub fn allocproc() ?*proc { 130 | ptable.lock.acquire(); 131 | 132 | for (&ptable.proc) |*p| { 133 | if (p.state == procstate.UNUSED) { 134 | p.*.state = procstate.EMBRYO; 135 | p.*.pid = nextpid; 136 | nextpid += 1; 137 | ptable.lock.release(); 138 | 139 | // Allocate kernel stack. 140 | p.*.kstack = kalloc.kalloc() orelse { 141 | p.*.state = procstate.UNUSED; 142 | return null; 143 | }; 144 | var sp = p.kstack + param.KSTACKSIZE; 145 | 146 | // Leave room for trap frame. 147 | sp -= @sizeOf(x86.trapframe); 148 | p.*.tf = @as(*x86.trapframe, @ptrFromInt(sp)); 149 | 150 | // Set up new context to start executing at forkret, which returns to trapret. 151 | sp -= 4; 152 | const trapret_pointer = @as(**const fn () callconv(.C) void, @ptrFromInt(sp)); 153 | trapret_pointer.* = trapret; 154 | 155 | sp -= @sizeOf(context); 156 | p.*.context = @as(*context, @ptrFromInt(sp)); 157 | p.*.context.* = context{}; 158 | p.*.context.eip = @intFromPtr(&forkret); 159 | return p; 160 | } 161 | } 162 | 163 | ptable.lock.release(); 164 | return null; 165 | } 166 | 167 | pub fn userinit() void { 168 | const p = allocproc() orelse unreachable; 169 | 170 | initproc = p; 171 | p.*.pgdir = vm.setupkvm() orelse @panic("usetinit: out of memory?"); 172 | 173 | vm.inituvm(p.pgdir, @as([*]u8, @ptrCast(&_binary_zig_out_bin_initcode_start)), @intFromPtr(&_binary_zig_out_bin_initcode_size)); 174 | p.*.sz = mmu.PGSIZE; 175 | @memset(@as([*]u8, @ptrCast(p.*.tf))[0..@sizeOf(x86.trapframe)], 0); 176 | p.*.tf.cs = (mmu.SEG_UCODE << 3) | mmu.DPL_USER; 177 | p.*.tf.ds = (mmu.SEG_UDATA << 3) | mmu.DPL_USER; 178 | p.*.tf.es = p.*.tf.ds; 179 | p.*.tf.ss = p.*.tf.ds; 180 | p.*.tf.eflags = mmu.FL_IF; 181 | p.*.tf.esp = mmu.PGSIZE; 182 | p.*.tf.eip = 0; // beginning of initcode.S 183 | 184 | util.safestrcpy(&p.*.name, "initcode"); 185 | // p.*.cwd = namei("/"); 186 | 187 | // Tell entryother.S what stack to use, where to enter, and what 188 | // pgdir to use. We cannot use kpgdir yet, because the AP processor 189 | // is running in low memory, so we use entrypgdir for the APs too. 190 | ptable.lock.acquire(); 191 | 192 | p.*.state = procstate.RUNNABLE; 193 | 194 | ptable.lock.release(); 195 | } 196 | 197 | pub fn sleep(chan: usize, lk: *spinlock.spinlock) void { 198 | const p = myproc(); 199 | 200 | // Must acquire ptable.lock in order to 201 | // change p->state and then call sched. 202 | // Once we hold ptable.lock, we can be 203 | // guaranteed that we won't miss any wakeup 204 | // (wakeup runs with ptable.lock locked), 205 | // so it's okay to release lk. 206 | if (lk != &ptable.lock) { 207 | ptable.lock.acquire(); 208 | lk.release(); 209 | } 210 | // Go to sleep. 211 | p.*.chan = chan; 212 | p.*.state = procstate.SLEEPING; 213 | 214 | // TODO: sched(); 215 | 216 | // Tidy up 217 | p.*.chan = 0; 218 | 219 | // Reacquire original lock. 220 | if (lk != &ptable.lock) { 221 | ptable.lock.release(); 222 | lk.acquire(); 223 | } 224 | } 225 | 226 | fn forkret() callconv(.C) void { 227 | const S = struct { 228 | var first: bool = true; 229 | }; 230 | 231 | // Still holding ptable.lock from scheduler. 232 | ptable.lock.release(); 233 | 234 | if (S.first) { 235 | S.first = false; 236 | // TOOD: iinit, initlog 237 | } 238 | 239 | // Return to "caller", actually trapret (see allocproc). 240 | } 241 | 242 | // Wake up all processes sleeping on chan. 243 | // The ptable lock must be held. 244 | fn wakeup1(chan: usize) void { 245 | for (&ptable.proc) |*p| { 246 | if (p.state == procstate.SLEEPING and p.chan == chan) { 247 | p.*.state = procstate.RUNNABLE; 248 | } 249 | } 250 | } 251 | 252 | // Wake up all processes sleeping on chan. 253 | pub fn wakeup(chan: usize) void { 254 | ptable.lock.acquire(); 255 | wakeup1(chan); 256 | ptable.lock.release(); 257 | } 258 | 259 | // Per-CPU process scheduler. 260 | // Each CPU calls scheduler() after setting itself up. 261 | // Scheduler never returns. It loops, doing: 262 | // - choose a process to run 263 | // - swtch to start running that process 264 | // - eventually that process transfers control 265 | // via swtch back to the scheduler. 266 | pub fn scheduler() void { 267 | const c = mycpu(); 268 | c.proc = null; 269 | 270 | const S = struct { 271 | var first: bool = true; 272 | }; 273 | if (S.first) { 274 | console.printf("Process Scheduling\n", .{}); 275 | S.first = false; 276 | } 277 | 278 | while (true) { 279 | x86.sti(); 280 | 281 | ptable.lock.acquire(); 282 | for (&ptable.proc) |*p| { 283 | if (p.state != procstate.RUNNABLE) { 284 | continue; 285 | } 286 | 287 | // Switch to chosen process. It is the process's job 288 | // to release ptable.lock and then reacquire it 289 | // before jumping back to us. 290 | c.proc = p; 291 | vm.switchuvm(p); 292 | p.*.state = procstate.RUNNING; 293 | x86.swtch(&c.scheduler, p.context); 294 | vm.switchkvm(); 295 | 296 | // Process is done running for now. 297 | // It should have changed its p->state before coming back. 298 | c.proc = null; 299 | } 300 | ptable.lock.release(); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/vector.S: -------------------------------------------------------------------------------- 1 | # generated by vectors.pl - do not edit 2 | # handlers 3 | .globl alltraps 4 | .globl vector0 5 | vector0: 6 | pushl $0 7 | pushl $0 8 | jmp alltraps 9 | .globl vector1 10 | vector1: 11 | pushl $0 12 | pushl $1 13 | jmp alltraps 14 | .globl vector2 15 | vector2: 16 | pushl $0 17 | pushl $2 18 | jmp alltraps 19 | .globl vector3 20 | vector3: 21 | pushl $0 22 | pushl $3 23 | jmp alltraps 24 | .globl vector4 25 | vector4: 26 | pushl $0 27 | pushl $4 28 | jmp alltraps 29 | .globl vector5 30 | vector5: 31 | pushl $0 32 | pushl $5 33 | jmp alltraps 34 | .globl vector6 35 | vector6: 36 | pushl $0 37 | pushl $6 38 | jmp alltraps 39 | .globl vector7 40 | vector7: 41 | pushl $0 42 | pushl $7 43 | jmp alltraps 44 | .globl vector8 45 | vector8: 46 | pushl $8 47 | jmp alltraps 48 | .globl vector9 49 | vector9: 50 | pushl $0 51 | pushl $9 52 | jmp alltraps 53 | .globl vector10 54 | vector10: 55 | pushl $10 56 | jmp alltraps 57 | .globl vector11 58 | vector11: 59 | pushl $11 60 | jmp alltraps 61 | .globl vector12 62 | vector12: 63 | pushl $12 64 | jmp alltraps 65 | .globl vector13 66 | vector13: 67 | pushl $13 68 | jmp alltraps 69 | .globl vector14 70 | vector14: 71 | pushl $14 72 | jmp alltraps 73 | .globl vector15 74 | vector15: 75 | pushl $0 76 | pushl $15 77 | jmp alltraps 78 | .globl vector16 79 | vector16: 80 | pushl $0 81 | pushl $16 82 | jmp alltraps 83 | .globl vector17 84 | vector17: 85 | pushl $17 86 | jmp alltraps 87 | .globl vector18 88 | vector18: 89 | pushl $0 90 | pushl $18 91 | jmp alltraps 92 | .globl vector19 93 | vector19: 94 | pushl $0 95 | pushl $19 96 | jmp alltraps 97 | .globl vector20 98 | vector20: 99 | pushl $0 100 | pushl $20 101 | jmp alltraps 102 | .globl vector21 103 | vector21: 104 | pushl $0 105 | pushl $21 106 | jmp alltraps 107 | .globl vector22 108 | vector22: 109 | pushl $0 110 | pushl $22 111 | jmp alltraps 112 | .globl vector23 113 | vector23: 114 | pushl $0 115 | pushl $23 116 | jmp alltraps 117 | .globl vector24 118 | vector24: 119 | pushl $0 120 | pushl $24 121 | jmp alltraps 122 | .globl vector25 123 | vector25: 124 | pushl $0 125 | pushl $25 126 | jmp alltraps 127 | .globl vector26 128 | vector26: 129 | pushl $0 130 | pushl $26 131 | jmp alltraps 132 | .globl vector27 133 | vector27: 134 | pushl $0 135 | pushl $27 136 | jmp alltraps 137 | .globl vector28 138 | vector28: 139 | pushl $0 140 | pushl $28 141 | jmp alltraps 142 | .globl vector29 143 | vector29: 144 | pushl $0 145 | pushl $29 146 | jmp alltraps 147 | .globl vector30 148 | vector30: 149 | pushl $0 150 | pushl $30 151 | jmp alltraps 152 | .globl vector31 153 | vector31: 154 | pushl $0 155 | pushl $31 156 | jmp alltraps 157 | .globl vector32 158 | vector32: 159 | pushl $0 160 | pushl $32 161 | jmp alltraps 162 | .globl vector33 163 | vector33: 164 | pushl $0 165 | pushl $33 166 | jmp alltraps 167 | .globl vector34 168 | vector34: 169 | pushl $0 170 | pushl $34 171 | jmp alltraps 172 | .globl vector35 173 | vector35: 174 | pushl $0 175 | pushl $35 176 | jmp alltraps 177 | .globl vector36 178 | vector36: 179 | pushl $0 180 | pushl $36 181 | jmp alltraps 182 | .globl vector37 183 | vector37: 184 | pushl $0 185 | pushl $37 186 | jmp alltraps 187 | .globl vector38 188 | vector38: 189 | pushl $0 190 | pushl $38 191 | jmp alltraps 192 | .globl vector39 193 | vector39: 194 | pushl $0 195 | pushl $39 196 | jmp alltraps 197 | .globl vector40 198 | vector40: 199 | pushl $0 200 | pushl $40 201 | jmp alltraps 202 | .globl vector41 203 | vector41: 204 | pushl $0 205 | pushl $41 206 | jmp alltraps 207 | .globl vector42 208 | vector42: 209 | pushl $0 210 | pushl $42 211 | jmp alltraps 212 | .globl vector43 213 | vector43: 214 | pushl $0 215 | pushl $43 216 | jmp alltraps 217 | .globl vector44 218 | vector44: 219 | pushl $0 220 | pushl $44 221 | jmp alltraps 222 | .globl vector45 223 | vector45: 224 | pushl $0 225 | pushl $45 226 | jmp alltraps 227 | .globl vector46 228 | vector46: 229 | pushl $0 230 | pushl $46 231 | jmp alltraps 232 | .globl vector47 233 | vector47: 234 | pushl $0 235 | pushl $47 236 | jmp alltraps 237 | .globl vector48 238 | vector48: 239 | pushl $0 240 | pushl $48 241 | jmp alltraps 242 | .globl vector49 243 | vector49: 244 | pushl $0 245 | pushl $49 246 | jmp alltraps 247 | .globl vector50 248 | vector50: 249 | pushl $0 250 | pushl $50 251 | jmp alltraps 252 | .globl vector51 253 | vector51: 254 | pushl $0 255 | pushl $51 256 | jmp alltraps 257 | .globl vector52 258 | vector52: 259 | pushl $0 260 | pushl $52 261 | jmp alltraps 262 | .globl vector53 263 | vector53: 264 | pushl $0 265 | pushl $53 266 | jmp alltraps 267 | .globl vector54 268 | vector54: 269 | pushl $0 270 | pushl $54 271 | jmp alltraps 272 | .globl vector55 273 | vector55: 274 | pushl $0 275 | pushl $55 276 | jmp alltraps 277 | .globl vector56 278 | vector56: 279 | pushl $0 280 | pushl $56 281 | jmp alltraps 282 | .globl vector57 283 | vector57: 284 | pushl $0 285 | pushl $57 286 | jmp alltraps 287 | .globl vector58 288 | vector58: 289 | pushl $0 290 | pushl $58 291 | jmp alltraps 292 | .globl vector59 293 | vector59: 294 | pushl $0 295 | pushl $59 296 | jmp alltraps 297 | .globl vector60 298 | vector60: 299 | pushl $0 300 | pushl $60 301 | jmp alltraps 302 | .globl vector61 303 | vector61: 304 | pushl $0 305 | pushl $61 306 | jmp alltraps 307 | .globl vector62 308 | vector62: 309 | pushl $0 310 | pushl $62 311 | jmp alltraps 312 | .globl vector63 313 | vector63: 314 | pushl $0 315 | pushl $63 316 | jmp alltraps 317 | .globl vector64 318 | vector64: 319 | pushl $0 320 | pushl $64 321 | jmp alltraps 322 | .globl vector65 323 | vector65: 324 | pushl $0 325 | pushl $65 326 | jmp alltraps 327 | .globl vector66 328 | vector66: 329 | pushl $0 330 | pushl $66 331 | jmp alltraps 332 | .globl vector67 333 | vector67: 334 | pushl $0 335 | pushl $67 336 | jmp alltraps 337 | .globl vector68 338 | vector68: 339 | pushl $0 340 | pushl $68 341 | jmp alltraps 342 | .globl vector69 343 | vector69: 344 | pushl $0 345 | pushl $69 346 | jmp alltraps 347 | .globl vector70 348 | vector70: 349 | pushl $0 350 | pushl $70 351 | jmp alltraps 352 | .globl vector71 353 | vector71: 354 | pushl $0 355 | pushl $71 356 | jmp alltraps 357 | .globl vector72 358 | vector72: 359 | pushl $0 360 | pushl $72 361 | jmp alltraps 362 | .globl vector73 363 | vector73: 364 | pushl $0 365 | pushl $73 366 | jmp alltraps 367 | .globl vector74 368 | vector74: 369 | pushl $0 370 | pushl $74 371 | jmp alltraps 372 | .globl vector75 373 | vector75: 374 | pushl $0 375 | pushl $75 376 | jmp alltraps 377 | .globl vector76 378 | vector76: 379 | pushl $0 380 | pushl $76 381 | jmp alltraps 382 | .globl vector77 383 | vector77: 384 | pushl $0 385 | pushl $77 386 | jmp alltraps 387 | .globl vector78 388 | vector78: 389 | pushl $0 390 | pushl $78 391 | jmp alltraps 392 | .globl vector79 393 | vector79: 394 | pushl $0 395 | pushl $79 396 | jmp alltraps 397 | .globl vector80 398 | vector80: 399 | pushl $0 400 | pushl $80 401 | jmp alltraps 402 | .globl vector81 403 | vector81: 404 | pushl $0 405 | pushl $81 406 | jmp alltraps 407 | .globl vector82 408 | vector82: 409 | pushl $0 410 | pushl $82 411 | jmp alltraps 412 | .globl vector83 413 | vector83: 414 | pushl $0 415 | pushl $83 416 | jmp alltraps 417 | .globl vector84 418 | vector84: 419 | pushl $0 420 | pushl $84 421 | jmp alltraps 422 | .globl vector85 423 | vector85: 424 | pushl $0 425 | pushl $85 426 | jmp alltraps 427 | .globl vector86 428 | vector86: 429 | pushl $0 430 | pushl $86 431 | jmp alltraps 432 | .globl vector87 433 | vector87: 434 | pushl $0 435 | pushl $87 436 | jmp alltraps 437 | .globl vector88 438 | vector88: 439 | pushl $0 440 | pushl $88 441 | jmp alltraps 442 | .globl vector89 443 | vector89: 444 | pushl $0 445 | pushl $89 446 | jmp alltraps 447 | .globl vector90 448 | vector90: 449 | pushl $0 450 | pushl $90 451 | jmp alltraps 452 | .globl vector91 453 | vector91: 454 | pushl $0 455 | pushl $91 456 | jmp alltraps 457 | .globl vector92 458 | vector92: 459 | pushl $0 460 | pushl $92 461 | jmp alltraps 462 | .globl vector93 463 | vector93: 464 | pushl $0 465 | pushl $93 466 | jmp alltraps 467 | .globl vector94 468 | vector94: 469 | pushl $0 470 | pushl $94 471 | jmp alltraps 472 | .globl vector95 473 | vector95: 474 | pushl $0 475 | pushl $95 476 | jmp alltraps 477 | .globl vector96 478 | vector96: 479 | pushl $0 480 | pushl $96 481 | jmp alltraps 482 | .globl vector97 483 | vector97: 484 | pushl $0 485 | pushl $97 486 | jmp alltraps 487 | .globl vector98 488 | vector98: 489 | pushl $0 490 | pushl $98 491 | jmp alltraps 492 | .globl vector99 493 | vector99: 494 | pushl $0 495 | pushl $99 496 | jmp alltraps 497 | .globl vector100 498 | vector100: 499 | pushl $0 500 | pushl $100 501 | jmp alltraps 502 | .globl vector101 503 | vector101: 504 | pushl $0 505 | pushl $101 506 | jmp alltraps 507 | .globl vector102 508 | vector102: 509 | pushl $0 510 | pushl $102 511 | jmp alltraps 512 | .globl vector103 513 | vector103: 514 | pushl $0 515 | pushl $103 516 | jmp alltraps 517 | .globl vector104 518 | vector104: 519 | pushl $0 520 | pushl $104 521 | jmp alltraps 522 | .globl vector105 523 | vector105: 524 | pushl $0 525 | pushl $105 526 | jmp alltraps 527 | .globl vector106 528 | vector106: 529 | pushl $0 530 | pushl $106 531 | jmp alltraps 532 | .globl vector107 533 | vector107: 534 | pushl $0 535 | pushl $107 536 | jmp alltraps 537 | .globl vector108 538 | vector108: 539 | pushl $0 540 | pushl $108 541 | jmp alltraps 542 | .globl vector109 543 | vector109: 544 | pushl $0 545 | pushl $109 546 | jmp alltraps 547 | .globl vector110 548 | vector110: 549 | pushl $0 550 | pushl $110 551 | jmp alltraps 552 | .globl vector111 553 | vector111: 554 | pushl $0 555 | pushl $111 556 | jmp alltraps 557 | .globl vector112 558 | vector112: 559 | pushl $0 560 | pushl $112 561 | jmp alltraps 562 | .globl vector113 563 | vector113: 564 | pushl $0 565 | pushl $113 566 | jmp alltraps 567 | .globl vector114 568 | vector114: 569 | pushl $0 570 | pushl $114 571 | jmp alltraps 572 | .globl vector115 573 | vector115: 574 | pushl $0 575 | pushl $115 576 | jmp alltraps 577 | .globl vector116 578 | vector116: 579 | pushl $0 580 | pushl $116 581 | jmp alltraps 582 | .globl vector117 583 | vector117: 584 | pushl $0 585 | pushl $117 586 | jmp alltraps 587 | .globl vector118 588 | vector118: 589 | pushl $0 590 | pushl $118 591 | jmp alltraps 592 | .globl vector119 593 | vector119: 594 | pushl $0 595 | pushl $119 596 | jmp alltraps 597 | .globl vector120 598 | vector120: 599 | pushl $0 600 | pushl $120 601 | jmp alltraps 602 | .globl vector121 603 | vector121: 604 | pushl $0 605 | pushl $121 606 | jmp alltraps 607 | .globl vector122 608 | vector122: 609 | pushl $0 610 | pushl $122 611 | jmp alltraps 612 | .globl vector123 613 | vector123: 614 | pushl $0 615 | pushl $123 616 | jmp alltraps 617 | .globl vector124 618 | vector124: 619 | pushl $0 620 | pushl $124 621 | jmp alltraps 622 | .globl vector125 623 | vector125: 624 | pushl $0 625 | pushl $125 626 | jmp alltraps 627 | .globl vector126 628 | vector126: 629 | pushl $0 630 | pushl $126 631 | jmp alltraps 632 | .globl vector127 633 | vector127: 634 | pushl $0 635 | pushl $127 636 | jmp alltraps 637 | .globl vector128 638 | vector128: 639 | pushl $0 640 | pushl $128 641 | jmp alltraps 642 | .globl vector129 643 | vector129: 644 | pushl $0 645 | pushl $129 646 | jmp alltraps 647 | .globl vector130 648 | vector130: 649 | pushl $0 650 | pushl $130 651 | jmp alltraps 652 | .globl vector131 653 | vector131: 654 | pushl $0 655 | pushl $131 656 | jmp alltraps 657 | .globl vector132 658 | vector132: 659 | pushl $0 660 | pushl $132 661 | jmp alltraps 662 | .globl vector133 663 | vector133: 664 | pushl $0 665 | pushl $133 666 | jmp alltraps 667 | .globl vector134 668 | vector134: 669 | pushl $0 670 | pushl $134 671 | jmp alltraps 672 | .globl vector135 673 | vector135: 674 | pushl $0 675 | pushl $135 676 | jmp alltraps 677 | .globl vector136 678 | vector136: 679 | pushl $0 680 | pushl $136 681 | jmp alltraps 682 | .globl vector137 683 | vector137: 684 | pushl $0 685 | pushl $137 686 | jmp alltraps 687 | .globl vector138 688 | vector138: 689 | pushl $0 690 | pushl $138 691 | jmp alltraps 692 | .globl vector139 693 | vector139: 694 | pushl $0 695 | pushl $139 696 | jmp alltraps 697 | .globl vector140 698 | vector140: 699 | pushl $0 700 | pushl $140 701 | jmp alltraps 702 | .globl vector141 703 | vector141: 704 | pushl $0 705 | pushl $141 706 | jmp alltraps 707 | .globl vector142 708 | vector142: 709 | pushl $0 710 | pushl $142 711 | jmp alltraps 712 | .globl vector143 713 | vector143: 714 | pushl $0 715 | pushl $143 716 | jmp alltraps 717 | .globl vector144 718 | vector144: 719 | pushl $0 720 | pushl $144 721 | jmp alltraps 722 | .globl vector145 723 | vector145: 724 | pushl $0 725 | pushl $145 726 | jmp alltraps 727 | .globl vector146 728 | vector146: 729 | pushl $0 730 | pushl $146 731 | jmp alltraps 732 | .globl vector147 733 | vector147: 734 | pushl $0 735 | pushl $147 736 | jmp alltraps 737 | .globl vector148 738 | vector148: 739 | pushl $0 740 | pushl $148 741 | jmp alltraps 742 | .globl vector149 743 | vector149: 744 | pushl $0 745 | pushl $149 746 | jmp alltraps 747 | .globl vector150 748 | vector150: 749 | pushl $0 750 | pushl $150 751 | jmp alltraps 752 | .globl vector151 753 | vector151: 754 | pushl $0 755 | pushl $151 756 | jmp alltraps 757 | .globl vector152 758 | vector152: 759 | pushl $0 760 | pushl $152 761 | jmp alltraps 762 | .globl vector153 763 | vector153: 764 | pushl $0 765 | pushl $153 766 | jmp alltraps 767 | .globl vector154 768 | vector154: 769 | pushl $0 770 | pushl $154 771 | jmp alltraps 772 | .globl vector155 773 | vector155: 774 | pushl $0 775 | pushl $155 776 | jmp alltraps 777 | .globl vector156 778 | vector156: 779 | pushl $0 780 | pushl $156 781 | jmp alltraps 782 | .globl vector157 783 | vector157: 784 | pushl $0 785 | pushl $157 786 | jmp alltraps 787 | .globl vector158 788 | vector158: 789 | pushl $0 790 | pushl $158 791 | jmp alltraps 792 | .globl vector159 793 | vector159: 794 | pushl $0 795 | pushl $159 796 | jmp alltraps 797 | .globl vector160 798 | vector160: 799 | pushl $0 800 | pushl $160 801 | jmp alltraps 802 | .globl vector161 803 | vector161: 804 | pushl $0 805 | pushl $161 806 | jmp alltraps 807 | .globl vector162 808 | vector162: 809 | pushl $0 810 | pushl $162 811 | jmp alltraps 812 | .globl vector163 813 | vector163: 814 | pushl $0 815 | pushl $163 816 | jmp alltraps 817 | .globl vector164 818 | vector164: 819 | pushl $0 820 | pushl $164 821 | jmp alltraps 822 | .globl vector165 823 | vector165: 824 | pushl $0 825 | pushl $165 826 | jmp alltraps 827 | .globl vector166 828 | vector166: 829 | pushl $0 830 | pushl $166 831 | jmp alltraps 832 | .globl vector167 833 | vector167: 834 | pushl $0 835 | pushl $167 836 | jmp alltraps 837 | .globl vector168 838 | vector168: 839 | pushl $0 840 | pushl $168 841 | jmp alltraps 842 | .globl vector169 843 | vector169: 844 | pushl $0 845 | pushl $169 846 | jmp alltraps 847 | .globl vector170 848 | vector170: 849 | pushl $0 850 | pushl $170 851 | jmp alltraps 852 | .globl vector171 853 | vector171: 854 | pushl $0 855 | pushl $171 856 | jmp alltraps 857 | .globl vector172 858 | vector172: 859 | pushl $0 860 | pushl $172 861 | jmp alltraps 862 | .globl vector173 863 | vector173: 864 | pushl $0 865 | pushl $173 866 | jmp alltraps 867 | .globl vector174 868 | vector174: 869 | pushl $0 870 | pushl $174 871 | jmp alltraps 872 | .globl vector175 873 | vector175: 874 | pushl $0 875 | pushl $175 876 | jmp alltraps 877 | .globl vector176 878 | vector176: 879 | pushl $0 880 | pushl $176 881 | jmp alltraps 882 | .globl vector177 883 | vector177: 884 | pushl $0 885 | pushl $177 886 | jmp alltraps 887 | .globl vector178 888 | vector178: 889 | pushl $0 890 | pushl $178 891 | jmp alltraps 892 | .globl vector179 893 | vector179: 894 | pushl $0 895 | pushl $179 896 | jmp alltraps 897 | .globl vector180 898 | vector180: 899 | pushl $0 900 | pushl $180 901 | jmp alltraps 902 | .globl vector181 903 | vector181: 904 | pushl $0 905 | pushl $181 906 | jmp alltraps 907 | .globl vector182 908 | vector182: 909 | pushl $0 910 | pushl $182 911 | jmp alltraps 912 | .globl vector183 913 | vector183: 914 | pushl $0 915 | pushl $183 916 | jmp alltraps 917 | .globl vector184 918 | vector184: 919 | pushl $0 920 | pushl $184 921 | jmp alltraps 922 | .globl vector185 923 | vector185: 924 | pushl $0 925 | pushl $185 926 | jmp alltraps 927 | .globl vector186 928 | vector186: 929 | pushl $0 930 | pushl $186 931 | jmp alltraps 932 | .globl vector187 933 | vector187: 934 | pushl $0 935 | pushl $187 936 | jmp alltraps 937 | .globl vector188 938 | vector188: 939 | pushl $0 940 | pushl $188 941 | jmp alltraps 942 | .globl vector189 943 | vector189: 944 | pushl $0 945 | pushl $189 946 | jmp alltraps 947 | .globl vector190 948 | vector190: 949 | pushl $0 950 | pushl $190 951 | jmp alltraps 952 | .globl vector191 953 | vector191: 954 | pushl $0 955 | pushl $191 956 | jmp alltraps 957 | .globl vector192 958 | vector192: 959 | pushl $0 960 | pushl $192 961 | jmp alltraps 962 | .globl vector193 963 | vector193: 964 | pushl $0 965 | pushl $193 966 | jmp alltraps 967 | .globl vector194 968 | vector194: 969 | pushl $0 970 | pushl $194 971 | jmp alltraps 972 | .globl vector195 973 | vector195: 974 | pushl $0 975 | pushl $195 976 | jmp alltraps 977 | .globl vector196 978 | vector196: 979 | pushl $0 980 | pushl $196 981 | jmp alltraps 982 | .globl vector197 983 | vector197: 984 | pushl $0 985 | pushl $197 986 | jmp alltraps 987 | .globl vector198 988 | vector198: 989 | pushl $0 990 | pushl $198 991 | jmp alltraps 992 | .globl vector199 993 | vector199: 994 | pushl $0 995 | pushl $199 996 | jmp alltraps 997 | .globl vector200 998 | vector200: 999 | pushl $0 1000 | pushl $200 1001 | jmp alltraps 1002 | .globl vector201 1003 | vector201: 1004 | pushl $0 1005 | pushl $201 1006 | jmp alltraps 1007 | .globl vector202 1008 | vector202: 1009 | pushl $0 1010 | pushl $202 1011 | jmp alltraps 1012 | .globl vector203 1013 | vector203: 1014 | pushl $0 1015 | pushl $203 1016 | jmp alltraps 1017 | .globl vector204 1018 | vector204: 1019 | pushl $0 1020 | pushl $204 1021 | jmp alltraps 1022 | .globl vector205 1023 | vector205: 1024 | pushl $0 1025 | pushl $205 1026 | jmp alltraps 1027 | .globl vector206 1028 | vector206: 1029 | pushl $0 1030 | pushl $206 1031 | jmp alltraps 1032 | .globl vector207 1033 | vector207: 1034 | pushl $0 1035 | pushl $207 1036 | jmp alltraps 1037 | .globl vector208 1038 | vector208: 1039 | pushl $0 1040 | pushl $208 1041 | jmp alltraps 1042 | .globl vector209 1043 | vector209: 1044 | pushl $0 1045 | pushl $209 1046 | jmp alltraps 1047 | .globl vector210 1048 | vector210: 1049 | pushl $0 1050 | pushl $210 1051 | jmp alltraps 1052 | .globl vector211 1053 | vector211: 1054 | pushl $0 1055 | pushl $211 1056 | jmp alltraps 1057 | .globl vector212 1058 | vector212: 1059 | pushl $0 1060 | pushl $212 1061 | jmp alltraps 1062 | .globl vector213 1063 | vector213: 1064 | pushl $0 1065 | pushl $213 1066 | jmp alltraps 1067 | .globl vector214 1068 | vector214: 1069 | pushl $0 1070 | pushl $214 1071 | jmp alltraps 1072 | .globl vector215 1073 | vector215: 1074 | pushl $0 1075 | pushl $215 1076 | jmp alltraps 1077 | .globl vector216 1078 | vector216: 1079 | pushl $0 1080 | pushl $216 1081 | jmp alltraps 1082 | .globl vector217 1083 | vector217: 1084 | pushl $0 1085 | pushl $217 1086 | jmp alltraps 1087 | .globl vector218 1088 | vector218: 1089 | pushl $0 1090 | pushl $218 1091 | jmp alltraps 1092 | .globl vector219 1093 | vector219: 1094 | pushl $0 1095 | pushl $219 1096 | jmp alltraps 1097 | .globl vector220 1098 | vector220: 1099 | pushl $0 1100 | pushl $220 1101 | jmp alltraps 1102 | .globl vector221 1103 | vector221: 1104 | pushl $0 1105 | pushl $221 1106 | jmp alltraps 1107 | .globl vector222 1108 | vector222: 1109 | pushl $0 1110 | pushl $222 1111 | jmp alltraps 1112 | .globl vector223 1113 | vector223: 1114 | pushl $0 1115 | pushl $223 1116 | jmp alltraps 1117 | .globl vector224 1118 | vector224: 1119 | pushl $0 1120 | pushl $224 1121 | jmp alltraps 1122 | .globl vector225 1123 | vector225: 1124 | pushl $0 1125 | pushl $225 1126 | jmp alltraps 1127 | .globl vector226 1128 | vector226: 1129 | pushl $0 1130 | pushl $226 1131 | jmp alltraps 1132 | .globl vector227 1133 | vector227: 1134 | pushl $0 1135 | pushl $227 1136 | jmp alltraps 1137 | .globl vector228 1138 | vector228: 1139 | pushl $0 1140 | pushl $228 1141 | jmp alltraps 1142 | .globl vector229 1143 | vector229: 1144 | pushl $0 1145 | pushl $229 1146 | jmp alltraps 1147 | .globl vector230 1148 | vector230: 1149 | pushl $0 1150 | pushl $230 1151 | jmp alltraps 1152 | .globl vector231 1153 | vector231: 1154 | pushl $0 1155 | pushl $231 1156 | jmp alltraps 1157 | .globl vector232 1158 | vector232: 1159 | pushl $0 1160 | pushl $232 1161 | jmp alltraps 1162 | .globl vector233 1163 | vector233: 1164 | pushl $0 1165 | pushl $233 1166 | jmp alltraps 1167 | .globl vector234 1168 | vector234: 1169 | pushl $0 1170 | pushl $234 1171 | jmp alltraps 1172 | .globl vector235 1173 | vector235: 1174 | pushl $0 1175 | pushl $235 1176 | jmp alltraps 1177 | .globl vector236 1178 | vector236: 1179 | pushl $0 1180 | pushl $236 1181 | jmp alltraps 1182 | .globl vector237 1183 | vector237: 1184 | pushl $0 1185 | pushl $237 1186 | jmp alltraps 1187 | .globl vector238 1188 | vector238: 1189 | pushl $0 1190 | pushl $238 1191 | jmp alltraps 1192 | .globl vector239 1193 | vector239: 1194 | pushl $0 1195 | pushl $239 1196 | jmp alltraps 1197 | .globl vector240 1198 | vector240: 1199 | pushl $0 1200 | pushl $240 1201 | jmp alltraps 1202 | .globl vector241 1203 | vector241: 1204 | pushl $0 1205 | pushl $241 1206 | jmp alltraps 1207 | .globl vector242 1208 | vector242: 1209 | pushl $0 1210 | pushl $242 1211 | jmp alltraps 1212 | .globl vector243 1213 | vector243: 1214 | pushl $0 1215 | pushl $243 1216 | jmp alltraps 1217 | .globl vector244 1218 | vector244: 1219 | pushl $0 1220 | pushl $244 1221 | jmp alltraps 1222 | .globl vector245 1223 | vector245: 1224 | pushl $0 1225 | pushl $245 1226 | jmp alltraps 1227 | .globl vector246 1228 | vector246: 1229 | pushl $0 1230 | pushl $246 1231 | jmp alltraps 1232 | .globl vector247 1233 | vector247: 1234 | pushl $0 1235 | pushl $247 1236 | jmp alltraps 1237 | .globl vector248 1238 | vector248: 1239 | pushl $0 1240 | pushl $248 1241 | jmp alltraps 1242 | .globl vector249 1243 | vector249: 1244 | pushl $0 1245 | pushl $249 1246 | jmp alltraps 1247 | .globl vector250 1248 | vector250: 1249 | pushl $0 1250 | pushl $250 1251 | jmp alltraps 1252 | .globl vector251 1253 | vector251: 1254 | pushl $0 1255 | pushl $251 1256 | jmp alltraps 1257 | .globl vector252 1258 | vector252: 1259 | pushl $0 1260 | pushl $252 1261 | jmp alltraps 1262 | .globl vector253 1263 | vector253: 1264 | pushl $0 1265 | pushl $253 1266 | jmp alltraps 1267 | .globl vector254 1268 | vector254: 1269 | pushl $0 1270 | pushl $254 1271 | jmp alltraps 1272 | .globl vector255 1273 | vector255: 1274 | pushl $0 1275 | pushl $255 1276 | jmp alltraps 1277 | 1278 | # vector table 1279 | .data 1280 | .globl vectors 1281 | vectors: 1282 | .long vector0 1283 | .long vector1 1284 | .long vector2 1285 | .long vector3 1286 | .long vector4 1287 | .long vector5 1288 | .long vector6 1289 | .long vector7 1290 | .long vector8 1291 | .long vector9 1292 | .long vector10 1293 | .long vector11 1294 | .long vector12 1295 | .long vector13 1296 | .long vector14 1297 | .long vector15 1298 | .long vector16 1299 | .long vector17 1300 | .long vector18 1301 | .long vector19 1302 | .long vector20 1303 | .long vector21 1304 | .long vector22 1305 | .long vector23 1306 | .long vector24 1307 | .long vector25 1308 | .long vector26 1309 | .long vector27 1310 | .long vector28 1311 | .long vector29 1312 | .long vector30 1313 | .long vector31 1314 | .long vector32 1315 | .long vector33 1316 | .long vector34 1317 | .long vector35 1318 | .long vector36 1319 | .long vector37 1320 | .long vector38 1321 | .long vector39 1322 | .long vector40 1323 | .long vector41 1324 | .long vector42 1325 | .long vector43 1326 | .long vector44 1327 | .long vector45 1328 | .long vector46 1329 | .long vector47 1330 | .long vector48 1331 | .long vector49 1332 | .long vector50 1333 | .long vector51 1334 | .long vector52 1335 | .long vector53 1336 | .long vector54 1337 | .long vector55 1338 | .long vector56 1339 | .long vector57 1340 | .long vector58 1341 | .long vector59 1342 | .long vector60 1343 | .long vector61 1344 | .long vector62 1345 | .long vector63 1346 | .long vector64 1347 | .long vector65 1348 | .long vector66 1349 | .long vector67 1350 | .long vector68 1351 | .long vector69 1352 | .long vector70 1353 | .long vector71 1354 | .long vector72 1355 | .long vector73 1356 | .long vector74 1357 | .long vector75 1358 | .long vector76 1359 | .long vector77 1360 | .long vector78 1361 | .long vector79 1362 | .long vector80 1363 | .long vector81 1364 | .long vector82 1365 | .long vector83 1366 | .long vector84 1367 | .long vector85 1368 | .long vector86 1369 | .long vector87 1370 | .long vector88 1371 | .long vector89 1372 | .long vector90 1373 | .long vector91 1374 | .long vector92 1375 | .long vector93 1376 | .long vector94 1377 | .long vector95 1378 | .long vector96 1379 | .long vector97 1380 | .long vector98 1381 | .long vector99 1382 | .long vector100 1383 | .long vector101 1384 | .long vector102 1385 | .long vector103 1386 | .long vector104 1387 | .long vector105 1388 | .long vector106 1389 | .long vector107 1390 | .long vector108 1391 | .long vector109 1392 | .long vector110 1393 | .long vector111 1394 | .long vector112 1395 | .long vector113 1396 | .long vector114 1397 | .long vector115 1398 | .long vector116 1399 | .long vector117 1400 | .long vector118 1401 | .long vector119 1402 | .long vector120 1403 | .long vector121 1404 | .long vector122 1405 | .long vector123 1406 | .long vector124 1407 | .long vector125 1408 | .long vector126 1409 | .long vector127 1410 | .long vector128 1411 | .long vector129 1412 | .long vector130 1413 | .long vector131 1414 | .long vector132 1415 | .long vector133 1416 | .long vector134 1417 | .long vector135 1418 | .long vector136 1419 | .long vector137 1420 | .long vector138 1421 | .long vector139 1422 | .long vector140 1423 | .long vector141 1424 | .long vector142 1425 | .long vector143 1426 | .long vector144 1427 | .long vector145 1428 | .long vector146 1429 | .long vector147 1430 | .long vector148 1431 | .long vector149 1432 | .long vector150 1433 | .long vector151 1434 | .long vector152 1435 | .long vector153 1436 | .long vector154 1437 | .long vector155 1438 | .long vector156 1439 | .long vector157 1440 | .long vector158 1441 | .long vector159 1442 | .long vector160 1443 | .long vector161 1444 | .long vector162 1445 | .long vector163 1446 | .long vector164 1447 | .long vector165 1448 | .long vector166 1449 | .long vector167 1450 | .long vector168 1451 | .long vector169 1452 | .long vector170 1453 | .long vector171 1454 | .long vector172 1455 | .long vector173 1456 | .long vector174 1457 | .long vector175 1458 | .long vector176 1459 | .long vector177 1460 | .long vector178 1461 | .long vector179 1462 | .long vector180 1463 | .long vector181 1464 | .long vector182 1465 | .long vector183 1466 | .long vector184 1467 | .long vector185 1468 | .long vector186 1469 | .long vector187 1470 | .long vector188 1471 | .long vector189 1472 | .long vector190 1473 | .long vector191 1474 | .long vector192 1475 | .long vector193 1476 | .long vector194 1477 | .long vector195 1478 | .long vector196 1479 | .long vector197 1480 | .long vector198 1481 | .long vector199 1482 | .long vector200 1483 | .long vector201 1484 | .long vector202 1485 | .long vector203 1486 | .long vector204 1487 | .long vector205 1488 | .long vector206 1489 | .long vector207 1490 | .long vector208 1491 | .long vector209 1492 | .long vector210 1493 | .long vector211 1494 | .long vector212 1495 | .long vector213 1496 | .long vector214 1497 | .long vector215 1498 | .long vector216 1499 | .long vector217 1500 | .long vector218 1501 | .long vector219 1502 | .long vector220 1503 | .long vector221 1504 | .long vector222 1505 | .long vector223 1506 | .long vector224 1507 | .long vector225 1508 | .long vector226 1509 | .long vector227 1510 | .long vector228 1511 | .long vector229 1512 | .long vector230 1513 | .long vector231 1514 | .long vector232 1515 | .long vector233 1516 | .long vector234 1517 | .long vector235 1518 | .long vector236 1519 | .long vector237 1520 | .long vector238 1521 | .long vector239 1522 | .long vector240 1523 | .long vector241 1524 | .long vector242 1525 | .long vector243 1526 | .long vector244 1527 | .long vector245 1528 | .long vector246 1529 | .long vector247 1530 | .long vector248 1531 | .long vector249 1532 | .long vector250 1533 | .long vector251 1534 | .long vector252 1535 | .long vector253 1536 | .long vector254 1537 | .long vector255 1538 | --------------------------------------------------------------------------------