summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarvin Borner2021-07-10 15:51:54 +0200
committerMarvin Borner2021-07-10 15:51:54 +0200
commitad56eb28f0614db4b7656ade390f1c79b446cbc9 (patch)
tree7d42ab3c9f36c188dbf54a25b1db1c3db5acb1bd
parent176b6eb808f4d02d871c30f6ec19daa179f374d1 (diff)
A20, E820h, Real Mode Emulation
-rw-r--r--makefile4
-rw-r--r--src/loader/a20.c59
-rw-r--r--src/loader/cpu.c17
-rw-r--r--src/loader/ent.asm33
-rw-r--r--src/loader/gdt.c49
-rw-r--r--src/loader/impl/mb1.c21
-rw-r--r--src/loader/inc/a20.h10
-rw-r--r--src/loader/inc/cpu.h3
-rw-r--r--src/loader/inc/def.h1
-rw-r--r--src/loader/inc/gdt.h25
-rw-r--r--src/loader/inc/int.h2
-rw-r--r--src/loader/inc/mem.h32
-rw-r--r--src/loader/inc/rem.h29
-rw-r--r--src/loader/int.c23
-rw-r--r--src/loader/link.ld14
-rw-r--r--src/loader/main.c4
-rw-r--r--src/loader/mem.c125
-rw-r--r--src/loader/rem.asm139
18 files changed, 570 insertions, 20 deletions
diff --git a/makefile b/makefile
index 1622d7b..6619bb8 100644
--- a/makefile
+++ b/makefile
@@ -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 &map;
+}
+
+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