From 4fa82854dd00eab7440dcfff29612bf9c2fef819 Mon Sep 17 00:00:00 2001 From: vmttmv Date: Thu, 15 Jan 2026 21:39:53 +0200 Subject: [PATCH] BL: Query and store e820 memory map - bl stage 1: move gdt+pm setup to stage 2 (avoids mode switching) - bl stage 2: at entry query e820 table from bios, then setup gdt+pm - kernel/memmap: pad map entry to 24 bytes, as exported by bl - kernel/memmap: map copy (gbowne1) - Makefile: facilitate placing the memory map at a known location - Update bl docs --- Makefile | 9 +++-- bootloader/README.md | 8 ++--- bootloader/stage1.asm | 31 ++--------------- bootloader/stage2.asm | 79 ++++++++++++++++++++++++++++++++++++++++--- kernel/memmap.c | 23 ++++++------- kernel/memmap.h | 1 + 6 files changed, 98 insertions(+), 53 deletions(-) diff --git a/Makefile b/Makefile index b1149d0..06368b5 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,13 @@ OBJCOPY = i386-elf-objcopy BUILD_DIR = build CROSS_DIR = cross DISK_IMG = $(BUILD_DIR)/disk.img + +STAGE2_ADDR = 0x7e00 STAGE2_SIZE = 2048 +# Place the memory map (e820) past stage2 bl in memory +MEMMAP_BASE = $(shell echo $$(($(STAGE2_ADDR) + $(STAGE2_SIZE)))) + KERNEL_C_SRC = $(wildcard kernel/*.c) KERNEL_ASM_SRC = $(wildcard kernel/*.asm) KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC)) @@ -29,7 +34,7 @@ stage1: $(BUILD_DIR) # NOTE: Stage2 final size should be checked against `$(STAGE2_SIZE)` by the build system to avoid an overflow. # Alternatively, convey the final stage2 size through other means to stage1. stage2: $(BUILD_DIR) - $(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm + $(AS) $(ASFLAGS) -DMEMMAP_BASE=$(MEMMAP_BASE) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c $(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o $(OBJCOPY) -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin @@ -39,7 +44,7 @@ $(BUILD_DIR)/asm_%.o: kernel/%.asm $(AS) $(ASFLAGS) -o $@ $< $(BUILD_DIR)/%.o: kernel/%.c - $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $< + $(CC) -DMEMMAP_BASE=$(MEMMAP_BASE) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $< $(BUILD_DIR)/klibc/%.o: klibc/src/%.c $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $< diff --git a/bootloader/README.md b/bootloader/README.md index 384754a..283f257 100644 --- a/bootloader/README.md +++ b/bootloader/README.md @@ -11,16 +11,16 @@ Bootloader documentation for ClassicOS ## Stage 1 (`stage1.asm`) -Responsible for loading the second stage using BIOS routines, and switching to protected mode. - - Queries CHS parameters from BIOS - Loads the second stage bootloader (2048 B) to `0x7c00` -- Sets up a GDT with descriptor entries for code and data both covering the whole 32-bit address space - Enables A20 -- Set CR0.PE (enable protected mode) and jump to stage 2 +- Jumps to stage2 ## Stage 2 (`stage2.asm, stage2_load.c`) +- Read and store E820 memory map from BIOS +- Sets up a GDT with descriptor entries for code and data both covering the whole 32-bit address space +- Set CR0.PE (enable protected mode) - Set up segment registers - Load the kernel ELF header - Parse the program headers, and load all `PT_LOAD` segments from disk diff --git a/bootloader/stage1.asm b/bootloader/stage1.asm index 949d30b..5c9a4c6 100644 --- a/bootloader/stage1.asm +++ b/bootloader/stage1.asm @@ -40,11 +40,8 @@ _start: call enable_a20 jc a20_error ; Jump if A20 enable fails - ; Setup Global Descriptor Table - call setup_gdt - - ; Switch to protected mode and jump to second stage at 0x08:0x7E00 - call switch_to_pm + ; Jump to s2 + jmp 0x7e00 disk_error: mov si, disk_error_msg @@ -241,30 +238,6 @@ check_a20: clc ; Clear carry flag to indicate success ret -; ---------------------------------------------------------------- -gdt_start: - dq 0x0000000000000000 ; Null descriptor - dq 0x00CF9A000000FFFF ; 32-bit code segment (selector 0x08) - dq 0x00CF92000000FFFF ; 32-bit data segment (selector 0x10) - dq 0x00009A000000FFFF ; 16-bit code segment for real mode (selector 0x18) - -gdt_descriptor: - dw gdt_end - gdt_start - 1 - dd gdt_start -gdt_end: - -setup_gdt: - lgdt [gdt_descriptor] - ret - -; ---------------------------------------------------------------- -switch_to_pm: - cli - mov eax, cr0 - or eax, 1 - mov cr0, eax - jmp 0x08:0x7E00 ; jump to S2 - ; ---------------------------------------------------------------- print_string_16: .loop: diff --git a/bootloader/stage2.asm b/bootloader/stage2.asm index e11f67b..d9b7bfd 100644 --- a/bootloader/stage2.asm +++ b/bootloader/stage2.asm @@ -1,10 +1,80 @@ -[BITS 32] global _start -global ata_lba_read - extern load_kernel +%define e820_magic 0x534d4150 ; "SMAP" +%define e820_entry_size 24 +%define e820_max_entries 128 + +; ---------------------------------------------------------------- +; Real mode +; ---------------------------------------------------------------- +[BITS 16] _start: + call read_e820 + call setup_gdt + call switch_to_pm + +read_e820: + xor ebx, ebx + mov es, bx + mov di, MEMMAP_BASE+4 ; ES=0 DI=MEMMAP_BASE+4 + xor bp, bp ; Keeping count in bp + +.e820_loop: + mov eax, 0xe820 + mov ecx, e820_entry_size + mov edx, e820_magic + int 0x15 + jc .done ; Error? + + cmp eax, e820_magic ; Verify "SMAP" + jne .done + + test ecx, ecx ; Skip 0-sized entries + jz .skip + + add di, e820_entry_size ; Advance write addr + inc bp ; Increment count + + cmp bp, e820_max_entries ; Stop if we're at capacity + jae .done +.skip: + test ebx, ebx + jne .e820_loop +.done: + mov [MEMMAP_BASE], bp ; Store count + ret + +setup_gdt: + lgdt [gdt_descriptor] + ret + +switch_to_pm: + cli + mov eax, cr0 + or eax, 1 + mov cr0, eax + jmp 0x08:pm_entry + +e820_count: + dw 0 + +gdt_start: + dq 0x0000000000000000 ; Null descriptor + dq 0x00CF9A000000FFFF ; 32-bit code segment (selector 0x08) + dq 0x00CF92000000FFFF ; 32-bit data segment (selector 0x10) + dq 0x00009A000000FFFF ; 16-bit code segment for real mode (selector 0x18) +gdt_descriptor: + dw gdt_end - gdt_start - 1 + dd gdt_start +gdt_end: + +; ---------------------------------------------------------------- +; Protected mode +; ---------------------------------------------------------------- +[BITS 32] + +pm_entry: ; Set up segments ; Data segments mov ax, 0x10 @@ -18,9 +88,8 @@ _start: mov ax, 0x08 mov cs, ax - ; Stack (must be identity-mapped) + ; Stack mov esp, 0x90000 call load_kernel - jmp eax diff --git a/kernel/memmap.c b/kernel/memmap.c index 53cbacf..142078d 100644 --- a/kernel/memmap.c +++ b/kernel/memmap.c @@ -1,21 +1,18 @@ #include "memmap.h" +#define BOOTLOADER_MEMMAP_COUNT_ADDR MEMMAP_BASE +#define BOOTLOADER_MEMMAP_ADDR (MEMMAP_BASE + 4) + uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) { + // Read the number of entries found by the bootloader + uint32_t entries_found = *(uint32_t*)BOOTLOADER_MEMMAP_COUNT_ADDR; + memory_map_entry_t *bios_data = (memory_map_entry_t*)BOOTLOADER_MEMMAP_ADDR; + uint32_t count = 0; - - if (max_entries >= 1) { - map[count].base_addr = 0x00000000; - map[count].length = 0x0009FC00; - map[count].type = 1; - count++; - } - - if (max_entries >= 2) { - map[count].base_addr = 0x00100000; - map[count].length = 0x1FF00000; - map[count].type = 1; + while (count < entries_found && count < max_entries) { + map[count] = bios_data[count]; count++; } return count; -} \ No newline at end of file +} diff --git a/kernel/memmap.h b/kernel/memmap.h index 8731df0..a257436 100644 --- a/kernel/memmap.h +++ b/kernel/memmap.h @@ -7,6 +7,7 @@ typedef struct { uint64_t base_addr; uint64_t length; uint32_t type; + uint32_t ext; } __attribute__((packed)) memory_map_entry_t; uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries);