From 0fd6a3e0d376b398d9e629fb2d8fabe858c7d29f Mon Sep 17 00:00:00 2001 From: pjht Date: Fri, 19 Oct 2018 07:59:38 -0500 Subject: [PATCH] Initial commit --- .gitignore | 5 + Makefile | 43 +++++ bochsrc.txt | 57 ++++++ boot/32bit-gdt.asm | 57 ++++++ boot/32bit-switch.asm | 21 +++ boot/boot.asm | 41 ++++ boot/disk.asm | 46 +++++ boot/kernel_entry.asm | 4 + boot/print.asm | 37 ++++ boot/print_hex.asm | 46 +++++ drivers/idt.c | 20 ++ drivers/idt.h | 39 ++++ drivers/interrupt.asm | 424 ++++++++++++++++++++++++++++++++++++++++++ drivers/isr.c | 146 +++++++++++++++ drivers/isr.h | 88 +++++++++ drivers/ports.c | 19 ++ drivers/ports.h | 9 + drivers/vga.c | 99 ++++++++++ drivers/vga.h | 30 +++ kernel/kernel.c | 37 ++++ libc/memory.c | 6 + libc/memory.h | 6 + libc/string.c | 34 ++++ libc/string.h | 8 + 24 files changed, 1322 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 bochsrc.txt create mode 100644 boot/32bit-gdt.asm create mode 100644 boot/32bit-switch.asm create mode 100644 boot/boot.asm create mode 100755 boot/disk.asm create mode 100644 boot/kernel_entry.asm create mode 100755 boot/print.asm create mode 100755 boot/print_hex.asm create mode 100644 drivers/idt.c create mode 100644 drivers/idt.h create mode 100644 drivers/interrupt.asm create mode 100644 drivers/isr.c create mode 100644 drivers/isr.h create mode 100644 drivers/ports.c create mode 100644 drivers/ports.h create mode 100644 drivers/vga.c create mode 100644 drivers/vga.h create mode 100644 kernel/kernel.c create mode 100644 libc/memory.c create mode 100644 libc/memory.h create mode 100644 libc/string.c create mode 100644 libc/string.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..639187b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.bin +*.dis +*.o +*.elf +*/*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c18da3a --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +C_SOURCES = $(wildcard kernel/*.c drivers/*.c libc/*.c) +HEADERS = $(wildcard kernel/*.h drivers/*.h libc/*.h) +OBJ = ${C_SOURCES:.c=.o drivers/interrupt.o} + +CC = /usr/local/bin/i386-elf-gcc +GDB = /usr/local/bin/i386-elf-gdb +CFLAGS = -g + +# First rule is run by default +os-image.bin: boot/boot.bin kernel.bin + cat $^ > os-image.bin + +# '--oformat binary' deletes all symbols as a collateral, so we don't need +# to 'strip' them manually on this case +kernel.bin: boot/kernel_entry.o ${OBJ} + i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary + +# Used for debugging purposes +kernel.elf: boot/kernel_entry.o ${OBJ} + i386-elf-ld -o $@ -Ttext 0x1000 $^ + +run: os-image.bin + qemu-system-i386 -fda os-image.bin + +# Open the connection to qemu and load our kernel-object file with symbols +debug: os-image.bin kernel.elf + qemu-system-i386 -s -fda os-image.bin & + ${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" + +# Generic rules for wildcards +# To make an object, always compile from its .c +%.o: %.c ${HEADERS} + ${CC} ${CFLAGS} -ffreestanding -c $< -o $@ + +%.o: %.asm + nasm $< -f elf -o $@ + +%.bin: %.asm + nasm $< -f bin -o $@ + +clean: + rm -rf *.bin *.dis *.o os-image.bin *.elf + rm -rf */*.o diff --git a/bochsrc.txt b/bochsrc.txt new file mode 100644 index 0000000..fa14b2d --- /dev/null +++ b/bochsrc.txt @@ -0,0 +1,57 @@ +# configuration file generated by Bochs +plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1, iodebug=1 +config_interface: textconfig +display_library: sdl2 +memory: host=32, guest=32 +romimage: file="/usr/local/Cellar/bochs/2.6.9_2/share/bochs/BIOS-bochs-latest", address=0x0, options=none +vgaromimage: file="/usr/local/Cellar/bochs/2.6.9_2/share/bochs/VGABIOS-lgpl-latest" +boot: floppy +floppy_bootsig_check: disabled=0 +floppya: type=1_44, 1_44="os-image.bin", status=inserted, write_protected=0 +# no floppyb +ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 +ata0-master: type=none +ata0-slave: type=none +ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15 +ata1-master: type=none +ata1-slave: type=none +ata2: enabled=0 +ata3: enabled=0 +optromimage1: file=none +optromimage2: file=none +optromimage3: file=none +optromimage4: file=none +optramimage1: file=none +optramimage2: file=none +optramimage3: file=none +optramimage4: file=none +pci: enabled=1, chipset=i440fx +vga: extension=vbe, update_freq=5, realtime=1 +cpu: count=1:1:1, ips=4000000, quantum=16, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 +cpuid: level=6, stepping=3, model=3, family=6, vendor_string="GenuineIntel", brand_string=" Intel(R) Pentium(R) 4 CPU " +cpuid: mmx=1, apic=xapic, simd=sse2, sse4a=0, misaligned_sse=0, sep=1, movbe=0, adx=0 +cpuid: aes=0, sha=0, xsave=0, xsaveopt=0, avx_f16c=0, avx_fma=0, bmi=0, xop=0, fma4=0 +cpuid: tbm=0, x86_64=1, 1g_pages=0, pcid=0, fsgsbase=0, smep=0, smap=0, mwait=1, vmx=1 +print_timestamps: enabled=0 +debugger_log: - +magic_break: enabled=0 +port_e9_hack: enabled=0 +private_colormap: enabled=0 +clock: sync=none, time0=local, rtc_sync=0 +# no cmosimage +# no loader +log: - +logprefix: %t%e%d +debug: action=ignore +info: action=report +error: action=report +panic: action=ask +keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none +mouse: type=ps2, enabled=0, toggle=ctrl+mbutton +speaker: enabled=1, mode=gui +parport1: enabled=1, file=none +parport2: enabled=0 +com1: enabled=1, mode=null +com2: enabled=0 +com3: enabled=0 +com4: enabled=0 diff --git a/boot/32bit-gdt.asm b/boot/32bit-gdt.asm new file mode 100644 index 0000000..e5b1ef1 --- /dev/null +++ b/boot/32bit-gdt.asm @@ -0,0 +1,57 @@ +gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps + ; the GDT starts with a null 8-byte + dd 0x0 ; 4 byte + dd 0x0 ; 4 byte + +; GDT for kernel code segment. base = 0x00000000, length = 0xfffff +; for flags, refer to os-dev.pdf document, page 36 +gdt_kern_code: + dw 0x0000 ; segment length, bits 0-15 + dw 0x0 ; segment base, bits 0-15 + db 0x0 ; segment base, bits 16-23 + db 10011010b ; flags (8 bits) + db 11000001b ; flags (4 bits) + segment length, bits 16-19 + db 0x0 ; segment base, bits 24-31 + +; GDT for kernel data segment. base and length identical to code segment +; some flags changed, again, refer to os-dev.pdf +gdt_kern_data: + dw 0x0000 + dw 0x0 + db 0x0 + db 10010010b + db 11000001b + db 0x0 + +; GDT for user code segment. base = 0x01000000, length = 0xfffff +; for flags, refer to os-dev.pdf document, page 36 +gdt_usr_code: + dw 0xffff ; segment length, bits 0-15 + dw 0x0 ; segment base, bits 0-15 + db 0x0 ; segment base, bits 16-23 + db 11111010b ; flags (8 bits) + db 11001111b ; flags (4 bits) + segment length, bits 16-19 + db 0x0 ; segment base, bits 24-31 + +; GDT for user data segment. base and length identical to code segment +; some flags changed, again, refer to os-dev.pdf +gdt_usr_data: + dw 0xffff + dw 0x0 + db 0x0 + db 11110010b + db 11001111b + db 0x0 + +gdt_end: + +; GDT descriptor +gdt_descriptor: + dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size + dd gdt_start ; address (32 bit) + +; define some constants for later use +CODE_SEG equ gdt_kern_code - gdt_start +DATA_SEG equ gdt_kern_data - gdt_start +USR_CODE_SEG equ gdt_usr_code - gdt_start +USR_DATA_SEG equ gdt_usr_data - gdt_start diff --git a/boot/32bit-switch.asm b/boot/32bit-switch.asm new file mode 100644 index 0000000..212443b --- /dev/null +++ b/boot/32bit-switch.asm @@ -0,0 +1,21 @@ +[bits 16] +switch_to_pm: + cli ; 1. disable interrupts + lgdt [gdt_descriptor] ; 2. load the GDT descriptor + mov eax, cr0 + or eax, 0x1 ; 3. set 32-bit mode bit in cr0 + mov cr0, eax + jmp CODE_SEG:init_pm ; 4. far jump by using a different segment + +[bits 32] +init_pm: ; we are now using 32-bit instructions + mov ax, DATA_SEG ; 5. update the segment registers + mov ds, ax + mov ss, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ebp, 0x90000 ; 6. update the stack right at the top of the free space + mov esp, ebp + + call BEGIN_PM ; 7. Call a well-known label with useful code diff --git a/boot/boot.asm b/boot/boot.asm new file mode 100644 index 0000000..0e29984 --- /dev/null +++ b/boot/boot.asm @@ -0,0 +1,41 @@ +[org 0x7c00] +KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel + + mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot + mov bp, 0x9000 + mov sp, bp + + + call load_kernel ; read the kernel from disk + call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM' + jmp $ ; Never executed + +%include "boot/print.asm" +%include "boot/print_hex.asm" +%include "boot/disk.asm" +%include "boot/32bit-gdt.asm" +%include "boot/32bit-switch.asm" + +[bits 16] +load_kernel: + mov bx, MSG_LOAD_KERNEL + call print + call print_nl + mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000 + mov dh, 20 + mov dl, [BOOT_DRIVE] + call disk_load + ret + +[bits 32] +BEGIN_PM: + call KERNEL_OFFSET ; Give control to the kernel + jmp $ ; Stay here when the kernel returns control to us (if ever) + + +BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten +MSG_LOAD_KERNEL db "Loading kernel into memory", 0 + +; padding +times 510 - ($-$$) db 0 +dw 0xaa55 diff --git a/boot/disk.asm b/boot/disk.asm new file mode 100755 index 0000000..be3d0a9 --- /dev/null +++ b/boot/disk.asm @@ -0,0 +1,46 @@ +; load 'dh' sectors from drive 'dl' into ES:BX +disk_load: + pusha + ; reading from disk requires setting specific values in all registers + ; so we will overwrite our input parameters from 'dx'. Let's save it + ; to the stack for later use. + push dx + + mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read' + mov al, dh ; al <- number of sectors to read (0x01 .. 0x80) + mov cl, 0x02 ; cl <- sector (0x01 .. 0x11) + ; 0x01 is our boot sector, 0x02 is the first 'available' sector + mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl') + ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS + ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2) + mov dh, 0x00 ; dh <- head number (0x0 .. 0xF) + + ; [es:bx] <- pointer to buffer where the data will be stored + ; caller sets it up for us, and it is actually the standard location for int 13h + int 0x13 ; BIOS interrupt + jc disk_error ; if error (stored in the carry bit) + + pop dx + cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it. + jne sectors_error + popa + ret + + +disk_error: + mov bx, DISK_ERROR + call print + call print_nl + mov dh, ah ; ah = error code, dl = disk drive that dropped the error + call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html + jmp disk_loop + +sectors_error: + mov bx, SECTORS_ERROR + call print + +disk_loop: + jmp $ + +DISK_ERROR: db "Disk read error", 0 +SECTORS_ERROR: db "Incorrect number of sectors read", 0 diff --git a/boot/kernel_entry.asm b/boot/kernel_entry.asm new file mode 100644 index 0000000..6728303 --- /dev/null +++ b/boot/kernel_entry.asm @@ -0,0 +1,4 @@ +[bits 32] +[extern main] ; Define calling point. Must have same name as kernel.c 'main' function +call main ; Calls the C function. The linker will know where it is placed in memory +jmp $ diff --git a/boot/print.asm b/boot/print.asm new file mode 100755 index 0000000..b066e0d --- /dev/null +++ b/boot/print.asm @@ -0,0 +1,37 @@ +print: + pusha + +; keep this in mind: +; while (string[i] != 0) { print string[i]; i++ } + +; the comparison for string end (null byte) +start: + mov al, [bx] ; 'bx' is the base address for the string + cmp al, 0 + je done + + ; the part where we print with the BIOS help + mov ah, 0x0e + int 0x10 ; 'al' already contains the char + + ; increment pointer and do next loop + add bx, 1 + jmp start + +done: + popa + ret + + + +print_nl: + pusha + + mov ah, 0x0e + mov al, 0x0a ; newline char + int 0x10 + mov al, 0x0d ; carriage return + int 0x10 + + popa + ret diff --git a/boot/print_hex.asm b/boot/print_hex.asm new file mode 100755 index 0000000..b537ef8 --- /dev/null +++ b/boot/print_hex.asm @@ -0,0 +1,46 @@ +; receiving the data in 'dx' +; For the examples we'll assume that we're called with dx=0x1234 +print_hex: + pusha + + mov cx, 0 ; our index variable + +; Strategy: get the last char of 'dx', then convert to ASCII +; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N. +; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40 +; Then, move the ASCII byte to the correct position on the resulting string +hex_loop: + cmp cx, 4 ; loop 4 times + je end + + ; 1. convert last char of 'dx' to ascii + mov ax, dx ; we will use 'ax' as our working register + and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros + add al, 0x30 ; add 0x30 to N to convert it to ASCII "N" + cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' + jle step2 + add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 + +step2: + ; 2. get the correct position of the string to place our ASCII char + ; bx <- base address + string length - index of char + mov bx, HEX_OUT + 5 ; base + length + sub bx, cx ; our index variable + mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx' + ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234 + + ; increment index and loop + add cx, 1 + jmp hex_loop + +end: + ; prepare the parameter and call the function + ; remember that print receives parameters in 'bx' + mov bx, HEX_OUT + call print + + popa + ret + +HEX_OUT: + db '0x0000',0 ; reserve memory for our new string diff --git a/drivers/idt.c b/drivers/idt.c new file mode 100644 index 0000000..fc1551e --- /dev/null +++ b/drivers/idt.c @@ -0,0 +1,20 @@ +#include "idt.h" +#include + +#define low_16(address) (uint16_t)((address) & 0xFFFF) +#define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF) + +void set_idt_gate(int n,uint32_t handler) { + idt[n].low_offset=low_16(handler); + idt[n].sel=KERNEL_CS; + idt[n].always0=0; + idt[n].flags=0x8E; + idt[n].high_offset=high_16(handler); +} + +void set_idt() { + idt_reg.base=(uint32_t) &idt; + idt_reg.limit=IDT_ENTRIES * sizeof(idt_gate_t) - 1; + /* Don't make the mistake of loading &idt -- always load &idt_reg */ + asm volatile("lidtl (%0)":: "r" (&idt_reg)); +} diff --git a/drivers/idt.h b/drivers/idt.h new file mode 100644 index 0000000..9796723 --- /dev/null +++ b/drivers/idt.h @@ -0,0 +1,39 @@ +#ifndef IDT_H +#define IDT_H + +#include + +/* Segment selectors */ +#define KERNEL_CS 0x08 + +/* How every interrupt gate (handler) is defined */ +typedef struct { + uint16_t low_offset; /* Lower 16 bits of handler function address */ + uint16_t sel; /* Kernel segment selector */ + uint8_t always0; + /* First byte + * Bit 7: "Interrupt is present" + * Bits 6-5: Privilege level of caller (0=kernel..3=user) + * Bit 4: Set to 0 for interrupt gates + * Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */ + uint8_t flags; + uint16_t high_offset; /* Higher 16 bits of handler function address */ +} __attribute__((packed)) idt_gate_t ; + +/* A pointer to the array of interrupt handlers. + * Assembly instruction 'lidt' will read it */ +typedef struct { + uint16_t limit; + uint32_t base; +} __attribute__((packed)) idt_register_t; + +#define IDT_ENTRIES 256 +idt_gate_t idt[IDT_ENTRIES]; +idt_register_t idt_reg; + + +/* Functions implemented in idt.c */ +void set_idt_gate(int n,uint32_t handler); +void set_idt(); + +#endif diff --git a/drivers/interrupt.asm b/drivers/interrupt.asm new file mode 100644 index 0000000..6cd7c8a --- /dev/null +++ b/drivers/interrupt.asm @@ -0,0 +1,424 @@ +; Defined in isr.c +[extern isr_handler] +[extern irq_handler] + +; Common ISR code +isr_common_stub: + ; 1. Save CPU state + pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax + mov ax, ds ; Lower 16-bits of eax = ds. + push eax ; save the data segment descriptor + mov ax, 0x10 ; kernel data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; 2. Call C handler + call isr_handler + + ; 3. Restore state + pop eax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + popa + add esp, 8 ; Cleans up the pushed error code and pushed ISR number + sti + iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP + +; Common IRQ code. Identical to ISR code except for the 'call' +; and the 'pop ebx' +irq_common_stub: + pusha + mov ax, ds + push eax + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + call irq_handler ; Different than the ISR code + pop ebx ; Different than the ISR code + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + popa + add esp, 8 + sti + iret + +; We don't get information about which interrupt was caller +; when the handler is run, so we will need to have a different handler +; for every interrupt. +; Furthermore, some interrupts push an error code onto the stack but others +; don't, so we will push a dummy error code for those which don't, so that +; we have a consistent stack for all of them. + +; First make the ISRs global +global isr0 +global isr1 +global isr2 +global isr3 +global isr4 +global isr5 +global isr6 +global isr7 +global isr8 +global isr9 +global isr10 +global isr11 +global isr12 +global isr13 +global isr14 +global isr15 +global isr16 +global isr17 +global isr18 +global isr19 +global isr20 +global isr21 +global isr22 +global isr23 +global isr24 +global isr25 +global isr26 +global isr27 +global isr28 +global isr29 +global isr30 +global isr31 +; IRQs +global irq0 +global irq1 +global irq2 +global irq3 +global irq4 +global irq5 +global irq6 +global irq7 +global irq8 +global irq9 +global irq10 +global irq11 +global irq12 +global irq13 +global irq14 +global irq15 + +; 0: Divide By Zero Exception +isr0: + cli + push byte 0 + push byte 0 + jmp isr_common_stub + +; 1: Debug Exception +isr1: + cli + push byte 0 + push byte 1 + jmp isr_common_stub + +; 2: Non Maskable Interrupt Exception +isr2: + cli + push byte 0 + push byte 2 + jmp isr_common_stub + +; 3: Int 3 Exception +isr3: + cli + push byte 0 + push byte 3 + jmp isr_common_stub + +; 4: INTO Exception +isr4: + cli + push byte 0 + push byte 4 + jmp isr_common_stub + +; 5: Out of Bounds Exception +isr5: + cli + push byte 0 + push byte 5 + jmp isr_common_stub + +; 6: Invalid Opcode Exception +isr6: + cli + push byte 0 + push byte 6 + jmp isr_common_stub + +; 7: Coprocessor Not Available Exception +isr7: + cli + push byte 0 + push byte 7 + jmp isr_common_stub + +; 8: Double Fault Exception (With Error Code!) +isr8: + cli + push byte 8 + jmp isr_common_stub + +; 9: Coprocessor Segment Overrun Exception +isr9: + cli + push byte 0 + push byte 9 + jmp isr_common_stub + +; 10: Bad TSS Exception (With Error Code!) +isr10: + cli + push byte 10 + jmp isr_common_stub + +; 11: Segment Not Present Exception (With Error Code!) +isr11: + cli + push byte 11 + jmp isr_common_stub + +; 12: Stack Fault Exception (With Error Code!) +isr12: + cli + push byte 12 + jmp isr_common_stub + +; 13: General Protection Fault Exception (With Error Code!) +isr13: + cli + push byte 13 + jmp isr_common_stub + +; 14: Page Fault Exception (With Error Code!) +isr14: + cli + push byte 14 + jmp isr_common_stub + +; 15: Reserved Exception +isr15: + cli + push byte 0 + push byte 15 + jmp isr_common_stub + +; 16: Floating Point Exception +isr16: + cli + push byte 0 + push byte 16 + jmp isr_common_stub + +; 17: Alignment Check Exception +isr17: + cli + push byte 0 + push byte 17 + jmp isr_common_stub + +; 18: Machine Check Exception +isr18: + cli + push byte 0 + push byte 18 + jmp isr_common_stub + +; 19: Reserved +isr19: + cli + push byte 0 + push byte 19 + jmp isr_common_stub + +; 20: Reserved +isr20: + cli + push byte 0 + push byte 20 + jmp isr_common_stub + +; 21: Reserved +isr21: + cli + push byte 0 + push byte 21 + jmp isr_common_stub + +; 22: Reserved +isr22: + cli + push byte 0 + push byte 22 + jmp isr_common_stub + +; 23: Reserved +isr23: + cli + push byte 0 + push byte 23 + jmp isr_common_stub + +; 24: Reserved +isr24: + cli + push byte 0 + push byte 24 + jmp isr_common_stub + +; 25: Reserved +isr25: + cli + push byte 0 + push byte 25 + jmp isr_common_stub + +; 26: Reserved +isr26: + cli + push byte 0 + push byte 26 + jmp isr_common_stub + +; 27: Reserved +isr27: + cli + push byte 0 + push byte 27 + jmp isr_common_stub + +; 28: Reserved +isr28: + cli + push byte 0 + push byte 28 + jmp isr_common_stub + +; 29: Reserved +isr29: + cli + push byte 0 + push byte 29 + jmp isr_common_stub + +; 30: Reserved +isr30: + cli + push byte 0 + push byte 30 + jmp isr_common_stub + +; 31: Reserved +isr31: + cli + push byte 0 + push byte 31 + jmp isr_common_stub + +; IRQ handlers +irq0: + cli + push byte 0 + push byte 32 + jmp irq_common_stub + +irq1: + cli + push byte 1 + push byte 33 + jmp irq_common_stub + +irq2: + cli + push byte 2 + push byte 34 + jmp irq_common_stub + +irq3: + cli + push byte 3 + push byte 35 + jmp irq_common_stub + +irq4: + cli + push byte 4 + push byte 36 + jmp irq_common_stub + +irq5: + cli + push byte 5 + push byte 37 + jmp irq_common_stub + +irq6: + cli + push byte 6 + push byte 38 + jmp irq_common_stub + +irq7: + cli + push byte 7 + push byte 39 + jmp irq_common_stub + +irq8: + cli + push byte 8 + push byte 40 + jmp irq_common_stub + +irq9: + cli + push byte 9 + push byte 41 + jmp irq_common_stub + +irq10: + cli + push byte 10 + push byte 42 + jmp irq_common_stub + +irq11: + cli + push byte 11 + push byte 43 + jmp irq_common_stub + +irq12: + cli + push byte 12 + push byte 44 + jmp irq_common_stub + +irq13: + cli + push byte 13 + push byte 45 + jmp irq_common_stub + +irq14: + cli + push byte 14 + push byte 46 + jmp irq_common_stub + +irq15: + cli + push byte 15 + push byte 47 + jmp irq_common_stub diff --git a/drivers/isr.c b/drivers/isr.c new file mode 100644 index 0000000..dd4c09e --- /dev/null +++ b/drivers/isr.c @@ -0,0 +1,146 @@ +#include "isr.h" +#include "idt.h" +#include "vga.h" +#include "../libc/string.h" +#include +#include "ports.h" + +isr_t interrupt_handlers[256]; + +/* Can't do this with a loop because we need the address + * of the function names */ +void isr_install() { + set_idt_gate(0,(uint32_t)isr0); + set_idt_gate(1,(uint32_t)isr1); + set_idt_gate(2,(uint32_t)isr2); + set_idt_gate(3,(uint32_t)isr3); + set_idt_gate(4,(uint32_t)isr4); + set_idt_gate(5,(uint32_t)isr5); + set_idt_gate(6,(uint32_t)isr6); + set_idt_gate(7,(uint32_t)isr7); + set_idt_gate(8,(uint32_t)isr8); + set_idt_gate(9,(uint32_t)isr9); + set_idt_gate(10,(uint32_t)isr10); + set_idt_gate(11,(uint32_t)isr11); + set_idt_gate(12,(uint32_t)isr12); + set_idt_gate(13,(uint32_t)isr13); + set_idt_gate(14,(uint32_t)isr14); + set_idt_gate(15,(uint32_t)isr15); + set_idt_gate(16,(uint32_t)isr16); + set_idt_gate(17,(uint32_t)isr17); + set_idt_gate(18,(uint32_t)isr18); + set_idt_gate(19,(uint32_t)isr19); + set_idt_gate(20,(uint32_t)isr20); + set_idt_gate(21,(uint32_t)isr21); + set_idt_gate(22,(uint32_t)isr22); + set_idt_gate(23,(uint32_t)isr23); + set_idt_gate(24,(uint32_t)isr24); + set_idt_gate(25,(uint32_t)isr25); + set_idt_gate(26,(uint32_t)isr26); + set_idt_gate(27,(uint32_t)isr27); + set_idt_gate(28,(uint32_t)isr28); + set_idt_gate(29,(uint32_t)isr29); + set_idt_gate(30,(uint32_t)isr30); + set_idt_gate(31,(uint32_t)isr31); + + // Remap the PIC + port_byte_out(0x20,0x11); + port_byte_out(0xA0,0x11); + port_byte_out(0x21,0x20); + port_byte_out(0xA1,0x28); + port_byte_out(0x21,0x04); + port_byte_out(0xA1,0x02); + port_byte_out(0x21,0x01); + port_byte_out(0xA1,0x01); + port_byte_out(0x21,0x0); + port_byte_out(0xA1,0x0); + + // Install the IRQs + set_idt_gate(32,(uint32_t)irq0); + set_idt_gate(33,(uint32_t)irq1); + set_idt_gate(34,(uint32_t)irq2); + set_idt_gate(35,(uint32_t)irq3); + set_idt_gate(36,(uint32_t)irq4); + set_idt_gate(37,(uint32_t)irq5); + set_idt_gate(38,(uint32_t)irq6); + set_idt_gate(39,(uint32_t)irq7); + set_idt_gate(40,(uint32_t)irq8); + set_idt_gate(41,(uint32_t)irq9); + set_idt_gate(42,(uint32_t)irq10); + set_idt_gate(43,(uint32_t)irq11); + set_idt_gate(44,(uint32_t)irq12); + set_idt_gate(45,(uint32_t)irq13); + set_idt_gate(46,(uint32_t)irq14); + set_idt_gate(47,(uint32_t)irq15); + + set_idt(); // Load with ASM +} + + +/* To print the message which defines every exception */ +char *exception_messages[] = { + "Division By Zero", + "Debug", + "Non Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + + "Double Fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment Not Present", + "Stack Fault", + "General Protection Fault", + "Page Fault", + "Unknown Interrupt", + + "Coprocessor Fault", + "Alignment Check", + "Machine Check", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved" +}; + +void isr_handler(registers_t r) { + write_string("Received interrupt no "); + char s[3]; + int_to_ascii(r.int_no, s); + write_string(s); + write_string(". ("); + write_string(exception_messages[r.int_no]); + write_string(")\n"); + asm volatile("hlt"); +} + + +void register_interrupt_handler(uint8_t n,isr_t handler) { + interrupt_handlers[n] = handler; +} + +void irq_handler(registers_t r) { + /* After every interrupt we need to send an EOI to the PICs + * or they will not send another interrupt again */ + if (r.int_no >= 40) port_byte_out(0xA0,0x20); /* slave */ + port_byte_out(0x20,0x20); /* master */ + + /* Handle the interrupt in a more modular way */ + if (interrupt_handlers[r.int_no] != 0) { + isr_t handler = interrupt_handlers[r.int_no]; + handler(r); + } +} diff --git a/drivers/isr.h b/drivers/isr.h new file mode 100644 index 0000000..5586f71 --- /dev/null +++ b/drivers/isr.h @@ -0,0 +1,88 @@ +#ifndef ISR_H +#define ISR_H + +#include + +/* ISRs reserved for CPU exceptions */ +extern void isr0(); +extern void isr1(); +extern void isr2(); +extern void isr3(); +extern void isr4(); +extern void isr5(); +extern void isr6(); +extern void isr7(); +extern void isr8(); +extern void isr9(); +extern void isr10(); +extern void isr11(); +extern void isr12(); +extern void isr13(); +extern void isr14(); +extern void isr15(); +extern void isr16(); +extern void isr17(); +extern void isr18(); +extern void isr19(); +extern void isr20(); +extern void isr21(); +extern void isr22(); +extern void isr23(); +extern void isr24(); +extern void isr25(); +extern void isr26(); +extern void isr27(); +extern void isr28(); +extern void isr29(); +extern void isr30(); +extern void isr31(); +/* IRQ definitions */ +extern void irq0(); +extern void irq1(); +extern void irq2(); +extern void irq3(); +extern void irq4(); +extern void irq5(); +extern void irq6(); +extern void irq7(); +extern void irq8(); +extern void irq9(); +extern void irq10(); +extern void irq11(); +extern void irq12(); +extern void irq13(); +extern void irq14(); +extern void irq15(); + +#define IRQ0 32 +#define IRQ1 33 +#define IRQ2 34 +#define IRQ3 35 +#define IRQ4 36 +#define IRQ5 37 +#define IRQ6 38 +#define IRQ7 39 +#define IRQ8 40 +#define IRQ9 41 +#define IRQ10 42 +#define IRQ11 43 +#define IRQ12 44 +#define IRQ13 45 +#define IRQ14 46 +#define IRQ15 47 + +/* Struct which aggregates many registers */ +typedef struct { + uint32_t ds; /* Data segment selector */ + uint32_t edi,esi,ebp,esp,ebx,edx,ecx,eax; /* Pushed by pusha. */ + uint32_t int_no,err_code; /* Interrupt number and error code (if applicable) */ + uint32_t eip,cs,eflags,useresp,ss; /* Pushed by the processor automatically */ +} registers_t; + +void isr_install(); +void isr_handler(registers_t r); + +typedef void (*isr_t)(registers_t); +void register_interrupt_handler(uint8_t n,isr_t handler); + +#endif diff --git a/drivers/ports.c b/drivers/ports.c new file mode 100644 index 0000000..8458533 --- /dev/null +++ b/drivers/ports.c @@ -0,0 +1,19 @@ +unsigned char port_byte_in(unsigned short port) { + unsigned char result; + asm("in %%dx, %%al":"=a"(result):"d"(port)); + return result; +} + +void port_byte_out(unsigned short port,unsigned char data) { + asm("out %%al, %%dx":: "a"(data),"d"(port)); +} + +unsigned short port_word_in(unsigned short port) { + unsigned short result; + asm("in %%dx, %%ax":"=a"(result):"d"(port)); + return result; +} + +void port_word_out(unsigned short port,unsigned short data) { + asm("out %%ax, %%dx":: "a" (data), "d" (port)); +} diff --git a/drivers/ports.h b/drivers/ports.h new file mode 100644 index 0000000..a7d80df --- /dev/null +++ b/drivers/ports.h @@ -0,0 +1,9 @@ +#ifndef PORTS_H +#define PORTS_H + +unsigned char port_byte_in(unsigned short port); +void port_byte_out(unsigned short port,unsigned char data); +unsigned short port_word_in(unsigned short port); +void port_word_out(unsigned short port,unsigned short data); + +#endif diff --git a/drivers/vga.c b/drivers/vga.c new file mode 100644 index 0000000..b50a723 --- /dev/null +++ b/drivers/vga.c @@ -0,0 +1,99 @@ +#include "ports.h" +#include "vga.h" +#include "../libc/memory.h" +#define SCREEN_CTRL 0x3d4 +#define SCREEN_DATA 0x3d5 +#define VIDEO_MEMORY 0xb8000 +static char* video_memory; +static char x; +static char y; +static char color; + + +int get_cursor_offset(); +void set_cursor_offset(int offset); + +int get_offset(int x,int y) { + return 2*(y*VGA_WIDTH+x); +} +int get_offset_y(int offset) { + return offset/(2*VGA_WIDTH); +} + +int get_offset_x(int offset) { + return (offset-(get_offset_y(offset)*2*VGA_WIDTH))/2; +} + +void init_vga(VGA_COLOR txt,VGA_COLOR bg) { + video_memory=(char*)VIDEO_MEMORY; + x=0; + y=0; + color=(int)bg; + color=color<<4; + color=color|txt; + clear_screen(); +} + +void write_string(const char *string) { + char c; + while(*string!=0) { + c=*string; + string++; + if (c=='\n') { + x=0; + y++; + continue; + } + if (x==80) { + x=0; + y++; + } + if (y==25) { + for (int i=1;i>8)); + // port_byte_out(SCREEN_CTRL,15); + // port_byte_out(SCREEN_DATA,(unsigned char)(offset&0xff)); +} diff --git a/drivers/vga.h b/drivers/vga.h new file mode 100644 index 0000000..b872210 --- /dev/null +++ b/drivers/vga.h @@ -0,0 +1,30 @@ +#ifndef VGA_H +#define VGA_H + +typedef enum { + BLACK=0, + BLUE=1, + GREEN=2, + CYAN=3, + RED=4, + PURPLE=5, + BROWN=6, + GRAY=7, + DARK_GRAY=8, + LIGHT_BLUE=9, + LIGHT_GREEN=10, + LIGHT_CYAN=11, + LIGHT_RED=12, + LIGHT_PURPLE=13, + YELLOW=14, + WHITE=15 +} VGA_COLOR; + +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 + +void init_vga(VGA_COLOR txt,VGA_COLOR bg); +void write_string(const char *string); +void clear_screen(); + +#endif diff --git a/kernel/kernel.c b/kernel/kernel.c new file mode 100644 index 0000000..2b109bf --- /dev/null +++ b/kernel/kernel.c @@ -0,0 +1,37 @@ +#include "../drivers/vga.h" +#include "../drivers/isr.h" +#include "../drivers/idt.h" +/* +pop %eax; \ +or $0x200,%eax; \ +push %eax; \ +*/ +void switch_to_user_mode() { + // Set up a stack structure for switching to user mode. + asm volatile(" \ + cli; \ + mov $0x23, %ax; \ + mov %ax, %ds; \ + mov %ax, %es; \ + mov %ax, %fs; \ + mov %ax, %gs; \ + mov %esp, %eax; \ + pushl $0x23; \ + pushl %eax; \ + pushf; \ + pushl $0x1B; \ + push $1f; \ + iret; \ + 1: \ + "); +} + + +void main() { + init_vga(GRAY,BLACK); + write_string("Initialized VGA\n"); + isr_install(); + asm volatile("sti"); + write_string("Setup interrupts\n"); + switch_to_user_mode(); +} diff --git a/libc/memory.c b/libc/memory.c new file mode 100644 index 0000000..e6d668b --- /dev/null +++ b/libc/memory.c @@ -0,0 +1,6 @@ +void memory_copy(char *source,char *dest,int nbytes) { + int i; + for(i=0;i 0); + if (sign < 0) { + str[i++] = '-'; + } + str[i]='\0'; + reverse(str); +} diff --git a/libc/string.h b/libc/string.h new file mode 100644 index 0000000..1fae8d2 --- /dev/null +++ b/libc/string.h @@ -0,0 +1,8 @@ +#ifndef STRING_H +#define STRING_H + +int strlen(char* str); +void reverse(char* str); +void int_to_ascii(int n,char* str); + +#endif