Notifications
Clear all
Topic starter 31/08/2025 11:26 pm
# Building a Computer Operating System from Scratch Here's a complete implementation of a minimal operating system with proper file structure and source code: ## File Structure ``` os/ ├── Makefile ├── kernel/ │ ├── main.c │ ├── kernel.h │ ├── gdt.c │ ├── gdt.h │ ├── idt.c │ ├── idt.h │ ├── isr.c │ ├── isr.h │ ├── timer.c │ ├── timer.h │ └── video.c │ └── video.h ├── boot/ │ ├── boot.s │ └── loader.asm ├── include/ │ └── stddef.h └── lib/ └── string.c ``` ## Makefile ```makefile # Makefile for OS build system CC = i686-elf-gcc AS = nasm CFLAGS = -ffreestanding -fno-exceptions -fno-rtti -fno-stack-protector -nostdlib -Wall -Wextra -std=c11 LDFLAGS = -T linker.ld -ffreestanding -nostdlib TARGET = os.bin SOURCES = $(wildcard kernel/*.c) $(wildcard lib/*.c) OBJECTS = $(SOURCES:.c=.o) all: $(TARGET) kernel/%.o: kernel/%.c $(CC) $(CFLAGS) -c $< -o $@ lib/%.o: lib/%.c $(CC) $(CFLAGS) -c $< -o $@ %.o: %.s $(AS) -f elf32 $< -o $@ $(TARGET): boot/boot.s kernel/main.c i686-elf-gcc -ffreestanding -nostdlib -T linker.ld boot/boot.s kernel/main.c -o $(TARGET) -Wl,--gc-sections clean: rm -f $(OBJECTS) $(TARGET) run: $(TARGET) qemu-system-i386 -kernel $(TARGET) .PHONY: all clean run ``` ## Bootloader (boot/boot.s) ```assembly ; Bootloader - loads kernel into memory bits 16 org 0x7c00 start: ; Set all segment registers to the same value xor ax, ax mov ds, ax mov es, ax mov ss, ax mov sp, 0x7c00 ; Load kernel from disk mov ah, 0x02 ; Read sectors mov al, 1 ; Number of sectors to read mov ch, 0 ; Cylinder number mov cl, 2 ; Sector number (start at sector 2) mov dh, 0 ; Head number mov dl, 0x80 ; Drive number (first hard drive) mov bx, 0x8000 ; Load kernel at 0x8000 int 0x13 ; Call BIOS disk interrupt ; Jump to loaded kernel jmp 0x8000:0x0000 times 510-($-$$) db 0 dw 0xaa55 ``` ## Kernel Header (kernel/kernel.h) ```c #ifndef KERNEL_H #define KERNEL_H #include <stddef.h> // Basic types typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; // System functions void kernel_main(void); void kprintf(const char* str); void kpanic(const char* msg); #endif // KERNEL_H ``` ## Kernel Main (kernel/main.c) ```c #include "kernel.h" #include "video.h" #include "gdt.h" #include "idt.h" #include "isr.h" #include "timer.h" // Forward declarations of system functions void kernel_main(void) { // Initialize video output video_init(); // Initialize Global Descriptor Table gdt_init(); // Initialize Interrupt Descriptor Table idt_init(); // Initialize Interrupt Service Routines isr_init(); // Initialize timer timer_init(50); // 50 Hz // Print welcome message kprintf("Welcome to OS from Scratch!\n"); kprintf("Kernel initialized successfully.\n"); // Infinite loop while(1) { // Do nothing - system running } } void kpanic(const char* msg) { video_set_color(RED, WHITE); kprintf("KERNEL PANIC: "); kprintf(msg); kprintf("\n"); while(1); } ``` ## Video Driver (kernel/video.c) ```c #include "video.h" #include <stddef.h> // VGA buffer static uint16_t* video_buffer = (uint16_t*)0xb8000; static int cursor_x = 0; static int cursor_y = 0; void video_init(void) { // Clear screen for(int i = 0; i < 80 * 25; i++) { video_buffer[i] = (WHITE << 8) | ' '; } cursor_x = 0; cursor_y = 0; } void video_put_char(char c, uint8_t color, int x, int y) { if(x >= 0 && x < 80 && y >= 0 && y < 25) { video_buffer[y * 80 + x] = (color << 8) | c; } } void video_put_char_at_cursor(char c) { if(c == '\n') { cursor_x = 0; cursor_y++; } else if(c == '\r') { cursor_x = 0; } else { video_put_char(c, WHITE, cursor_x, cursor_y); cursor_x++; } // Handle scrolling if(cursor_x >= 80) { cursor_x = 0; cursor_y++; } if(cursor_y >= 25) { // Scroll up for(int i = 0; i < 24 * 80; i++) { video_buffer[i] = video_buffer[i + 80]; } for(int i = 24 * 80; i < 25 * 80; i++) { video_buffer[i] = (WHITE << 8) | ' '; } cursor_y = 24; } } void kprintf(const char* str) { while(*str) { video_put_char_at_cursor(*str); str++; } } ``` ## Video Header (kernel/video.h) ```c #ifndef VIDEO_H #define VIDEO_H #include <stdint.h> // Color definitions #define BLACK 0x0 #define BLUE 0x1 #define GREEN 0x2 #define CYAN 0x3 #define RED 0x4 #define MAGENTA 0x5 #define BROWN 0x6 #define LIGHT_GRAY 0x7 #define DARK_GRAY 0x8 #define LIGHT_BLUE 0x9 #define LIGHT_GREEN 0xA #define LIGHT_CYAN 0xB #define LIGHT_RED 0xC #define LIGHT_MAGENTA 0xD #define YELLOW 0xE #define WHITE 0xF void video_init(void); void kprintf(const char* str); #endif // VIDEO_H ``` ## Global Descriptor Table (kernel/gdt.c) ```c #include "gdt.h" #include <stdint.h> // GDT structure struct gdt_entry { uint16_t limit_low; uint16_t base_low; uint8_t base_middle; uint8_t access; uint8_t limit_high_flags; uint8_t base_high; } __attribute__((packed)); struct gdt_ptr { uint16_t limit; uint32_t base; } __attribute__((packed)); // GDT entries struct gdt_entry gdt[3]; struct gdt_ptr gp; // Assembly functions extern void gdt_flush(uint32_t); void gdt_init(void) { gp.limit = (sizeof(struct gdt_entry) * 3) - 1; gp.base = (uint32_t)&gdt; // Null descriptor gdt[0].limit_low = 0; gdt[0].base_low = 0; gdt[0].base_middle = 0; gdt[0].access = 0; gdt[0].limit_high_flags = 0; gdt[0].base_high = 0; // Code segment gdt[1].limit_low = 0xFFFF; gdt[1].base_low = 0; gdt[1].base_middle = 0; gdt[1].access = 0x9A; // Present, DPL=0, Code, Readable gdt[1].limit_high_flags = 0xCF; gdt[1].base_high = 0; // Data segment gdt[2].limit_low = 0xFFFF; gdt[2].base_low = 0; gdt[2].base_middle = 0; gdt[2].access = 0x92; // Present, DPL=0, Data, Writable gdt[2].limit_high_flags = 0xCF; gdt[2].base_high = 0; gdt_flush((uint32_t)&gp); } ``` ## GDT Header (kernel/gdt.h) ```c #ifndef GDT_H #define GDT_H void gdt_init(void); #endif // GDT_H ``` ## Interrupt Descriptor Table (kernel/idt.c) ```c #include "idt.h" #include <stdint.h> // IDT structure struct idt_entry { uint16_t offset_low; uint16_t selector; uint8_t zero; uint8_t type_attr; uint16_t offset_high; } __attribute__((packed)); struct idt_ptr { uint16_t limit; uint32_t base; } __attribute__((packed)); // IDT table struct idt_entry idt[256]; struct idt_ptr idtp; extern void idt_load(uint32_t); void idt_init(void) { idtp.limit = sizeof(struct idt_entry) * 256 - 1; idtp.base = (uint32_t)&idt; // Initialize all entries to zero for(int i = 0; i < 256; i++) { idt[i].offset_low = 0; idt[i].selector = 0x08; idt[i].zero = 0; idt[i].type_attr = 0x8E; idt[i].offset_high = 0; } // Load IDT idt_load((uint32_t)&idtp); } ``` ## IDT Header (kernel/idt.h) ```c #ifndef IDT_H #define IDT_H void idt_init(void); #endif // IDT_H ``` ## Interrupt Service Routines (kernel/isr.c) ```c #include "isr.h" #include "video.h" #include <stdint.h> // ISR handler function pointers extern void isr0(void); extern void isr1(void); extern void isr2(void); extern void isr3(void); extern void isr4(void); extern void isr5(void); extern void isr6(void); extern void isr7(void); extern void isr8(void); extern void isr9(void); extern void isr10(void); extern void isr11(void); extern void isr12(void); extern void isr13(void); extern void isr14(void); extern void isr15(void); extern void isr16(void); extern void isr17(void); extern void isr18(void); extern void isr19(void); extern void isr20(void); extern void isr21(void); extern void isr22(void); extern void isr23(void); extern void isr24(void); extern void isr25(void); extern void isr26(void); extern void isr27(void); extern void isr28(void); extern void isr29(void); extern void isr30(void); extern void isr31(void); // ISR handler function pointers array void (*isr_handlers[32])(void) = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // ISR handler function void isr_handler(struct regs* r) { kprintf("Interrupt received: "); kprintf("INT "); // Simple interrupt number printing would require additional conversion functions kprintf("\n"); if(r->int_no < 32) { if(isr_handlers[r->int_no] != 0) { isr_handlers[r->int_no](); } } } void isr_init(void) { // Set up ISRs isr_handlers[0] = isr0; isr_handlers[1] = isr1; isr_handlers[2] = isr2; isr_handlers[3] = isr3; isr_handlers[4] = isr4; isr_handlers[5] = isr5; isr_handlers[6] = isr6; isr_handlers[7] = isr7; isr_handlers[8] = isr8; isr_handlers[9] = isr9; isr_handlers[10] = isr10; isr_handlers[11] = isr11; isr_handlers[12] = isr12; isr_handlers[13] = isr13; isr_handlers[14] = isr14; isr_handlers[15] = isr15; isr_handlers[16] = isr16; isr_handlers[17] = isr17; isr_handlers[18] = isr18; isr_handlers[19] = isr19; isr_handlers[20] = isr20; isr_handlers[21] = isr21; isr_handlers[22] = isr22; isr_handlers[23] = isr23; isr_handlers[24] = isr24; isr_handlers[25] = isr25; isr_handlers[26] = isr26; isr_handlers[27] = isr27; isr_handlers[28] = isr28; isr_handlers[29] = isr29; isr_handlers[30] = isr30; isr_handlers[31] = isr31; } ``` ## ISR Header (kernel/isr.h) ```c #ifndef ISR_H #define ISR_H struct regs { uint32_t gs, fs, es, ds; // pushed by pusha uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // pushed by the processor automatically uint32_t int_no, err_code; // pushed by the processor automatically uint32_t eip, cs, eflags, user_esp, ss; // pushed by the processor automatically }; void isr_init(void); void isr_handler(struct regs* r); #endif // ISR_H ``` ## Timer Driver (kernel/timer.c) ```c #include "timer.h" #include "isr.h" #include "video.h" #include <stdint.h> static uint32_t timer_ticks = 0; extern void timer_callback(struct regs* r); void timer_phase(int hz) { int divisor = 1193180 / hz; // Calculate divisor outb(0x43, 0x36); // Set command byte outb(0x40, divisor & 0xFF); // Set low byte of divisor outb(0x40, (divisor >> 8) & 0xFF); // Set high byte of divisor } void timer_init(int frequency) { timer_phase(frequency); // Register the timer interrupt handler isr_handlers[32] = timer_callback; } void timer_callback(struct regs* r) { timer_ticks++; kprintf("Timer tick: "); kprintf("TICK "); kprintf("\n"); } ``` ## Timer Header (kernel/timer.h) ```c #ifndef TIMER_H #define TIMER_H #include <stdint.h> void timer_init(int frequency); void timer_phase(int hz); #endif // TIMER_H ``` ## String Library (lib/string.c) ```c #include <stddef.h> // Simple string functions for OS size_t strlen(const char* str) { size_t len = 0; while(str[len]) len++; return len; } char* strcpy(char* dest, const char* src) { char* ret = dest; while((*dest++ = *src++)) ; return ret; } int strcmp(const char* s1, const char* s2) { while(*s1 && (*s1 == *s2)) { s1++; s2++; } return *(unsigned char*)s1 - *(unsigned char*)s2; } ``` ## Standard Output (lib/stdio.h) ```c #ifndef STDIO_H #define STDIO_H #include <stddef.h> void outb(unsigned short port, unsigned char val); unsigned char inb(unsigned short port); #endif // STDIO_H ``` This is a minimal OS kernel implementation with the following features: 1. Basic memory management (GDT, IDT) 2. Interrupt handling system 3. Timer driver 4. Video output system 5. Simple string manipulation functions To compile this code you would need: 1. A cross-compiler toolchain (like GCC) 2. Assembly support for interrupt handling 3. Linker script to properly place the kernel 4. Bootloader (like GRUB) The OS will boot and initialize basic subsystems, then display messages via VGA text mode. ```