os

Toy OS
git clone git://git.christosmarg.xyz
Log | Files | Refs | README | LICENSE

commit 2fa7e11f715aa20c8015d54678a4c034269e485f
parent f5bdd46c928f314671dc5e934424bcfb4254a6e1
Author: Christos Margiolis <christos@margiolis.net>
Date:   Sun, 16 May 2021 19:13:41 +0300

Added A20 line stuff, improved idt.c, removed Long Mode completely for now

Diffstat:
Mboot/Makefile | 7++-----
Mboot/boot.asm | 303+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mboot/extern.h | 52++++------------------------------------------------
Mboot/idt.c | 147+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mboot/idt.h | 96+++++++++++++++++++++++++++++++++++--------------------------------------------
Mboot/int.asm | 263++++++++++++++++++++++++++++++++++++-------------------------------------------
Aboot/io.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mboot/kbd.c | 6+++---
Mboot/kbd.h | 5+++++
Dboot/kernel.c | 17-----------------
Aboot/kmain.c | 16++++++++++++++++
Mboot/loader.asm | 4++--
Aboot/port.h | 14++++++++++++++
Mboot/string.c | 27+++++++++++++++++++++++++++
Mboot/string.h | 9+++++----
Mboot/timer.c | 9+++++----
Mboot/timer.h | 6+++---
Mboot/tty.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mboot/tty.h | 21++++++++++++++-------
Dboot/unused/a20.asm | 153-------------------------------------------------------------------------------
Aboot/unused/lm.asm | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
21 files changed, 738 insertions(+), 659 deletions(-)

diff --git a/boot/Makefile b/boot/Makefile @@ -12,7 +12,7 @@ BOOT_BIN = boot.bin KERNEL_BIN = kernel.bin SRC = *.c *.asm OBJ = loader.o \ - kernel.o \ + kmain.o \ string.o \ tty.o \ idt.o \ @@ -46,10 +46,7 @@ ${BIN}: ${OBJ} install: all mv ${BIN} ${BINDIR} -run: all - qemu-system-i386 -hdd ${BINDIR}/${BIN} - clean: rm -rf *.bin *.o -.PHONY: options all install run clean +.PHONY: options all install clean diff --git a/boot/boot.asm b/boot/boot.asm @@ -5,7 +5,7 @@ KERNOFF equ 0x1000 section .text -global _start + global _start ; Entry point. _start: @@ -21,18 +21,167 @@ _start: cld ; Read strings from low to high. sti ; Enable interrupts back. - mov [BOOTDRV], dl ; Set boot drive. - call kernel_load - jmp pm_enter + call a20_test + call a20_enable + call kernel_load ; Read kernel from disk to memory. + jmp pm_enter ; Enter 32-bit Protected Mode. jmp $ ; Safety hang. +; Test to see if the A20 line is enabled. +; As a reference we will use the magic number 0xaa55 since we know it's always +; going to have the same value and be located at address 0x7dfe (0x7c00 + 510). +; If the return code in `ax` is 1, the A20 is enabled, and if it's 0 it's disabled. +a20_test: + pusha + + mov ax, [0x7dfe] ; 0x7c00 + 510. The magic number is there. + mov dx, ax + +; We'll try to advance 1MB in memory. If the end result hasn't wrapped up +; around zero, it means that our processor is able to access more than 1MB +; of addresses, so the A20 is enabled. Using 0x7dfe again, the expected +; address after the 1MB advance should be: +; +; 0x100000 + 0x7dfe = 0x107dfe +; +; The formula for memory segmentation is: +; +; address = segment * 16 + offset +; +; To find the offset, the segment will be 0xffff, so `0xffff * 16 = 0xffff0`. +; Applying the formula to calculate our offset, we have: +; +; 0xffff0 + offset = 0x107dfe => +; offset = 0x107dfe - 0xffff0 => +; offset = 0x7e0e + + push bx + mov bx, 0xffff + mov es, bx ; Assign value to the segment register. + pop bx + + mov bx, 0x7e0e + mov dx, [es:bx] ; If the A20 line is disabled we get 0xaa55. + + cmp ax, dx + je cont ; If they're equal, the A20 line might be disabled. + popa + mov ax, 0x01 ; Success code 1. + ret +cont: + mov ax, [0x7dff] + mov dx, ax + + push bx + mov bx, 0xffff ; Make another segment. + mov es, bx + pop bx + + mov bx, 0x7e0f + mov dx, [es:bx] + + cmp ax, dx ; Now it really is disabled if ax == [es:bx]. + je exit + popa + mov ax, 0x01 ; Success code 1. + ret +exit: + popa + xor ax, ax ; Error code 0. + ret + +; Enable the A20 line. We'll try enabling it using the following ways: +; - BIOS interrupt. +; - Keyboard controller. +; - FastA20. +a20_enable: + pusha + +; BIOS interrupt. + mov ax, 0x2401 ; A20-Gate Active. + int 0x15 + + call a20_test + cmp ax, 0x01 + je a20_done + jmp a20_fail + +; Keyboard controller. + sti ; Enable interrupts. + + call a20_waitc + mov al, 0xad ; Disable controller. + out 0x64, al ; Send data to port 0x64. + call a20_waitc + mov al, 0xd0 ; We want to read from the controller. + out 0x64, al + + call a20_waitd + in al, 0x60 ; Read data from port 0x60. + push ax ; Save data. + + call a20_waitc + mov al, 0xd1 ; We want to send data. + out 0x64, al + + call a20_waitc + pop ax + or al, 0x02 ; Write the second bit back. + out 0x60, al ; Send data to the data port. + + call a20_waitc + mov al, 0xae ; Enable controller. + out 0x64, al + + call a20_waitc + sti ; Enable interrupts again. + + call a20_test + cmp ax, 0x01 + je a20_done + jmp a20_fail + +; Wait for the keyboard controller to be available for receiving commands. +a20_waitc: + in al, 0x64 ; Read from port 0x64. + test al, 0x02 ; Test the second bit. + jnz a20_waitc ; If it's 1, it's busy. + ret +; Wait for the keyboard controller to send data back. +a20_waitd: + in al, 0x64 ; Read from port 0x64 again. + test al, 0x01 ; Test the first bit. + jz a20_waitd ; If it's 1, the data is not ready to be sent. + ret + +; FastA20 + in al, 0x92 ; FastA20 uses port 0x92. + or al, 0x02 ; Mask second bit. + out 0x92, al ; Send data back to port 0x92. + + call a20_test + cmp ax, 0x01 + je a20_done + jmp a20_fail + +; The A20 is enabled. Go on. +a20_done: + popa + ret + +; The A20 is disabled. +a20_fail: + mov si, str_a20_fail + call puts + jmp $ + ; Load kernel from disk to memory. kernel_load: pusha - mov ah, 0x42 - mov dl, [BOOTDRV] + mov ah, 0x42 ; BIOS read disk extended function. + mov dl, [BOOTDRV] ; Set boot drive. mov si, disk_packet - int 0x13 + int 0x13 ; BIOS read disk interrupt. jc diskerr ; There's an error. Too bad. popa @@ -50,44 +199,42 @@ disk_packet: .seg16 dw 0x0000 .lba dq 1 -[bits 16] -; Set up the GDT. +; Set up the GDT (Global Descriptor Table). gdt: gdt_null: - dd 0x0 - dd 0x0 + dq 0x00000000 gdt_kernel_code: dw 0xffff - dw 0x0 - db 0x0 + dw 0x0000 + db 0x00 db 10011010b db 11001111b - db 0x0 + db 0x00 gdt_kernel_data: dw 0xffff - dw 0x0 - db 0x0 + dw 0x0000 + db 0x00 db 10010010b db 11001111b - db 0x0 + db 0x00 gdt_userland_code: dw 0xffff - dw 0x0 - db 0x0 + dw 0x0000 + db 0x00 db 11111010b db 11001111b - db 0x0 + db 0x00 gdt_userland_data: dw 0xffff - dw 0x0 - db 0x0 + dw 0x0000 + db 0x00 db 11110010b db 11001111b - db 0x0 + db 0x00 gdt_ptr: dw $ - gdt - 1 @@ -100,9 +247,8 @@ GDT_DATASEG equ gdt_kernel_data - gdt pm_enter: cli ; Disable BIOS interrupts. lgdt [gdt_ptr] ; Load the GDT. - mov eax, cr0 - or eax, 1 << 0 ; Protected Mode Enable. + or eax, 1 ; Protected Mode Enable. mov cr0, eax ; There's no going back now. jmp GDT_CODESEG:pm_init @@ -119,112 +265,8 @@ pm_init: mov ebp, 0x90000 mov esp, ebp - call lm_check - call kernel_exec ; If we cannot get into Long Mode, start in PE. - jmp $ - -; Check for Long Mode. -lm_check: - pusha - pushfd ; Push EFLAGS register to the stack. - pop eax ; Pop EFLAGS registers to `eax`. - mov ecx, eax ; Keep a backup in `ecx`. - xor eax, 1 << 21 ; Flip the 21st bit if the it's not 1. - push eax - popfd ; Pop `eax` to EFLAGS. - - pushfd - pop eax ; Copy EFLAGS back to `eax`. - push ecx - popfd ; Restore the flipped version. - xor eax, ecx - jz lm_fail ; If EFLAGS' value hasn't changed, we have CPUID. - - mov eax, 0x80000000 ; CPUID argument. - cpuid ; CPU identification. `eax` is now populated with info. - cmp eax, 0x80000001 ; If it's less than 0x80000000 we cannot have Long Mode. - jb lm_fail - - mov eax, 0x80000001 ; Get extended processor information. - cpuid - test edx, 1 << 29 - jz lm_fail ; If the 29th bit is 0, we cannot have Long Mode. - - popa - ret - -; No long mode. -lm_fail: - popa - call kernel_exec - jmp $ ; Safety hang. - -; We'll check for (and switch to) Long Mode here. -lm_enter: - cli - mov eax, cr0 - or eax, 1 << 31 ; Paging Enable. - mov cr0, eax -; Set up PAE paging. - mov edi, 0x1000 ; Page table starts 4KiB in memory. - mov cr3, edi ; Hold the location of the highest page table. - xor eax, eax ; Clear memory space. - mov ecx, 4096 - rep stosd ; Write 4096 dwords with the value 0. - mov edi, 0x1000 ; Go to the initial location. - -; Page table locations: -; Page Map Level 4 Table (PML4T): 0x1000 -; Page Directory Pointer Table (PDPT): 0x2000 -; Page Directory Table (PDT): 0x3000 -; Page Table (PT); 0x4000 -; -; In order for each table to point to each other we'll use a size directive. -; Since tables are 4KiB away from each other we'll move dwords. - mov dword [edi], 0x2003 ; PML4T -> PDPT - add edi, 0x1000 ; Move 4KiB forward. - mov dword [edi], 0x3003 ; PDPT -> PDT - add edi, 0x1000 - mov dword [edi], 0x4003 ; PDT -> PT - add edi, 0x1000 - - mov dword ebx, 3 ; Map beginning of code. - mov ecx, 512 ; Do it 512 times. -setentry: - mov dword [edi], ebx - add ebx, 0x1000 - add edi, 8 ; Go to the next entry in the page table. - loop setentry - -; Enable the 6th bit in CR4 (Physical Address Extension Bit) to tell -; the CPU that we're using PAE paging. - mov eax, cr4 - or eax, 1 << 5 - mov cr4, eax - -; Activate Protected Mode and Paging using the EFER. - mov ecx, 0xc0000080 - rdmsr ; Copy the contents of EFER to `eax`. - or eax, 1 << 8 ; Set LME bit to enable Long Mode. - wrmsr ; Write `eax` back to EFER. - - lgdt [gdt_ptr] ; Load the GDT. - jmp GDT_CODESEG:lm_init ; Switch to Long Mode. - -; We're in Long Mode now. -[bits 64] -lm_init: - mov ax, GDT_DATASEG - mov ds, ax - mov ss, ax - mov es, ax - mov fs, ax - mov gs, ax - - mov rbp, 0x90000 - mov rsp, rbp call kernel_exec - jmp $ + jmp $ ; Hand control over to the C kernel. Godspeed. kernel_exec: @@ -249,6 +291,7 @@ putchar: ; String declarations. str_diskerr: db "Error loading disk.", 0x0a, 0x0d, 0x00 +str_a20_fail: db "The A20 Line is disabled", 0x0a, 0x0d, 0x00 BOOTDRV: db 0x80 ; Padding to 512 bytes. The last 2 bytes will come from the magic number. diff --git a/boot/extern.h b/boot/extern.h @@ -1,56 +1,12 @@ -#ifndef _KERNEL_EXTERN_H -#define _KERNEL_EXTERN_H +#ifndef _KERNEL_EXTERN_H_ +#define _KERNEL_EXTERN_H_ #include <stdarg.h> #include <stddef.h> #include <stdint.h> +#include "io.h" #include "tty.h" #include "string.h" -static inline uint8_t -inb(uint16_t port) -{ - uint8_t res; - - __asm__ __volatile__ ("in %%dx, %%al" : "=a" (res) : "d" (port)); - return res; -} - -static inline void -outb(uint8_t v, uint16_t port) -{ - __asm__ __volatile__ ("out %%al, %%dx" : : "a" (v), "d" (port)); -} - -static inline uint16_t -inw(uint16_t port) -{ - uint16_t res; - - __asm__ __volatile__ ("in %%dx, %%ax" : "=a" (res) : "d" (port)); - return res; -} - -static inline void -outw(uint16_t v, uint16_t port) -{ - __asm__ __volatile__ ("out %%ax, %%dx" : : "a" (v), "d" (port)); -} - -static inline uint32_t -inl(uint16_t port) -{ - uint32_t res; - - __asm__ __volatile__ ("in %%dx, %%eax" : "=a" (res) : "d" (port)); - return res; -} - -static inline void -outl(uint32_t v, uint16_t port) -{ - __asm__ __volatile__ ("out %%eax, %%dx" : : "a" (v), "d" (port)); -} - -#endif /* _KERNEL_EXTERN_H */ +#endif /* _KERNEL_EXTERN_H_ */ diff --git a/boot/idt.c b/boot/idt.c @@ -1,12 +1,14 @@ #include "extern.h" #include "idt.h" +#include "port.h" + +#define N_INT 256 /* There are 256 interrupts in total. */ +#define KERN_CODESEG 0x08 /* Kernel code segment. */ static void idt_set_gate(uint8_t, uint32_t); static struct idt_gate idt[N_INT]; -static struct idt_reg idtr; -static void *irq_routines[16] = {NULL}; - +static void *isr[16] = {NULL}; /* Exception messages for the first 32 ISRs. */ static const char *except[] = { "Division By Zero Exception", @@ -49,61 +51,69 @@ idt_set_gate(uint8_t n, uint32_t ptr) idt[n].off_lo = ptr & 0xffff; idt[n].sel = KERN_CODESEG; idt[n].zero = 0; - idt[n].attr = 0x8e; /* The gate is present and is running in kernel mode. */ + /* The gate is present and is running in kernel mode. */ + idt[n].flags = 0x8e; idt[n].off_hi = (ptr >> 16) & 0xffff; } void idt_init(void) { - /* Set up the ISRs */ - idt_set_gate(0, (uint32_t)isr0); - idt_set_gate(1, (uint32_t)isr1); - idt_set_gate(2, (uint32_t)isr2); - idt_set_gate(3, (uint32_t)isr3); - idt_set_gate(4, (uint32_t)isr4); - idt_set_gate(5, (uint32_t)isr5); - idt_set_gate(6, (uint32_t)isr6); - idt_set_gate(7, (uint32_t)isr7); - idt_set_gate(8, (uint32_t)isr8); - idt_set_gate(9, (uint32_t)isr9); - idt_set_gate(10, (uint32_t)isr10); - idt_set_gate(11, (uint32_t)isr11); - idt_set_gate(12, (uint32_t)isr12); - idt_set_gate(13, (uint32_t)isr13); - idt_set_gate(14, (uint32_t)isr14); - idt_set_gate(15, (uint32_t)isr15); - idt_set_gate(16, (uint32_t)isr16); - idt_set_gate(17, (uint32_t)isr17); - idt_set_gate(18, (uint32_t)isr18); - idt_set_gate(19, (uint32_t)isr19); - idt_set_gate(20, (uint32_t)isr20); - idt_set_gate(21, (uint32_t)isr21); - idt_set_gate(22, (uint32_t)isr22); - idt_set_gate(23, (uint32_t)isr23); - idt_set_gate(24, (uint32_t)isr24); - idt_set_gate(25, (uint32_t)isr25); - idt_set_gate(26, (uint32_t)isr26); - idt_set_gate(27, (uint32_t)isr27); - idt_set_gate(28, (uint32_t)isr28); - idt_set_gate(29, (uint32_t)isr29); - idt_set_gate(30, (uint32_t)isr30); - idt_set_gate(31, (uint32_t)isr31); + struct idt_reg { + uint16_t limit; /* Points at IDT[0]. */ + uint32_t base; /* Points at the end of the IDT. */ + } __attribute__((packed)) idtr; + + (void)memset(&idt, 0, sizeof(idt)); + + /* Set up exception interrupts. */ + idt_set_gate(0, (uint32_t)ex0); + idt_set_gate(1, (uint32_t)ex1); + idt_set_gate(2, (uint32_t)ex2); + idt_set_gate(3, (uint32_t)ex3); + idt_set_gate(4, (uint32_t)ex4); + idt_set_gate(5, (uint32_t)ex5); + idt_set_gate(6, (uint32_t)ex6); + idt_set_gate(7, (uint32_t)ex7); + idt_set_gate(8, (uint32_t)ex8); + idt_set_gate(9, (uint32_t)ex9); + idt_set_gate(10, (uint32_t)ex10); + idt_set_gate(11, (uint32_t)ex11); + idt_set_gate(12, (uint32_t)ex12); + idt_set_gate(13, (uint32_t)ex13); + idt_set_gate(14, (uint32_t)ex14); + idt_set_gate(15, (uint32_t)ex15); + idt_set_gate(16, (uint32_t)ex16); + idt_set_gate(17, (uint32_t)ex17); + idt_set_gate(18, (uint32_t)ex18); + idt_set_gate(19, (uint32_t)ex19); + idt_set_gate(20, (uint32_t)ex20); + idt_set_gate(21, (uint32_t)ex21); + idt_set_gate(22, (uint32_t)ex22); + idt_set_gate(23, (uint32_t)ex23); + idt_set_gate(24, (uint32_t)ex24); + idt_set_gate(25, (uint32_t)ex25); + idt_set_gate(26, (uint32_t)ex26); + idt_set_gate(27, (uint32_t)ex27); + idt_set_gate(28, (uint32_t)ex28); + idt_set_gate(29, (uint32_t)ex29); + idt_set_gate(30, (uint32_t)ex30); + idt_set_gate(31, (uint32_t)ex31); /* Remap the PIC */ /* TODO: explain.*/ - outb(0x11, 0x20); - outb(0x11, 0xa0); - outb(0x20, 0x21); - outb(0x28, 0xa1); - outb(0x04, 0x21); - outb(0x02, 0xa1); - outb(0x01, 0x21); - outb(0x01, 0xa1); - outb(0x00, 0x21); - outb(0x00, 0xa1); + outb(P_PIC1_CMD, 0x11); + outb(P_PIC2_CMD, 0x11); + outb(P_PIC1_DATA, 0x20); + outb(P_PIC2_DATA, 0x28); + outb(P_PIC1_DATA, 0x04); + outb(P_PIC2_DATA, 0x02); + outb(P_PIC1_DATA, 0x01); + outb(P_PIC2_DATA, 0x01); + outb(P_PIC1_DATA, 0x00); + outb(P_PIC2_DATA, 0x00); - /* Set up the IRQs */ + /* Set up IRQs */ idt_set_gate(32, (uint32_t)irq0); idt_set_gate(33, (uint32_t)irq1); idt_set_gate(34, (uint32_t)irq2); @@ -128,41 +138,30 @@ idt_init(void) __asm__ __volatile__ ("lidtl (%0)" : : "r" (&idtr)); } -/* TODO: add comments */ void -isr_handler(struct reg *r) +int_handler(struct reg *r) { - /* XXX: 31? */ - if (r->intno < 32) { + void (*handler)(struct reg *); + + /* + * We'll call the handler only if the interrupt number is > 32, + * which means that we're dealing with an IRQ and not an exception. + */ + if (r->intno >= 32 && (handler = isr[r->intno - 32]) != 0) + handler(r); + /* Entries below index 32 in the IDT are exceptions, we need to hang. */ + else if (r->intno < 32) { tty_write(except[r->intno]); tty_write(". System halted...\n"); __asm__ __volatile__ ("hlt"); } -} - -/* TODO: add comments */ -void -irq_handler(struct reg *r) -{ - void (*handler)(struct reg *); - - if ((handler = irq_routines[r->intno - 32]) != 0) - handler(r); if (r->intno >= 40) - outb(0x20, 0xa0); - outb(0x20, 0x20); -} - -/* TODO: add comments */ -void -irq_add_handler(uint8_t irqno, void (*handler)(struct reg *r)) -{ - irq_routines[irqno] = handler; + outb(P_PIC2_CMD, 0x20); + outb(P_PIC1_CMD, 0x20); } -/* TODO: add comments */ void -irq_rm_handler(uint8_t irqno) +int_add_handler(uint8_t intno, void (*handler)(struct reg *r)) { - irq_routines[irqno] = NULL; + isr[intno] = handler; } diff --git a/boot/idt.h b/boot/idt.h @@ -1,27 +1,18 @@ -#ifndef _KERNEL_IDT_H -#define _KERNEL_IDT_H - -#define N_INT 256 /* There are 256 interrupts in total. */ -#define KERN_CODESEG 0x08 /* Kernel code segment. */ - -struct idt_reg { - uint16_t limit; /* Points at &IDT. */ - uint32_t base; /* Points at the end of the IDT. */ -} __attribute__((packed)); +#ifndef _KERNEL_IDT_H_ +#define _KERNEL_IDT_H_ /* IA-32 */ struct idt_gate { uint16_t off_lo; uint16_t sel; uint8_t zero; - uint8_t attr; + uint8_t flags; uint16_t off_hi; } __attribute__((packed)); - -/* This will be populated by isr_common_stub. */ +/* This will be populated by int_common_stub. */ struct reg { - /* Segment registers. Will be poped last. */ + /* Segment registers. Will be popped last. */ uint32_t gs; uint32_t fs; uint32_t es; @@ -35,7 +26,7 @@ struct reg { uint32_t edx; uint32_t ecx; uint32_t eax; - /* ISR info. Pushed by `push byte`. */ + /* Interrupt info. Pushed by `push byte`. */ uint32_t intno; uint32_t err; /* Pushed by the processor. */ @@ -49,44 +40,43 @@ struct reg { /* `kernel_main` calls this. */ extern void idt_init(void); -extern void isr_handler(struct reg *); -extern void irq_handler(struct reg *); -extern void irq_add_handler(uint8_t, void (*)(struct reg *)); -extern void irq_rm_handler(uint8_t); +/* Called by drivers */ +extern void int_handler(struct reg *); +extern void int_add_handler(uint8_t, void (*)(struct reg *)); -/* The first 32 ISRs are reserved for exceptions. */ -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); +/* The first 32 interrupts are reserved for exceptions. */ +extern void ex0(void); +extern void ex1(void); +extern void ex2(void); +extern void ex3(void); +extern void ex4(void); +extern void ex5(void); +extern void ex6(void); +extern void ex7(void); +extern void ex8(void); +extern void ex9(void); +extern void ex10(void); +extern void ex11(void); +extern void ex12(void); +extern void ex13(void); +extern void ex14(void); +extern void ex15(void); +extern void ex16(void); +extern void ex17(void); +extern void ex18(void); +extern void ex19(void); +extern void ex20(void); +extern void ex21(void); +extern void ex22(void); +extern void ex23(void); +extern void ex24(void); +extern void ex25(void); +extern void ex26(void); +extern void ex27(void); +extern void ex28(void); +extern void ex29(void); +extern void ex30(void); +extern void ex31(void); /* IRQs */ extern void irq0(void); @@ -106,4 +96,4 @@ extern void irq13(void); extern void irq14(void); extern void irq15(void); -#endif /* _KERNEL_IDT_H */ +#endif /* _KERNEL_IDT_H_ */ diff --git a/boot/int.asm b/boot/int.asm @@ -1,39 +1,39 @@ -[extern isr_handler] -[extern irq_handler] +; C functions defined in `idt.h`. +[extern int_handler] ; ISRs -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 +global ex0 +global ex1 +global ex2 +global ex3 +global ex4 +global ex5 +global ex6 +global ex7 +global ex8 +global ex9 +global ex10 +global ex11 +global ex12 +global ex13 +global ex14 +global ex15 +global ex16 +global ex17 +global ex18 +global ex19 +global ex20 +global ex21 +global ex22 +global ex23 +global ex24 +global ex25 +global ex26 +global ex27 +global ex28 +global ex29 +global ex30 +global ex31 ; IRQs global irq0 @@ -53,225 +53,225 @@ global irq13 global irq14 global irq15 -; ================== ISR ====================== +; ================== EXCEPTIONS ====================== ; 0 - Division By Zero -isr0: +ex0: cli push byte 0 push byte 0 - jmp isr_common_stub + jmp int_common_stub ; 1 - Debug -isr1: +ex1: cli push byte 0 push byte 1 - jmp isr_common_stub + jmp int_common_stub ; 2 - Non Maskable Interrupt -isr2: +ex2: cli push byte 0 push byte 2 - jmp isr_common_stub + jmp int_common_stub ; 3 - Breakpoint -isr3: +ex3: cli push byte 0 push byte 3 - jmp isr_common_stub + jmp int_common_stub ; 4 - Into Detected Overflow -isr4: +ex4: cli push byte 0 push byte 4 - jmp isr_common_stub + jmp int_common_stub ; 5 - Out of Bounds -isr5: +ex5: cli push byte 0 push byte 5 - jmp isr_common_stub + jmp int_common_stub ; 6 - Invalid Opcode -isr6: +ex6: cli push byte 0 push byte 6 - jmp isr_common_stub + jmp int_common_stub ; 7 - No Coprocessor -isr7: +ex7: cli push byte 0 push byte 7 - jmp isr_common_stub + jmp int_common_stub ; 8 - Double Fault (with error code) -isr8: +ex8: cli push byte 8 - jmp isr_common_stub + jmp int_common_stub ; 9 - Coprocessor Segment Overrrun -isr9: +ex9: cli push byte 0 push byte 9 - jmp isr_common_stub + jmp int_common_stub ; 10 - Bad TSS (with error code) -isr10: +ex10: cli push byte 10 - jmp isr_common_stub + jmp int_common_stub ; 11 - Segment Not Present (with error code) -isr11: +ex11: cli push byte 11 - jmp isr_common_stub + jmp int_common_stub ; 12 - Stack Fault (with error code) -isr12: +ex12: cli push byte 12 - jmp isr_common_stub + jmp int_common_stub ; 13 - General Protection Fault (with error code) -isr13: +ex13: cli push byte 13 - jmp isr_common_stub + jmp int_common_stub ; 14 - Page Fault (with error code) -isr14: +ex14: cli push byte 14 - jmp isr_common_stub + jmp int_common_stub ; 15 - Unkown Interrupt -isr15: +ex15: cli push byte 0 push byte 15 - jmp isr_common_stub + jmp int_common_stub ; 16 - Coprocessor Fault -isr16: +ex16: cli push byte 0 push byte 16 - jmp isr_common_stub + jmp int_common_stub ; 17 - Alignment Check (486+) -isr17: +ex17: cli push byte 0 push byte 17 - jmp isr_common_stub + jmp int_common_stub ; 18 - Machine Check (Pentium/586+) -isr18: +ex18: cli push byte 0 push byte 18 - jmp isr_common_stub + jmp int_common_stub ; 19 - Reserved -isr19: +ex19: cli push byte 0 push byte 19 - jmp isr_common_stub + jmp int_common_stub ; 20 - Reserved -isr20: +ex20: cli push byte 0 push byte 20 - jmp isr_common_stub + jmp int_common_stub ; 21 - Reserved -isr21: +ex21: cli push byte 0 push byte 21 - jmp isr_common_stub + jmp int_common_stub ; 22 - Reserved -isr22: +ex22: cli push byte 0 push byte 22 - jmp isr_common_stub + jmp int_common_stub ; 23 - Reserved -isr23: +ex23: cli push byte 0 push byte 23 - jmp isr_common_stub + jmp int_common_stub ; 24 - Reserved -isr24: +ex24: cli push byte 0 push byte 24 - jmp isr_common_stub + jmp int_common_stub ; 25 - Reserved -isr25: +ex25: cli push byte 0 push byte 25 - jmp isr_common_stub + jmp int_common_stub ; 26 - Reserved -isr26: +ex26: cli push byte 0 push byte 26 - jmp isr_common_stub + jmp int_common_stub ; 27 - Reserved -isr27: +ex27: cli push byte 0 push byte 27 - jmp isr_common_stub + jmp int_common_stub ; 28 - Reserved -isr28: +ex28: cli push byte 0 push byte 28 - jmp isr_common_stub + jmp int_common_stub ; 29 - Reserved -isr29: +ex29: cli push byte 0 push byte 29 - jmp isr_common_stub + jmp int_common_stub ; 30 - Reserved -isr30: +ex30: cli push byte 0 push byte 30 - jmp isr_common_stub + jmp int_common_stub ; 31 - Reserved -isr31: +ex31: cli push byte 0 push byte 31 - jmp isr_common_stub + jmp int_common_stub ; ================== IRQ ====================== @@ -280,115 +280,116 @@ irq0: cli push byte 0 push byte 32 - jmp irq_common_stub + jmp int_common_stub ; 33 - Keyboard irq1: cli push byte 0 push byte 33 - jmp irq_common_stub + jmp int_common_stub ; 34 Cascade (used internally by the two PICs, never raised) irq2: cli push byte 0 push byte 34 - jmp irq_common_stub + jmp int_common_stub ; 35 - COM2 (if enabled) irq3: cli push byte 0 push byte 35 - jmp irq_common_stub + jmp int_common_stub ; 36 - COM1 (if enabled) irq4: cli push byte 0 push byte 36 - jmp irq_common_stub + jmp int_common_stub ; 37 - LPT2 (if enabled) irq5: cli push byte 0 push byte 37 - jmp irq_common_stub + jmp int_common_stub ; 38 - Floppy Disk irq6: cli push byte 0 push byte 38 - jmp irq_common_stub + jmp int_common_stub ; 39 - LPT1 irq7: cli push byte 0 push byte 39 - jmp irq_common_stub + jmp int_common_stub ; 40 - CMOS real-time clock (if enabled) irq8: cli push byte 0 push byte 40 - jmp irq_common_stub + jmp int_common_stub ; 41 - Peripherals / Legacy SCSI / NIC irq9: cli push byte 0 push byte 41 - jmp irq_common_stub + jmp int_common_stub ; 42 - Peripherals / SCSI / NIC irq10: cli push byte 0 push byte 42 - jmp irq_common_stub + jmp int_common_stub ; 43 - Peripherals / SCSI / NIC irq11: cli push byte 0 push byte 43 - jmp irq_common_stub + jmp int_common_stub ; 44 - PS2 Mouse irq12: cli push byte 0 push byte 44 - jmp irq_common_stub + jmp int_common_stub ; 45 - FPU / Coprocessor / Inter-processor irq13: cli push byte 0 push byte 45 - jmp irq_common_stub + jmp int_common_stub ; 46 - Primary ATA Hard Disk irq14: cli push byte 0 push byte 46 - jmp irq_common_stub + jmp int_common_stub ; 47 - Secondary ATA Hard Disk irq15: cli push byte 0 push byte 47 - jmp irq_common_stub + jmp int_common_stub -; Save the processor state, call the C fault handler and restore the stack frame. -isr_common_stub: +; Save the processor state, call the C interrupt handler and restore the +; stack frame. +int_common_stub: pusha push ds push es @@ -401,7 +402,7 @@ isr_common_stub: mov gs, ax mov eax, esp ; Push the stack. push eax - mov eax, isr_handler + mov eax, int_handler call eax pop eax pop gs @@ -412,29 +413,3 @@ isr_common_stub: add esp, 8 ; Clean up ISR info. sti iret - -; Call the C handler and restore the stack frame. -irq_common_stub: - pusha - push ds - push es - push fs - push gs - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - mov eax, esp - push eax - mov eax, irq_handler - call eax - pop eax - pop gs - pop fs - pop es - pop ds - popa - add esp, 8 - sti - iret diff --git a/boot/io.h b/boot/io.h @@ -0,0 +1,51 @@ +#ifndef _KERNEL_IO_H_ +#define _KERNEL_IO_H_ + +#include <stdint.h> + +static inline uint8_t +inb(uint16_t port) +{ + uint8_t res; + + __asm__ __volatile__ ("in %%dx, %%al" : "=a" (res) : "d" (port)); + return res; +} + +static inline void +outb(uint16_t port, uint8_t v) +{ + __asm__ __volatile__ ("out %%al, %%dx" : : "a" (v), "d" (port)); +} + +static inline uint16_t +inw(uint16_t port) +{ + uint16_t res; + + __asm__ __volatile__ ("in %%dx, %%ax" : "=a" (res) : "d" (port)); + return res; +} + +static inline void +outw(uint16_t port, uint16_t v) +{ + __asm__ __volatile__ ("out %%ax, %%dx" : : "a" (v), "d" (port)); +} + +static inline uint32_t +inl(uint16_t port) +{ + uint32_t res; + + __asm__ __volatile__ ("in %%dx, %%eax" : "=a" (res) : "d" (port)); + return res; +} + +static inline void +outl(uint16_t port, uint32_t v) +{ + __asm__ __volatile__ ("out %%eax, %%dx" : : "a" (v), "d" (port)); +} + +#endif /* _KERNEL_IO_H_ */ diff --git a/boot/kbd.c b/boot/kbd.c @@ -1,5 +1,6 @@ #include "extern.h" #include "idt.h" +#include "port.h" static void kbd_callback(struct reg *); @@ -46,8 +47,7 @@ kbd_callback(struct reg *r) { uint8_t sc; - /* TODO: backspace */ - if ((sc = inb(0x60)) & 0x80) { + if ((sc = inb(P_KBD)) & 0x80) { } else { tty_putc(kbdus[sc]); } @@ -61,5 +61,5 @@ kbd_callback(struct reg *r) void kbd_init(void) { - irq_add_handler(1, kbd_callback); + int_add_handler(1, kbd_callback); } diff --git a/boot/kbd.h b/boot/kbd.h @@ -1,3 +1,8 @@ +#ifndef _KERNEL_KBD_H_ +#define _KERNEL_KBD_H_ + #include "extern.h" extern void kbd_init(void); + +#endif /* _KERNEL_KBD_H_ */ diff --git a/boot/kernel.c b/boot/kernel.c @@ -1,17 +0,0 @@ -#include "extern.h" -#include "idt.h" -#include "kbd.h" -#include "timer.h" - -void -kernel_main(void) -{ - tty_init(); - idt_init(); - timer_init(50); - kbd_init(); - /* Enable interrupts so that the IRQs can work now. */ - __asm__ __volatile__ ("sti"); - tty_write("Nothing to see here yet. At least it booted.\n"); - __asm__ __volatile__ ("hlt"); -} diff --git a/boot/kmain.c b/boot/kmain.c @@ -0,0 +1,16 @@ +#include "extern.h" +#include "idt.h" +#include "kbd.h" +#include "timer.h" + +void +kmain(void) +{ + tty_clear(); + idt_init(); + timer_init(50); + kbd_init(); + /* Enable interrupts so that the handlers can work now. */ + __asm__ __volatile__ ("sti"); + __asm__ __volatile__ ("hlt"); +} diff --git a/boot/loader.asm b/boot/loader.asm @@ -1,6 +1,6 @@ [bits 32] -[extern kernel_main] +[extern kmain] _start: - call kernel_main + call kmain jmp $ diff --git a/boot/port.h b/boot/port.h @@ -0,0 +1,14 @@ +#ifndef _KERNEL_PORT_H +#define _KERNEL_PORT_H + +#define P_PIC1_CMD 0x20 +#define P_PIC2_CMD 0xa0 +#define P_PIC1_DATA (P_PIC1_CMD + 1) +#define P_PIC2_DATA (P_PIC2_CMD + 1) +#define P_TIMER_CMD 0x43 +#define P_TIMER_DATA 0x40 +#define P_CURS_CMD 0x3d4 +#define P_CURS_DATA 0x3d5 +#define P_KBD 0x60 + +#endif /* _KERNEL_PORT_H */ diff --git a/boot/string.c b/boot/string.c @@ -1,5 +1,32 @@ #include "string.h" +void * +memset(void *dst, int v, size_t len) +{ + unsigned char *dst0; + + dst0 = dst; + while (len--) + *dst0++ = v; + + return dst; +} + +void * +memcpy(void *dst, const void *src, size_t len) +{ + const unsigned char *src0; + unsigned char *dst0; + + src0 = src; + dst0 = dst; + + while (len--) + *dst0++ = *src0++; + + return dst; +} + size_t strlen(const char *str) { diff --git a/boot/string.h b/boot/string.h @@ -1,10 +1,11 @@ -#ifndef _KERNEL_STRING_H -#define _KERNEL_STRING_H +#ifndef _KERNEL_STRING_H_ +#define _KERNEL_STRING_H_ #include <stddef.h> -#include <stdint.h> +extern void *memset(void *, int, size_t); +extern void *memcpy(void *, const void *, size_t); extern size_t strlen(const char *); extern int strcmp(const char *, const char *); -#endif /* _KERNEL_STRING_H */ +#endif /* _KERNEL_STRING_H_ */ diff --git a/boot/timer.c b/boot/timer.c @@ -1,5 +1,6 @@ #include "extern.h" #include "idt.h" +#include "port.h" static void timer_callback(struct reg *); @@ -17,8 +18,8 @@ timer_init(uint32_t hz) { uint32_t div = 1193180 / hz; - irq_add_handler(0, timer_callback); - outb(0x36, 0x43); - outb((uint8_t)(div & 0xff), 0x40); - outb((uint8_t)((div >> 8) & 0xff), 0x40); + int_add_handler(0, timer_callback); + outb(P_TIMER_CMD, 0x36); + outb(P_TIMER_DATA, (uint8_t)(div & 0xff)); + outb(P_TIMER_DATA, (uint8_t)((div >> 8) & 0xff)); } diff --git a/boot/timer.h b/boot/timer.h @@ -1,6 +1,6 @@ -#ifndef _KERNEL_TIMER_H -#define _KERNEL_TIMER_H +#ifndef _KERNEL_TIMER_H_ +#define _KERNEL_TIMER_H_ extern void timer_init(uint32_t); -#endif /* _KERNEL_TIMER_H */ +#endif /* _KERNEL_TIMER_H_ */ diff --git a/boot/tty.c b/boot/tty.c @@ -1,22 +1,23 @@ +#include "io.h" +#include "port.h" #include "tty.h" -#define _PUTC(c) (((uint16_t)tty.color << 8) | c) -#define _COLOR(f, b) (f | b << 4) - static struct tty_info tty; void -tty_init(void) +tty_clear(void) { size_t y, x; + tty_curs_enable(0x0e, 0x0f); /* Low cursor shape. */ + tty_curs_setpos(0, 0); tty.row = 0; tty.col = 0; - tty.color = _COLOR(VGA_GREEN, VGA_BLACK); - tty.buf = (uint16_t *)0xb8000; - for (x = 0; x < VGA_COLS; x++) - for (y = 0; y < VGA_ROWS; y++) - tty.buf[y * VGA_COLS + x] = _PUTC(' '); + tty.color = VGA_SET_COLOR(VGA_BLUE, VGA_WHITE); + tty.buf = (uint16_t *)_VGA_MEM; + for (x = 0; x < _VGA_COLS; x++) + for (y = 0; y < _VGA_ROWS; y++) + tty.buf[y * _VGA_COLS + x] = _PUTC(' '); } void @@ -29,6 +30,7 @@ tty_putc(char c) break; case '\b': tty.col--; + tty.buf[tty.row * _VGA_COLS + tty.col] = _PUTC(' '); break; case '\r': tty.col = 0; @@ -37,18 +39,19 @@ tty_putc(char c) tty.col += 8; break; default: - tty.buf[tty.row * VGA_COLS + tty.col] = _PUTC(c); + tty.buf[tty.row * _VGA_COLS + tty.col] = _PUTC(c); tty.col++; } - if (tty.row >= VGA_ROWS) { + if (tty.row >= _VGA_ROWS) { tty.row = 0; tty.col = 0; } - if (tty.col >= VGA_COLS) { + if (tty.col >= _VGA_COLS) { tty.row++; tty.col = 0; } + tty_curs_setpos(tty.col, tty.row); } void @@ -57,3 +60,61 @@ tty_write(const char *str) while (*str != '\0') tty_putc(*str++); } + +/* + * The arguments `start` and `end` define the shape of the cursor. + * The lowest scaline value is 0x00 and the highest 0x0f. A cursor shape + * with `start = 0x00` and `end = 0x0f` is effectively a block cursor. + */ +void +tty_curs_enable(uint8_t start, uint8_t end) +{ + /* 0x0a: Low cursor shape */ + outb(P_CURS_CMD, 0x0a); + outb(P_CURS_DATA, (inb(P_CURS_DATA) & 0xc0) | start); + outb(P_CURS_CMD, 0x0b); + outb(P_CURS_DATA, (inb(P_CURS_DATA) & 0xe0) | end); +} + +void +tty_curs_disable(void) +{ + outb(P_CURS_CMD, 0x0a); + /* + * Bit representation: + * 7 6 | 5 | 4 0 + * Unused | Disable | Shape + */ + outb(P_CURS_DATA, 0x20); +} + +/* + * Returns the position in `y * _VGA_COLS + x` format. + * x = pos % _VGA_COLS + * y = pos / _VGA_COLS + */ +uint16_t +tty_curs_getpos(void) +{ + uint16_t pos = 0; + + outb(P_CURS_CMD, 0x0e); + pos |= (uint16_t)inb(P_CURS_DATA) << 8; + outb(P_CURS_CMD, 0x0f); + pos |= inb(P_CURS_DATA); + + return pos; +} + +void +tty_curs_setpos(int x, int y) +{ + uint16_t pos = y * _VGA_COLS + x; + + /* Expect 8 highest bits */ + outb(P_CURS_CMD, 0x0e); + outb(P_CURS_DATA, (uint8_t)((pos >> 8) & 0xff)); + /* Expect 8 lowest bits */ + outb(P_CURS_CMD, 0x0f); + outb(P_CURS_DATA, (uint8_t)(pos & 0xff)); +} diff --git a/boot/tty.h b/boot/tty.h @@ -1,11 +1,14 @@ -#ifndef _KERNEL_TTY_H -#define _KERNEL_TTY_H +#ifndef _KERNEL_TTY_H_ +#define _KERNEL_TTY_H_ #include <stddef.h> -#include <stdint.h> -#define VGA_COLS 80 -#define VGA_ROWS 25 +#define _VGA_MEM 0xb8000; +#define _VGA_COLS 80 +#define _VGA_ROWS 25 +#define _PUTC(c) (((uint16_t)tty.color << 8) | (c)) + +#define VGA_SET_COLOR(fg, bg) ((fg) | (bg) << 4) struct tty_info { volatile uint16_t *buf; @@ -33,8 +36,12 @@ enum vga_color { VGA_WHITE, }; -extern void tty_init(void); +extern void tty_clear(void); extern void tty_putc(char); extern void tty_write(const char *); +extern void tty_curs_enable(uint8_t, uint8_t); +extern void tty_curs_disable(void); +extern void tty_curs_setpos(int, int); +extern uint16_t tty_curs_getpos(void); -#endif /* _KERNEL_TTY_H */ +#endif /* _KERNEL_TTY_H_ */ diff --git a/boot/unused/a20.asm b/boot/unused/a20.asm @@ -1,153 +0,0 @@ -call a20_test -call a20_enable - -; Test to see if the A20 line is enabled. -; As a reference we will use the magic number 0xaa55 since we know it's always -; going to have the same value and be located at address 0x7dfe (0x7c00 + 510). -; If the return code in `ax` is 1, the A20 is enabled, and if it's 0 it's disabled. -a20_test: - pusha - - mov ax, [0x7dfe] ; 0x7c00 + 510. The magic number is there. - mov dx, ax - -; We'll try to advance 1MB in memory. If the end result hasn't wrapped up -; around zero, it means that our processor is able to access more than 1MB -; of addresses, so the A20 is enabled. Using 0x7dfe again, the expected -; address after the 1MB advance should be: -; -; 0x100000 + 0x7dfe = 0x107dfe -; -; The formula for memory segmentation is: -; -; address = segment * 16 + offset -; -; To find the offset, the segment will be 0xffff, so `0xffff * 16 = 0xffff0`. -; Applying the formula to calculate our offset, we have: -; -; 0xffff0 + offset = 0x107dfe => -; offset = 0x107dfe - 0xffff0 => -; offset = 0x7e0e - - push bx - mov bx, 0xffff - mov es, bx ; Assign value to the segment register. - pop bx - - mov bx, 0x7e0e - mov dx, [es:bx] ; If the A20 line is disabled we get 0xaa55. - - cmp ax, dx - je cont ; If they're equal, the A20 line might be disabled. - popa - mov ax, 0x01 ; Success code 1. - ret -cont: - mov ax, [0x7dff] - mov dx, ax - - push bx - mov bx, 0xffff ; Make another segment. - mov es, bx - pop bx - - mov bx, 0x7e0f - mov dx, [es:bx] - - cmp ax, dx ; Now it really is disabled if ax == [es:bx]. - je exit - popa - mov ax, 0x01 ; Success code 1. - ret -exit: - popa - xor ax, ax ; Error code 0. - ret - -; Enable the A20 line. We'll try enabling it using the following ways: -; - BIOS interrupt. -; - Keyboard controller. -; - FastA20. -a20_enable: - pusha - -; BIOS interrupt. - mov ax, 0x2401 ; A20-Gate Active. - int 0x15 - - call a20_test - cmp ax, 0x01 - je a20_done - jmp a20_fail - -; Keyboard controller. - sti ; Enable interrupts. - - call a20_waitc - mov al, 0xad ; Disable controller. - out 0x64, al ; Send data to port 0x64. - call a20_waitc - mov al, 0xd0 ; We want to read from the controller. - out 0x64, al - - call a20_waitd - in al, 0x60 ; Read data from port 0x60. - push ax ; Save data. - - call a20_waitc - mov al, 0xd1 ; We want to send data. - out 0x64, al - - call a20_waitc - pop ax - or al, 0x02 ; Write the second bit back. - out 0x60, al ; Send data to the data port. - - call a20_waitc - mov al, 0xae ; Enable controller. - out 0x64, al - - call a20_waitc - sti ; Enable interrupts again. - - call a20_test - cmp ax, 0x01 - je a20_done - jmp a20_fail - -; Wait for the keyboard controller to be available for receiving commands. -a20_waitc: - in al, 0x64 ; Read from port 0x64. - test al, 0x02 ; Test the second bit. - jnz a20_waitc ; If it's 1, it's busy. - ret -; Wait for the keyboard controller to send data back. -a20_waitd: - in al, 0x64 ; Read from port 0x64 again. - test al, 0x01 ; Test the first bit. - jz a20_waitd ; If it's 1, the data is not ready to be sent. - ret - -; FastA20 - in al, 0x92 ; FastA20 uses port 0x92. - or al, 0x02 ; Mask second bit. - out 0x92, al ; Send data back to port 0x92. - - call a20_test - cmp ax, 0x01 - je a20_done - jmp a20_fail - -; The A20 is enabled. Go on. -a20_done: - popa - ret - -; The A20 is disabled. -a20_fail: - mov si, str_a20_fail - call puts - jmp $ - - -str_a20_fail: db "The A20 Line is disabled", 0x0a, 0x0d, 0x00 diff --git a/boot/unused/lm.asm b/boot/unused/lm.asm @@ -0,0 +1,106 @@ +; call in pm_mode + call lm_check + +; Check for Long Mode. +lm_check: + pusha + pushfd ; Push EFLAGS register to the stack. + pop eax ; Pop EFLAGS registers to `eax`. + mov ecx, eax ; Keep a backup in `ecx`. + xor eax, 1 << 21 ; Flip the 21st bit if the it's not 1. + push eax + popfd ; Pop `eax` to EFLAGS. + + pushfd + pop eax ; Copy EFLAGS back to `eax`. + push ecx + popfd ; Restore the flipped version. + xor eax, ecx + jz lm_fail ; If EFLAGS' value hasn't changed, we have CPUID. + + mov eax, 0x80000000 ; CPUID argument. + cpuid ; CPU identification. `eax` is now populated with info. + cmp eax, 0x80000001 ; If it's less than 0x80000000 we cannot have Long Mode. + jb lm_fail + + mov eax, 0x80000001 ; Get extended processor information. + cpuid + test edx, 1 << 29 + jz lm_fail ; If the 29th bit is 0, we cannot have Long Mode. + + popa + ret + +; No long mode. +lm_fail: + popa + call kernel_exec + jmp $ ; Safety hang. + +; We'll check for (and switch to) Long Mode here. +lm_enter: + cli + mov eax, cr0 + or eax, 1 << 31 ; Paging Enable. + mov cr0, eax +; Set up PAE paging. + mov edi, 0x1000 ; Page table starts 4KiB in memory. + mov cr3, edi ; Hold the location of the highest page table. + xor eax, eax ; Clear memory space. + mov ecx, 4096 + rep stosd ; Write 4096 dwords with the value 0. + mov edi, 0x1000 ; Go to the initial location. + +; Page table locations: +; Page Map Level 4 Table (PML4T): 0x1000 +; Page Directory Pointer Table (PDPT): 0x2000 +; Page Directory Table (PDT): 0x3000 +; Page Table (PT); 0x4000 +; +; In order for each table to point to each other we'll use a size directive. +; Since tables are 4KiB away from each other we'll move dwords. + mov dword [edi], 0x2003 ; PML4T -> PDPT + add edi, 0x1000 ; Move 4KiB forward. + mov dword [edi], 0x3003 ; PDPT -> PDT + add edi, 0x1000 + mov dword [edi], 0x4003 ; PDT -> PT + add edi, 0x1000 + + mov dword ebx, 3 ; Map beginning of code. + mov ecx, 512 ; Do it 512 times. +setentry: + mov dword [edi], ebx + add ebx, 0x1000 + add edi, 8 ; Go to the next entry in the page table. + loop setentry + +; Enable the 6th bit in CR4 (Physical Address Extension Bit) to tell +; the CPU that we're using PAE paging. + mov eax, cr4 + or eax, 1 << 5 + mov cr4, eax + +; Activate Long Mode and Paging using the EFER. + mov ecx, 0xc0000080 + rdmsr ; Copy the contents of EFER to `eax`. + or eax, 1 << 8 ; Set LME bit to enable Long Mode. + wrmsr ; Write `eax` back to EFER. + + lgdt [gdt_ptr] ; Load the GDT. + jmp GDT_CODESEG:lm_init ; Switch to Long Mode. + +; We're in Long Mode now. +[bits 64] +lm_init: + mov ax, GDT_DATASEG + mov ds, ax + mov ss, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov rbp, 0x90000 + mov rsp, rbp + call kernel_exec + jmp $ +