🖥️ Build Your Own Operating System

Complete step-by-step guide to creating a simple operating system from scratch. Based on Operating Systems: Three Easy Pieces (OSTEP) and the OSDev Wiki.

🔢 Essential OS Concepts

⚡ CPU Scheduling

SJF → RR → MLFQ
Shortest Job First → Round Robin → Multi-Level Feedback Queue

💾 Memory Hierarchy

Reg → Cache → RAM → Disk
Registers → Cache → Main Memory → Secondary Storage

🔌 Virtual Memory

VA = VPN + Offset
Virtual Address = Virtual Page Number + Offset

🔄 Context Switch

Save State → Switch → Restore
Save process state, switch to new process, restore its state

📚 OS Development Roadmap

User Applications ↓ System Calls (API) ↓ Kernel (Process, Memory, File mgmt) ↓ Hardware Abstraction Layer ↓ Hardware (CPU, RAM, Disk)

⚠️ Development Environment Setup

Required: Linux or macOS (Windows needs WSL or VM)
Compiler: Cross-compiler for your target architecture (i686-elf-gcc)
Emulator: QEMU for testing without real hardware
Tools: NASM (assembler), GRUB (bootloader), Make
Backup: Always test in virtual machine first!

1 Bootloader (Assembly)

The bootloader is the first code that runs when your computer starts. It sets up the CPU in protected mode and loads the kernel.

boot.asm - Simple Boot Sector

bits 16          ; 16-bit real mode
org 0x7C00       ; BIOS loads boot sector at 0x7C00

start:
    ; Disable interrupts
    cli
    
    ; Set up segments
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00
    
    ; Print boot message
    mov si, boot_msg
    call print_string
    
    ; Load kernel from disk
    mov bx, 0x7E00  ; Load kernel after bootloader
    mov ah, 0x02    ; Read sectors
    mov al, 10      ; Number of sectors to read
    mov ch, 0       ; Cylinder 0
    mov cl, 2       ; Sector 2 (boot is sector 1)
    mov dh, 0       ; Head 0
    int 0x13
    
    ; Jump to kernel
    jmp 0x7E00
    
; String printing function
print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    int 0x10
    jmp print_string
.done:
    ret

boot_msg db "Booting MyOS...", 0

; Boot signature
times 510-($-$$) db 0
dw 0xAA55          ; Boot sector magic number

linker.ld - Linker Script

ENTRY(start)
OUTPUT_FORMAT(elf32-i386)

SECTIONS {
    . = 1M;  /* Load kernel at 1MB (conventional) */
    
    .text BLOCK(4K) : ALIGN(4K) {
        *(.multiboot)
        *(.text)
    }
    
    .rodata BLOCK(4K) : ALIGN(4K) {
        *(.rodata)
    }
    
    .data BLOCK(4K) : ALIGN(4K) {
        *(.data)
    }
    
    .bss BLOCK(4K) : ALIGN(4K) {
        *(COMMON)
        *(.bss)
    }
}

Compile with: nasm -f elf32 boot.asm -o boot.o

2 Minimal Kernel (C)

A simple kernel that prints to screen and halts. This is your OS's core.

kernel.c - Basic Kernel

#include <stdint.h>

/* VGA text mode buffer */
volatile uint16_t* vga_buffer = (uint16_t*)0xB8000;
const int VGA_WIDTH = 80;
const int VGA_HEIGHT = 25;

/* Current cursor position */
int cursor_x = 0;
int cursor_y = 0;

/* Clear screen */
void clear_screen(uint8_t color) {
    uint16_t blank = (color << 8) | ' ';
    for (int i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) {
        vga_buffer[i] = blank;
    }
    cursor_x = 0;
    cursor_y = 0;
}

/* Print character */
void putchar(char c, uint8_t color) {
    if (c == '\n') {
        cursor_x = 0;
        cursor_y++;
    } else if (c == '\r') {
        cursor_x = 0;
    } else {
        int index = cursor_y * VGA_WIDTH + cursor_x;
        vga_buffer[index] = (color << 8) | c;
        cursor_x++;
        if (cursor_x >= VGA_WIDTH) {
            cursor_x = 0;
            cursor_y++;
        }
    }
    
    /* Scroll if needed */
    if (cursor_y >= VGA_HEIGHT) {
        for (int i = 0; i < VGA_WIDTH * (VGA_HEIGHT - 1); i++) {
            vga_buffer[i] = vga_buffer[i + VGA_WIDTH];
        }
        for (int i = VGA_WIDTH * (VGA_HEIGHT - 1); i < VGA_WIDTH * VGA_HEIGHT; i++) {
            vga_buffer[i] = (color << 8) | ' ';
        }
        cursor_y = VGA_HEIGHT - 1;
    }
}

/* Print string */
void print(const char* str, uint8_t color) {
    for (int i = 0; str[i] != '\0'; i++) {
        putchar(str[i], color);
    }
}

/* Kernel main function */
void kernel_main(void) {
    /* Clear screen with blue background */
    clear_screen(0x1F);  /* White on blue */
    
    /* Print welcome message */
    print("Welcome to MyOS!\n", 0x0F);
    print("Kernel loaded successfully.\n\n", 0x0F);
    
    /* System information */
    print("System Status:\n", 0x0A);
    print("- Running in 32-bit protected mode\n", 0x07);
    print("- VGA text mode: 80x25\n", 0x07);
    print("- Memory available: TBD\n", 0x07);
    
    /* Hang forever */
    while (1) {
        __asm__("hlt");  /* Halt CPU */
    }
}

kernel_entry.asm - Kernel Entry Point

global start
extern kernel_main

section .text
bits 32
start:
    ; Set up stack pointer
    mov esp, stack_top
    
    ; Call kernel main
    call kernel_main
    
    ; If kernel_main returns, halt
    hlt

section .bss
stack_bottom:
    resb 16384  ; 16KB stack
stack_top:

Compile with: i686-elf-gcc -c kernel.c -o kernel.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra

3 Build System (Makefile)

Automate compilation and linking with a Makefile.

Makefile - Build Automation

# Cross-compiler prefix
CC = i686-elf-gcc
AS = nasm
LD = i686-elf-ld

# Flags
CFLAGS = -std=gnu99 -ffreestanding -O2 -Wall -Wextra
LDFLAGS = -T linker.ld -ffreestanding -O2 -nostdlib

# Output files
KERNEL = myos.bin
ISO = myos.iso

# Object files
OBJS = boot.o kernel_entry.o kernel.o

all: $(ISO)

# Compile bootloader
boot.o: boot.asm
	$(AS) -f elf32 $< -o $@

# Compile kernel entry
kernel_entry.o: kernel_entry.asm
	$(AS) -f elf32 $< -o $@

# Compile kernel C code
kernel.o: kernel.c
	$(CC) $(CFLAGS) -c $< -o $@

# Link kernel
myos.bin: $(OBJS)
	$(LD) $(LDFLAGS) $(OBJS) -o $@

# Create bootable ISO
$(ISO): myos.bin
	mkdir -p isodir/boot/grub
	cp myos.bin isodir/boot/
	cp grub.cfg isodir/boot/grub/
	grub-mkrescue -o $@ isodir

# Run in QEMU
run: $(ISO)
	qemu-system-i386 -cdrom $(ISO)

# Clean build files
clean:
	rm -f *.o *.bin *.iso
	rm -rf isodir

.PHONY: all run clean

4 Interrupts & System Calls

Handle hardware interrupts and implement system call interface.

interrupts.c - Interrupt Descriptor Table

#include <stdint.h>

/* IDT Entry */
struct idt_entry {
    uint16_t base_low;
    uint16_t selector;
    uint8_t zero;
    uint8_t flags;
    uint16_t base_high;
} __attribute__((packed));

/* IDT Pointer */
struct idt_ptr {
    uint16_t limit;
    uint32_t base;
} __attribute__((packed));

/* IDT */
struct idt_entry idt[256];
struct idt_ptr idtp;

/* External assembly function to load IDT */
extern void idt_load();

/* Set IDT gate */
void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags) {
    idt[num].base_low = (base & 0xFFFF);
    idt[num].base_high = (base >> 16) & 0xFFFF;
    idt[num].selector = sel;
    idt[num].zero = 0;
    idt[num].flags = flags;
}

/* Initialize IDT */
void init_idt() {
    idtp.limit = (sizeof(struct idt_entry) * 256) - 1;
    idtp.base = (uint32_t)&idt;
    
    /* Clear IDT */
    for (int i = 0; i < 256; i++) {
        idt_set_gate(i, 0, 0, 0);
    }
    
    /* Load IDT */
    idt_load();
}

/* Basic system call handler */
void syscall_handler() {
    __asm__ volatile (
        "pusha\n"
        "call handle_syscall\n"
        "popa\n"
        "iret\n"
    );
}

/* Example system calls */
#define SYS_WRITE 1
#define SYS_EXIT  2

void handle_syscall() {
    uint32_t syscall_num, arg1, arg2, arg3;
    
    __asm__ volatile (
        "mov %%eax, %0\n"
        "mov %%ebx, %1\n"
        "mov %%ecx, %2\n"
        "mov %%edx, %3\n"
        : "=r"(syscall_num), "=r"(arg1), "=r"(arg2), "=r"(arg3)
    );
    
    switch(syscall_num) {
        case SYS_WRITE:
            /* Write to screen */
            print((char*)arg1, arg2);
            break;
        case SYS_EXIT:
            /* Exit process */
            while(1) __asm__("hlt");
            break;
    }
}

5 Memory Management

Implement physical and virtual memory management with paging.

memory.c - Physical Memory Manager

#include <stdint.h>
#include <stddef.h>

#define PAGE_SIZE 4096
#define MEMORY_SIZE 0x1000000  /* 16MB for example */

/* Bitmap for tracking free pages */
uint32_t* memory_bitmap;
uint32_t bitmap_size;

/* Initialize memory manager */
void init_memory() {
    /* Place bitmap at 1MB (after kernel) */
    memory_bitmap = (uint32_t*)0x100000;
    
    /* Calculate bitmap size */
    bitmap_size = MEMORY_SIZE / PAGE_SIZE / 32;
    if ((MEMORY_SIZE / PAGE_SIZE) % 32) bitmap_size++;
    
    /* Mark all memory as free (0 = free, 1 = used) */
    for (uint32_t i = 0; i < bitmap_size; i++) {
        memory_bitmap[i] = 0;
    }
    
    /* Mark kernel memory as used (first 2MB) */
    uint32_t kernel_pages = 0x200000 / PAGE_SIZE;
    for (uint32_t i = 0; i < kernel_pages / 32; i++) {
        memory_bitmap[i] = 0xFFFFFFFF;
    }
}

/* Allocate a page */
void* alloc_page() {
    for (uint32_t i = 0; i < bitmap_size; i++) {
        if (memory_bitmap[i] != 0xFFFFFFFF) {
            /* Find free bit */
            for (int j = 0; j < 32; j++) {
                if (!(memory_bitmap[i] & (1 << j))) {
                    /* Mark as used */
                    memory_bitmap[i] |= (1 << j);
                    
                    /* Calculate physical address */
                    uint32_t page_num = i * 32 + j;
                    return (void*)(page_num * PAGE_SIZE);
                }
            }
        }
    }
    return NULL;  /* Out of memory */
}

/* Free a page */
void free_page(void* addr) {
    uint32_t page_num = (uint32_t)addr / PAGE_SIZE;
    uint32_t i = page_num / 32;
    uint32_t j = page_num % 32;
    
    /* Mark as free */
    memory_bitmap[i] &= ~(1 << j);
}

/* Page Directory Entry */
typedef struct {
    uint32_t present    : 1;
    uint32_t rw         : 1;
    uint32_t user       : 1;
    uint32_t writethrough : 1;
    uint32_t cache      : 1;
    uint32_t accessed   : 1;
    uint32_t dirty      : 1;
    uint32_t page_size  : 1;
    uint32_t global     : 1;
    uint32_t available  : 3;
    uint32_t frame      : 20;
} page_dir_entry;

/* Enable paging */
void enable_paging() {
    /* Create page directory at 0x200000 */
    page_dir_entry* page_dir = (page_dir_entry*)0x200000;
    
    /* Clear page directory */
    for (int i = 0; i < 1024; i++) {
        page_dir[i].present = 0;
    }
    
    /* Map first 4MB identity */
    for (uint32_t i = 0; i < 1024; i++) {
        page_dir[i].present = 1;
        page_dir[i].rw = 1;
        page_dir[i].frame = i;
    }
    
    /* Load page directory and enable paging */
    __asm__ volatile (
        "mov %0, %%cr3\n"
        "mov %%cr0, %%eax\n"
        "or $0x80000000, %%eax\n"
        "mov %%eax, %%cr0\n"
        :: "r"(page_dir)
    );
}

6 Process Scheduling

Implement a simple Round Robin scheduler.

scheduler.c - Simple Scheduler

#include <stdint.h>

#define MAX_PROCESSES 64
#define STACK_SIZE 4096

/* Process Control Block */
typedef struct {
    uint32_t pid;
    uint32_t esp;      /* Stack pointer */
    uint32_t eip;      /* Instruction pointer */
    uint32_t state;    /* 0=ready, 1=running, 2=blocked */
    uint32_t priority;
    uint32_t stack[STACK_SIZE];
} pcb_t;

pcb_t processes[MAX_PROCESSES];
pcb_t* current_process = NULL;
uint32_t next_pid = 1;

/* Initialize scheduler */
void init_scheduler() {
    for (int i = 0; i < MAX_PROCESSES; i++) {
        processes[i].pid = 0;  /* 0 = unused */
    }
    
    /* Create idle process */
    create_process(NULL);  /* Kernel idle */
}

/* Create a new process */
uint32_t create_process(void (*entry_point)()) {
    for (int i = 0; i < MAX_PROCESSES; i++) {
        if (processes[i].pid == 0) {
            processes[i].pid = next_pid++;
            processes[i].state = 0;  /* Ready */
            processes[i].priority = 1;
            
            /* Set up stack */
            processes[i].esp = (uint32_t)&processes[i].stack[STACK_SIZE - 1];
            processes[i].eip = (uint32_t)entry_point;
            
            /* Push fake context for iret */
            uint32_t* stack_top = (uint32_t*)processes[i].esp;
            *(--stack_top) = 0x202;  /* EFLAGS */
            *(--stack_top) = 0x08;   /* CS */
            *(--stack_top) = processes[i].eip;  /* EIP */
            *(--stack_top) = 0;  /* EAX */
            *(--stack_top) = 0;  /* ECX */
            *(--stack_top) = 0;  /* EDX */
            *(--stack_top) = 0;  /* EBX */
            *(--stack_top) = (uint32_t)stack_top;  /* ESP */
            *(--stack_top) = 0;  /* EBP */
            *(--stack_top) = 0;  /* ESI */
            *(--stack_top) = 0;  /* EDI */
            
            processes[i].esp = (uint32_t)stack_top;
            
            return processes[i].pid;
        }
    }
    return 0;  /* No free slots */
}

/* Round Robin Scheduler */
void schedule() {
    static uint32_t current_pid = 0;
    
    /* Save current process context */
    if (current_process) {
        __asm__ volatile (
            "mov %%esp, %0\n"
            "mov %%eip, %1\n"
            : "=r"(current_process->esp), "=r"(current_process->eip)
        );
    }
    
    /* Find next ready process */
    for (int i = 0; i < MAX_PROCESSES; i++) {
        uint32_t idx = (current_pid + i + 1) % MAX_PROCESSES;
        if (processes[idx].pid != 0 && processes[idx].state == 0) {
            current_pid = idx;
            current_process = &processes[idx];
            current_process->state = 1;  /* Running */
            
            /* Switch to process */
            __asm__ volatile (
                "mov %0, %%esp\n"
                "ret\n"
                :: "r"(current_process->esp)
            );
            break;
        }
    }
    
    /* If no process found, idle */
    __asm__("hlt");
}

/* Example process */
void process1() {
    while(1) {
        print("Process 1 running\n", 0x07);
        for (int i = 0; i < 1000000; i++);  /* Busy wait */
        schedule();  /* Yield CPU */
    }
}

void process2() {
    while(1) {
        print("Process 2 running\n", 0x07);
        for (int i = 0; i < 1000000; i++);
        schedule();
    }
}

7 Simple File System

Implement a basic FAT-like file system.

filesystem.c - Basic FAT Implementation

#include <stdint.h>
#include <stddef.h>

#define BLOCK_SIZE 512
#define FAT_ENTRIES 128
#define ROOT_DIR_ENTRIES 16

/* File Allocation Table Entry */
typedef struct {
    uint8_t used : 1;
    uint8_t next_block : 7;  /* 0-127 */
} fat_entry_t;

/* Directory Entry */
typedef struct {
    char name[11];      /* 8.3 format */
    uint8_t attributes; /* Unused in this simple version */
    uint32_t size;      /* File size in bytes */
    uint8_t first_block;/* First FAT block */
} dir_entry_t;

/* File System */
fat_entry_t fat[FAT_ENTRIES];
dir_entry_t root_dir[ROOT_DIR_ENTRIES];
uint8_t* disk_blocks;  /* Simulated disk */

/* Initialize file system */
void init_filesystem() {
    /* Clear FAT */
    for (int i = 0; i < FAT_ENTRIES; i++) {
        fat[i].used = 0;
        fat[i].next_block = 0;
    }
    
    /* Clear root directory */
    for (int i = 0; i < ROOT_DIR_ENTRIES; i++) {
        root_dir[i].name[0] = '\0';
        root_dir[i].first_block = 0;
        root_dir[i].size = 0;
    }
    
    /* "Allocate" disk blocks */
    disk_blocks = (uint8_t*)0x300000;  /* 3MB mark */
}

/* Find free FAT entry */
int find_free_fat() {
    for (int i = 1; i < FAT_ENTRIES; i++) {  /* Skip 0 */
        if (!fat[i].used) {
            return i;
        }
    }
    return -1;  /* No space */
}

/* Create file */
int create_file(const char* name, uint32_t size) {
    /* Find free directory entry */
    int dir_index = -1;
    for (int i = 0; i < ROOT_DIR_ENTRIES; i++) {
        if (root_dir[i].name[0] == '\0') {
            dir_index = i;
            break;
        }
    }
    if (dir_index == -1) return -1;
    
    /* Find FAT blocks */
    int blocks_needed = (size + BLOCK_SIZE - 1) / BLOCK_SIZE;
    if (blocks_needed == 0) blocks_needed = 1;
    
    int first_block = find_free_fat();
    if (first_block == -1) return -1;
    
    int current_block = first_block;
    for (int i = 0; i < blocks_needed; i++) {
        fat[current_block].used = 1;
        if (i == blocks_needed - 1) {
            fat[current_block].next_block = 0;  /* End of chain */
        } else {
            int next_block = find_free_fat();
            if (next_block == -1) {
                /* Clean up allocated blocks */
                while (current_block != 0) {
                    int temp = fat[current_block].next_block;
                    fat[current_block].used = 0;
                    current_block = temp;
                }
                return -1;
            }
            fat[current_block].next_block = next_block;
            current_block = next_block;
        }
    }
    
    /* Fill directory entry */
    int name_len = 0;
    while (name[name_len] && name_len < 10) {
        root_dir[dir_index].name[name_len] = name[name_len];
        name_len++;
    }
    root_dir[dir_index].name[name_len] = '\0';
    root_dir[dir_index].size = size;
    root_dir[dir_index].first_block = first_block;
    
    return dir_index;
}

/* Read file */
int read_file(const char* name, void* buffer, uint32_t size) {
    /* Find file */
    int dir_index = -1;
    for (int i = 0; i < ROOT_DIR_ENTRIES; i++) {
        int match = 1;
        for (int j = 0; j < 11; j++) {
            if (root_dir[i].name[j] != name[j]) {
                match = 0;
                break;
            }
            if (name[j] == '\0') break;
        }
        if (match) {
            dir_index = i;
            break;
        }
    }
    if (dir_index == -1) return -1;
    
    /* Read file */
    uint32_t bytes_read = 0;
    int current_block = root_dir[dir_index].first_block;
    uint8_t* dest = (uint8_t*)buffer;
    
    while (current_block != 0 && bytes_read < size) {
        uint8_t* src = disk_blocks + (current_block * BLOCK_SIZE);
        uint32_t to_copy = BLOCK_SIZE;
        if (to_copy > size - bytes_read) {
            to_copy = size - bytes_read;
        }
        
        /* Copy block */
        for (uint32_t i = 0; i < to_copy; i++) {
            dest[bytes_read + i] = src[i];
        }
        bytes_read += to_copy;
        
        current_block = fat[current_block].next_block;
    }
    
    return bytes_read;
}

/* List files */
void list_files() {
    print("Files:\n", 0x0F);
    for (int i = 0; i < ROOT_DIR_ENTRIES; i++) {
        if (root_dir[i].name[0] != '\0') {
            print("- ", 0x07);
            print(root_dir[i].name, 0x07);
            print("\n", 0x07);
        }
    }
}

8 Complete OS Integration

Final kernel with all components integrated.

main.c - Complete Kernel

#include <stdint.h>

/* Function prototypes */
void clear_screen(uint8_t color);
void print(const char* str, uint8_t color);
void init_idt();
void init_memory();
void init_scheduler();
void init_filesystem();
void enable_paging();
void schedule();

/* Example processes */
void process1();
void process2();

/* Kernel main - everything starts here */
void kernel_main(void) {
    /* Basic setup */
    clear_screen(0x1F);
    print("MyOS Booting...\n\n", 0x0F);
    
    /* Initialize subsystems */
    print("Initializing IDT... ", 0x07);
    init_idt();
    print("[OK]\n", 0x0A);
    
    print("Initializing Memory... ", 0x07);
    init_memory();
    print("[OK]\n", 0x0A);
    
    print("Enabling Paging... ", 0x07);
    enable_paging();
    print("[OK]\n", 0x0A);
    
    print("Initializing Scheduler... ", 0x07);
    init_scheduler();
    print("[OK]\n", 0x0A);
    
    print("Initializing Filesystem... ", 0x07);
    init_filesystem();
    print("[OK]\n", 0x0A);
    
    /* Create some files */
    create_file("README.TXT", 1024);
    create_file("CONFIG.CFG", 512);
    
    /* Create processes */
    print("\nCreating processes...\n", 0x0F);
    create_process(process1);
    create_process(process2);
    
    /* Show system info */
    print("\n=== MyOS Ready ===\n", 0x0E);
    list_files();
    print("\nType 'help' for commands\n", 0x07);
    
    /* Start scheduler */
    schedule();
}

/* Simple shell */
void shell() {
    print("\nmyos> ", 0x0F);
    
    /* Very simple command parser */
    char cmd[32];
    /* ... command parsing logic ... */
    
    if (/* cmd == "help" */) {
        print("Commands: ls, ps, mem, help, exit\n", 0x07);
    } else if (/* cmd == "ls" */) {
        list_files();
    } else if (/* cmd == "ps" */) {
        print("PID 1: Process 1\n", 0x07);
        print("PID 2: Process 2\n", 0x07);
    } else if (/* cmd == "mem" */) {
        print("Memory: 16MB total\n", 0x07);
    } else if (/* cmd == "exit" */) {
        print("Goodbye!\n", 0x07);
        while(1) __asm__("hlt");
    } else {
        print("Unknown command\n", 0x0C);
    }
}

🚀 Building and Testing Your OS

Complete Build Commands

# 1. Install cross-compiler (Ubuntu/Debian)
sudo apt-get install build-essential nasm grub-pc-bin xorriso
wget https://github.com/lordmilko/i686-elf-tools/releases/download/7.1.0/i686-elf-tools-linux.zip
unzip i686-elf-tools-linux.zip
export PATH=$PATH:~/i686-elf-tools/bin

# 2. Build OS
make

# 3. Run in QEMU
make run

# 4. Or test in VirtualBox
# Convert ISO to VDI:
VBoxManage convertdd myos.iso myos.vdi --format VDI
# Create new VM with this VDI as boot disk

📚 Resources & Next Steps

💡 Project Ideas to Extend Your OS

1. Simple Shell

Implement a command-line interface with basic commands (ls, cat, echo)

2. GUI Framework

Add VESA graphics support and basic windowing system

3. Network Stack

Implement TCP/IP networking with Ethernet driver

4. Multi-core Support

Add SMP support for multiple CPUs

⚠️ Important Notes

Start Simple: Get basic boot → kernel → print working first
Use QEMU: Never test new OS code on real hardware initially
Debugging: Use QEMU's -s -S flags for GDB debugging
Backup Often: OS development can corrupt disks if bugs escape VM
Reference Code: Study xv6, Minix, or Linux 0.01 for inspiration
Community: Join OSDev.org forums for help

← Back to Wiki