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:
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 $
+