diff options
author | Marvin Borner | 2021-07-10 15:51:54 +0200 |
---|---|---|
committer | Marvin Borner | 2021-07-10 15:51:54 +0200 |
commit | ad56eb28f0614db4b7656ade390f1c79b446cbc9 (patch) | |
tree | 7d42ab3c9f36c188dbf54a25b1db1c3db5acb1bd | |
parent | 176b6eb808f4d02d871c30f6ec19daa179f374d1 (diff) |
A20, E820h, Real Mode Emulation
-rw-r--r-- | makefile | 4 | ||||
-rw-r--r-- | src/loader/a20.c | 59 | ||||
-rw-r--r-- | src/loader/cpu.c | 17 | ||||
-rw-r--r-- | src/loader/ent.asm | 33 | ||||
-rw-r--r-- | src/loader/gdt.c | 49 | ||||
-rw-r--r-- | src/loader/impl/mb1.c | 21 | ||||
-rw-r--r-- | src/loader/inc/a20.h | 10 | ||||
-rw-r--r-- | src/loader/inc/cpu.h | 3 | ||||
-rw-r--r-- | src/loader/inc/def.h | 1 | ||||
-rw-r--r-- | src/loader/inc/gdt.h | 25 | ||||
-rw-r--r-- | src/loader/inc/int.h | 2 | ||||
-rw-r--r-- | src/loader/inc/mem.h | 32 | ||||
-rw-r--r-- | src/loader/inc/rem.h | 29 | ||||
-rw-r--r-- | src/loader/int.c | 23 | ||||
-rw-r--r-- | src/loader/link.ld | 14 | ||||
-rw-r--r-- | src/loader/main.c | 4 | ||||
-rw-r--r-- | src/loader/mem.c | 125 | ||||
-rw-r--r-- | src/loader/rem.asm | 139 |
18 files changed, 570 insertions, 20 deletions
@@ -23,7 +23,7 @@ AOBJS = $(patsubst $(SRC)/%.asm,$(BLD)/%_asm.o,$(ASRCS)) WARNINGS = -Wall -Wextra -Wshadow -Wpointer-arith -Wwrite-strings -Wredundant-decls -Wnested-externs -Wformat=2 -Wmissing-declarations -Wstrict-prototypes -Wmissing-prototypes -Wcast-qual -Wswitch-default -Wswitch-enum -Wunreachable-code -Wundef -Wold-style-definition -Wvla -pedantic-errors # Disable some GCC features and boilerplate generation -CFLAGS = $(WARNINGS) -std=c99 -m32 -nostdlib -nostdinc -ffunction-sections -fno-builtin -fno-profile-generate -fno-omit-frame-pointer -fno-common -fno-asynchronous-unwind-tables -fno-stack-protector -fno-pie -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -Ofast -ffreestanding -Wl,-estart -I$(SRC)/loader/inc/ +CFLAGS = $(WARNINGS) -std=c99 -m32 -nostdlib -nostdinc -ffunction-sections -fno-builtin -fno-profile-generate -fno-omit-frame-pointer -fno-common -fno-asynchronous-unwind-tables -fno-stack-protector -fno-pie -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -Ofast -ffreestanding -Wl,-e_start -I$(SRC)/loader/inc/ ASFLAGS = -f elf32 @@ -46,7 +46,7 @@ $(BLD)/boot.bin: $(BLD)/loader.bin @$(AS) $(ASFLAGS) -f bin $(SRC)/entry/bootsector.asm -o $@ $(BLD)/loader.o: $(COBJS) $(AOBJS) - @$(LD) -N -z max-page-size=0x1000 -estart -T$(SRC)/loader/link.ld -o $@ $^ + @$(LD) -N -z max-page-size=0x1000 -e_start -T$(SRC)/loader/link.ld -o $@ $^ $(BLD)/loader.bin: $(BLD)/loader.o @$(OC) -O binary $^ $@ diff --git a/src/loader/a20.c b/src/loader/a20.c new file mode 100644 index 0000000..b59500a --- /dev/null +++ b/src/loader/a20.c @@ -0,0 +1,59 @@ +// MIT License, Copyright (c) 2021 Marvin Borner +// https://wiki.osdev.org/A20_Line + +#include <a20.h> +#include <cpu.h> +#include <def.h> +#include <pnc.h> +#include <rem.h> + +static u8 a20_check(void) +{ + if (mminw(0x7dfe) != mminw(0x7dfe + 0x100000)) + return 1; + + mmoutw(0x7dfe, ~mminw(0x7dfe)); + + if (mminw(0x7dfe) != mminw(0x7dfe + 0x100000)) + return 1; + + return 0; +} + +void a20_enable(void) +{ + if (a20_check()) + return; + + // BIOS method + struct rem_regs r = { 0 }; + r.eax = 0x2401; + rem_int(0x15, &r, &r); + + if (a20_check()) + return; + + // Keyboard controller method + while (inb(0x64) & 2) + ; + outb(0x64, 0xad); + while (inb(0x64) & 2) + ; + outb(0x64, 0xd0); + while (!(inb(0x64) & 1)) + ; + u8 b = inb(0x60); + while (inb(0x64) & 2) + ; + outb(0x64, 0xd1); + while (inb(0x64) & 2) + ; + outb(0x60, b | 2); + while (inb(0x64) & 2) + ; + outb(0x64, 0xae); + while (inb(0x64) & 2) + ; + + assert(a20_check()); +} diff --git a/src/loader/cpu.c b/src/loader/cpu.c index a0694ed..d5b56df 100644 --- a/src/loader/cpu.c +++ b/src/loader/cpu.c @@ -41,3 +41,20 @@ void outl(u16 port, u32 data) { __asm__ volatile("outl %0, %1" ::"a"(data), "Nd"(port)); } + +/** + * Move memory + */ + +#define WORD_PTR(PTR) (*((u16 *)(PTR))) +u16 mminw(u32 addr) +{ + u16 ret; + __asm__ volatile("movw %1, %0" : "=r"(ret) : "m"(WORD_PTR(addr)) : "memory"); + return ret; +} + +void mmoutw(u32 addr, u16 value) +{ + __asm__ volatile("movw %1, %0" : "=m"(WORD_PTR(addr)) : "ir"(value) : "memory"); +} diff --git a/src/loader/ent.asm b/src/loader/ent.asm new file mode 100644 index 0000000..47cf49b --- /dev/null +++ b/src/loader/ent.asm @@ -0,0 +1,33 @@ +; MIT License, Copyright (c) 2021 Marvin Borner + +extern bss_begin +extern bss_end +extern start +extern gdt + +section .entry + +global _start +_start: + cld + + ; Clear bss section + xor al, al + mov edi, bss_begin + mov ecx, bss_end + sub ecx, bss_begin + rep stosb + + lgdt [gdt] + jmp 0x18:.reload_cs + +.reload_cs: + mov eax, 0x20 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + cli + jmp start diff --git a/src/loader/gdt.c b/src/loader/gdt.c new file mode 100644 index 0000000..853b793 --- /dev/null +++ b/src/loader/gdt.c @@ -0,0 +1,49 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#include <gdt.h> + +static struct gdt_desc gdt_descs[] = { { 0 }, + + { .limit = 0xffff, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0x9a, + .granularity = 0x00, + .base_hi = 0x00 }, + + { .limit = 0xffff, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0x92, + .granularity = 0x00, + .base_hi = 0x00 }, + + { .limit = 0xffff, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0x9a, + .granularity = 0xcf, + .base_hi = 0x00 }, + + { .limit = 0xffff, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0x92, + .granularity = 0xcf, + .base_hi = 0x00 }, + + { .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0x9a, + .granularity = 0x20, + .base_hi = 0x00 }, + + { .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0x92, + .granularity = 0x00, + .base_hi = 0x00 } }; + +REAL struct gdtr gdt = { sizeof(gdt_descs) - 1, (u32)gdt_descs, 0 }; diff --git a/src/loader/impl/mb1.c b/src/loader/impl/mb1.c index 4aa267a..e713dd7 100644 --- a/src/loader/impl/mb1.c +++ b/src/loader/impl/mb1.c @@ -4,6 +4,7 @@ #include <elf.h> #include <impl/mb1.h> #include <lib.h> +#include <mem.h> #include <pnc.h> // The address where data gets stored @@ -39,6 +40,22 @@ static u32 mb1_store(void *data, u32 size) return MB1_LOAD_ADDRESS + (offset - size); } +static void mb1_store_mmap(struct mb1_info *info) +{ + struct mem_map *mem_map = mem_map_get(); + info->flags |= MB1_INFO_MEM_MAP; + info->mmap_length = mem_map->count * sizeof(struct mb1_mmap_entry); + info->mmap_addr = mb1_store(NULL, 0); + for (u32 i = 0; i < mem_map->count; i++) { + struct mb1_mmap_entry mmap_entry; + mmap_entry.struct_size = sizeof(mmap_entry) - 4; + mmap_entry.addr_low = mem_map->entry[i].base; + mmap_entry.len_low = mem_map->entry[i].length; + mmap_entry.type = mem_map->entry[i].type; + mb1_store(&mmap_entry, sizeof(mmap_entry)); + } +} + // Load the mb1 structs into memory static void mb1_load(struct mb1_entry *entry) { @@ -55,6 +72,10 @@ static void mb1_load(struct mb1_entry *entry) info->flags |= MB1_INFO_BOOT_LOADER_NAME; char loader_name[] = "SegelBoot"; info->boot_loader_name = mb1_store(loader_name, sizeof(loader_name)); + + // Set memory map + /* if (entry->flags & 2) TODO */ + mb1_store_mmap(info); } // Jump to kernel with correct info pointer in eax diff --git a/src/loader/inc/a20.h b/src/loader/inc/a20.h new file mode 100644 index 0000000..705ada0 --- /dev/null +++ b/src/loader/inc/a20.h @@ -0,0 +1,10 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#ifndef A20_H +#define A20_H + +#include <def.h> + +void a20_enable(void); + +#endif diff --git a/src/loader/inc/cpu.h b/src/loader/inc/cpu.h index 0236002..4fa7d0c 100644 --- a/src/loader/inc/cpu.h +++ b/src/loader/inc/cpu.h @@ -13,4 +13,7 @@ void outb(u16 port, u8 data); void outw(u16 port, u16 data); void outl(u16 port, u32 data); +u16 mminw(u32 addr); +void mmoutw(u32 addr, u16 value); + #endif diff --git a/src/loader/inc/def.h b/src/loader/inc/def.h index a8ee30b..e42c372 100644 --- a/src/loader/inc/def.h +++ b/src/loader/inc/def.h @@ -28,6 +28,7 @@ typedef __builtin_va_list va_list; #define STRINGIFY_PARAM(a) #a #define STRINGIFY(a) STRINGIFY_PARAM(a) +#define REAL __attribute__((section(".realmode"))) #define PACKED __attribute__((packed)) #endif diff --git a/src/loader/inc/gdt.h b/src/loader/inc/gdt.h new file mode 100644 index 0000000..1ff20b7 --- /dev/null +++ b/src/loader/inc/gdt.h @@ -0,0 +1,25 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#ifndef GDT_H +#define GDT_H + +#include <def.h> + +struct gdtr { + u16 limit; + u32 ptr; + u32 pad; +} PACKED; + +struct gdt_desc { + u16 limit; + u16 base_low; + u8 base_mid; + u8 access; + u8 granularity; + u8 base_hi; +} PACKED; + +extern struct gdtr gdt; + +#endif diff --git a/src/loader/inc/int.h b/src/loader/inc/int.h index ccd9ea8..8336cd8 100644 --- a/src/loader/inc/int.h +++ b/src/loader/inc/int.h @@ -43,6 +43,8 @@ struct idt_ptr { void *base; } PACKED; +extern struct idt_ptr idt; + void idt_install(void); void int_event_handler_add(u32 int_no, void (*handler)(void)); diff --git a/src/loader/inc/mem.h b/src/loader/inc/mem.h new file mode 100644 index 0000000..7153465 --- /dev/null +++ b/src/loader/inc/mem.h @@ -0,0 +1,32 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#ifndef MEM_H +#define MEM_H + +#include <def.h> + +enum mem_entry_type { + MEM_NONE, + MEM_USABLE, + MEM_RESERVED, + MEM_RECLAIMABLE, + MEM_ACPI_NVS, + MEM_UNUSABLE, +}; + +// Generalised memory map entry struct +struct mem_entry { + u32 base; + u32 length; + u32 type; +}; + +struct mem_map { + struct mem_entry *entry; + u32 count; +}; + +void mem_map(void); +struct mem_map *mem_map_get(void); + +#endif diff --git a/src/loader/inc/rem.h b/src/loader/inc/rem.h new file mode 100644 index 0000000..61eb30a --- /dev/null +++ b/src/loader/inc/rem.h @@ -0,0 +1,29 @@ +// MIT License, Copyright (c) 2021 Marvin Borner +// Real mode emulation - Implementation by Napalm (see rem.asm) + +#ifndef REM_H +#define REM_H + +#include <def.h> + +#define EFLAGS_CF (1 << 0) // Carry flag +#define EFLAGS_ZF (1 << 6) // Zero flag + +struct rem_regs { + u16 gs; + u16 fs; + u16 es; + u16 ds; + u32 eflags; + u32 ebp; + u32 edi; + u32 esi; + u32 edx; + u32 ecx; + u32 ebx; + u32 eax; +} PACKED; + +void rem_int(u8 int_num, struct rem_regs *out_regs, struct rem_regs *in_regs); + +#endif diff --git a/src/loader/int.c b/src/loader/int.c index 6bfea09..8fc0ae3 100644 --- a/src/loader/int.c +++ b/src/loader/int.c @@ -10,31 +10,22 @@ */ extern u32 int_table[]; -static struct idt_entry idt[256] = { 0 }; -static struct idt_ptr idt_ptr = { 0 }; +static struct idt_entry idt_entries[256] = { 0 }; +REAL struct idt_ptr idt = { .size = sizeof(idt_entries) - 1, .base = &idt_entries }; void idt_install(void) { - idt_ptr.size = sizeof(idt) - 1; - idt_ptr.base = &idt; - for (u8 i = 0; i < 3; i++) - idt[i] = IDT_ENTRY(int_table[i], 0x08, INT_GATE); + idt_entries[i] = IDT_ENTRY(int_table[i], 0x08, INT_GATE); - idt[3] = IDT_ENTRY(int_table[3], 0x08, INT_TRAP); - idt[4] = IDT_ENTRY(int_table[4], 0x08, INT_TRAP); + idt_entries[3] = IDT_ENTRY(int_table[3], 0x08, INT_TRAP); + idt_entries[4] = IDT_ENTRY(int_table[4], 0x08, INT_TRAP); for (u8 i = 5; i < 48; i++) - idt[i] = IDT_ENTRY(int_table[i], 0x08, INT_GATE); - - idt[128] = IDT_ENTRY(int_table[48], 0x08, INT_GATE | INT_USER); - idt[129] = IDT_ENTRY(int_table[49], 0x08, INT_GATE); + idt_entries[i] = IDT_ENTRY(int_table[i], 0x08, INT_GATE); // Load table - __asm__ volatile("lidt %0" : : "m"(idt_ptr)); - - // Enable interrupts - __asm__ volatile("sti"); + __asm__ volatile("lidt %0" : : "m"(idt)); } /** diff --git a/src/loader/link.ld b/src/loader/link.ld index aa97f49..fe00d82 100644 --- a/src/loader/link.ld +++ b/src/loader/link.ld @@ -2,16 +2,24 @@ OUTPUT_FORMAT("elf32-i386") OUTPUT_ARCH(i386) -ENTRY(start) +ENTRY(_start) phys = 0x7e00; SECTIONS { . = phys; + .entry : + { + *(.entry*) + } + + .realmode : { + *(.realmode*) + } + .text : { - *(.text.start) *(.text*) } @@ -27,8 +35,10 @@ SECTIONS .bss : { + bss_begin = .; *(COMMON) *(.bss) + bss_end = .; } end = .; diff --git a/src/loader/main.c b/src/loader/main.c index ed3014b..54f35be 100644 --- a/src/loader/main.c +++ b/src/loader/main.c @@ -1,5 +1,6 @@ // MIT License, Copyright (c) 2021 Marvin Borner +#include <a20.h> #include <acpi.h> #include <cfg.h> #include <def.h> @@ -7,6 +8,7 @@ #include <ide.h> #include <int.h> #include <log.h> +#include <mem.h> #include <pci.h> #include <pic.h> #include <sel.h> @@ -21,6 +23,8 @@ int start(u8 disk); int start(u8 disk) { boot_disk = disk; + mem_map(); + a20_enable(); vga_clear(); serial_install(); diff --git a/src/loader/mem.c b/src/loader/mem.c new file mode 100644 index 0000000..698c85d --- /dev/null +++ b/src/loader/mem.c @@ -0,0 +1,125 @@ +// MIT License, Copyright (c) 2021 Marvin Borner +// Memory map generator + +#include <log.h> +#include <mem.h> +#include <pnc.h> +#include <rem.h> + +/** + * Memory map using e820 BIOS call + */ + +#define E820_MAX_ENTRIES 256 // Spec + +enum e820_entry_type { + E820_MEM_NONE, + E820_MEM_USABLE, + E820_MEM_RESERVED, + E820_MEM_RECLAIMABLE, + E820_MEM_ACPI_NVS, + E820_MEM_UNUSABLE, +}; + +struct e820_entry { + u32 base_low; + u32 base_high; + u32 length_low; + u32 length_high; + enum e820_entry_type type; + u32 unused; +} PACKED; + +u16 e820_count = 0; +struct e820_entry e820_map[E820_MAX_ENTRIES] = { 0 }; + +static u8 mem_e820(void) +{ + struct rem_regs r = { 0 }; + + for (u32 i = 0; i < E820_MAX_ENTRIES; i++) { + struct e820_entry entry = { 0 }; + + r.eax = 0xe820; + r.ecx = 24; + r.edx = 0x534d4150; + r.edi = (u32)&entry; + rem_int(0x15, &r, &r); + + if (r.eflags & EFLAGS_CF) { + e820_count = i; + return 1; + } + + e820_map[i] = entry; + + if (!r.ebx) { + e820_count = i++; + return 1; + } + } + + return 0; +} + +/** + * Configure memory map + */ + +#define MAP_MAX_ENTRIES 256 + +static struct mem_entry mem[MAP_MAX_ENTRIES] = { 0 }; +static struct mem_map map = { .entry = mem, .count = 0 }; + +static void mem_map_e820(void) +{ + u32 i; + for (i = 0; i < COUNT(e820_map); i++) { + struct e820_entry *e820_entry = &e820_map[i]; + struct mem_entry *map_entry = &mem[i]; + + map_entry->base = e820_entry->base_low; + map_entry->length = e820_entry->length_low; + + // Set type accordingly + switch (e820_entry->type) { + case E820_MEM_NONE: + map_entry->type = MEM_USABLE; + continue; + case E820_MEM_USABLE: + map_entry->type = MEM_USABLE; + break; + case E820_MEM_RESERVED: + map_entry->type = MEM_RESERVED; + break; + case E820_MEM_RECLAIMABLE: + map_entry->type = MEM_RECLAIMABLE; + break; + case E820_MEM_ACPI_NVS: + map_entry->type = MEM_ACPI_NVS; + break; + case E820_MEM_UNUSABLE: + map_entry->type = MEM_UNUSABLE; + break; + default: + panic("Unknown e820 type\n"); + } + } + + map.count = i; +} + +struct mem_map *mem_map_get(void) +{ + return ↦ +} + +void mem_map(void) +{ + if (mem_e820()) { + mem_map_e820(); + return; + } + + panic("Couldn't find memory map\n"); +} diff --git a/src/loader/rem.asm b/src/loader/rem.asm new file mode 100644 index 0000000..7a33048 --- /dev/null +++ b/src/loader/rem.asm @@ -0,0 +1,139 @@ +; Real mode interrupt from protected mode +; Written by mintsuki, licensed under BSD 2-Clause "Simplified" License + +section .realmode + +global rem_int +rem_int: + ; Self-modifying code: int $int_no + mov al, byte [esp+4] + mov byte [.int_no], al + + ; Save out_regs + mov eax, dword [esp+8] + mov dword [.out_regs], eax + + ; Save in_regs + mov eax, dword [esp+12] + mov dword [.in_regs], eax + + ; Save GDT in case BIOS overwrites it + sgdt [.gdt] + + ; Save IDT + sidt [.idt] + + ; Load BIOS IVT + lidt [.rem_idt] + + ; Save non-scratch GPRs + push ebx + push esi + push edi + push ebp + + ; Jump to real mode + jmp 0x08:.bits16 + .bits16: + bits 16 + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov eax, cr0 + and al, 0xfe + mov cr0, eax + jmp 0x00:.cszero + .cszero: + xor ax, ax + mov ss, ax + + ; Load in_regs + mov dword [ss:.esp], esp + mov esp, dword [ss:.in_regs] + pop gs + pop fs + pop es + pop ds + popfd + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + mov esp, dword [ss:.esp] + + sti + + ; Indirect interrupt call + db 0xcd + .int_no: + db 0 + + cli + + ; Load out_regs + mov dword [ss:.esp], esp + mov esp, dword [ss:.out_regs] + lea esp, [esp + 10*4] + push eax + push ebx + push ecx + push edx + push esi + push edi + push ebp + pushfd + push ds + push es + push fs + push gs + mov esp, dword [ss:.esp] + + ; Restore GDT + o32 lgdt [ss:.gdt] + + ; Restore IDT + o32 lidt [ss:.idt] + + ; Jump back to pmode + mov eax, cr0 + or al, 1 + mov cr0, eax + jmp 0x18:.bits32 + .bits32: + bits 32 + mov ax, 0x20 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + ; Restore non-scratch GPRs + pop ebp + pop edi + pop esi + pop ebx + + ; Exit + ret + +align 16 +.esp: + dd 0 +.out_regs: + dd 0 +.in_regs: + dd 0 +.gdt: + dq 0 +.idt: + dq 0 +.rem_idt: + dw 0x3ff + dd 0 |