I tried following the Higher Half x86 Bare Bones guide on the OSDev Wiki, using the provided linker script with a few small modifications, but without success. Whenever I use my current linker script (attached below, along with the Makefile and boot.asm), I end up seeing garbage instructions at the memory location where the _start label is supposed to be.
What i see in gdb:
Code: Select all
B+ 0x10000c <_start> mov %eax, 0x164(%esi)
> 0x100012 <_start+6> mov $0x7f98, %ecx
0x100017 <_start+11> mov $0x9000, %edi
0x10001c <loop_start+1> rep movsb %ds:(%esi), %es:(%edi)
0x10001e <loop_start+3> mov $0x9025, %esi
0x100023 <loop_start+8> jmp *%esi
0x100025 <loop_start+10> mov $0x10fa0, %edi
0x10002a <loop_start+15> mov $0x18740, %ecx
0x10002f <loop_start+20> sub %edi, %ecx
0x100031 <loop_start+22> xor %eax, %eaxCode: Select all
ENTRY (_start)
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386:i386)
STACK_SIZE = 0x200000; /* 2 MiB */
HEAP_SIZE = 0x10000000; /* 200 MiB */
SECTIONS
{
/*
* Start physical address (typical for bootloaders like GRUB).
* This is where everything before 0xC0000000 virtual is loaded.
*/
. = 0x00100000;
/*
* The multiboot header and initial entry point must be in the identity-mapped
* physical address space so the bootloader can find them and execute them
* before paging is enabled.
*/
__kernel_start = .;
.multiboot.data : AT(0x00100000)
{
KEEP(*(.multiboot.data))
}
/* Ensure the startup code is placed right after the header, physically. */
.multiboot.text : AT(ADDR(.multiboot.text))
{
KEEP(*(.multiboot.text))
}
/*
* Now we switch to the higher-half virtual address space for the rest
* of the kernel sections. We define the virtual address relative to
* the physical load address using the 0xC0000000 offset.
*/
. += 0xC0000000;
/* Add a symbol that indicates the start address of the kernel in virtual memory. */
.text ALIGN (4K) : AT (ADDR (.text) - 0xC0000000)
{
*(.text)
}
.rodata ALIGN (4K) : AT (ADDR (.rodata) - 0xC0000000)
{
*(.rodata)
}
.data ALIGN (4K) : AT (ADDR (.data) - 0xC0000000)
{
*(.data)
}
.bss ALIGN (4K) : AT (ADDR (.bss) - 0xC0000000)
{
*(COMMON)
*(.bss)
*(.bootstrap_stack)
}
/* -------- Heap Section (1GiB) at end -------- */
.heap ALIGN(4096) (NOLOAD) :
{
heap_start = .;
. = . + HEAP_SIZE;
heap_end = .;
}
/* Add a symbol that indicates the end address of the kernel. */
__kernel_end = .;
/DISCARD/ : {
*(.eh_frame)
}
}Code: Select all
; Declare constants for the multiboot header.
%define ALIGN 1<<0 ; align loaded modules on page boundaries
%define MEMINFO 1<<1 ; provide memory map
%define FLAGS ALIGN | MEMINFO ; this is the Multiboot 'flag' field
%define MAGIC 0x1BADB002 ; 'magic number' lets bootloader find the header
%define CHECKSUM -(MAGIC + FLAGS) ; checksum of above, to prove we are multiboot
; Extern symbols
extern kernel_main
extern __kernel_start
extern __kernel_end
; Declare a multiboot header that marks the program as a kernel.
section .multiboot.data
multiboot_header:
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
; Allocate the initial stack.
section .bootstrap_stack
stack_bottom:
resb 16384 ; 16 KiB
stack_top:
; Preallocate pages used for paging. Don't hard-code addresses and assume they
; are available, as the bootloader might have loaded its multiboot structures or
; modules there. This lets the bootloader know it must avoid the addresses.
section .bss
align 4096
boot_page_directory:
resb 4096
boot_page_table1:
resb 4096
; Further page tables may be required if the kernel grows beyond 3 MiB.
; The kernel entry point.
section .multiboot.text exec
global _start
_start:
; Physical address of boot_page_table1.
; TODO: I recall seeing some assembly that used a macro to do the
; conversions to and from physical. Maybe this should be done in this
; code as well?
mov edi, (boot_page_table1 - 0xC0000000)
; First address to map is address 0.
; TODO: Start at the first kernel page instead. Alternatively map the first
; 1 MiB as it can be generally useful, and there's no need to
; specially map the VGA buffer.
mov esi, 0
; Map 1023 pages. The 1024th will be the VGA text buffer.
mov ecx, 1023
loop_start:
; Only map the kernel.
cmp esi, __kernel_start
jl skip_map
cmp esi, (__kernel_end - 0xC0000000)
jge loop_end
; Map physical address as "present, writable". Note that this maps
; .text and .rodata as writable. Mind security and map them as non-writable.
mov edx, esi
or edx, 0x003
mov [edi], edx
skip_map:
; Size of page is 4096 bytes.
add esi, 4096
; Size of entries in boot_page_table1 is 4 bytes.
add edi, 4
; Loop to the next entry if we haven't finished.
loop loop_start
loop_end:
; Map VGA video memory to 0xC03FF000 as "present, writable".
mov dword [(boot_page_table1 - 0xC0000000) + 1023 * 4], (0x000B8000 | 0x003)
; The page table is used at both page directory entry 0 (virtually from 0x0
; to 0x3FFFFF) (thus identity mapping the kernel) and page directory entry
; 768 (virtually from 0xC0000000 to 0xC03FFFFF) (thus mapping it in the
; higher half). The kernel is identity mapped because enabling paging does
; not change the next instruction, which continues to be physical. The CPU
; would instead page fault if there was no identity mapping.
; Map the page table to both virtual addresses 0x00000000 and 0xC0000000.
mov dword [(boot_page_directory - 0xC0000000) + 0], (boot_page_table1 - 0xC0000000 + 0x003)
mov dword [(boot_page_directory - 0xC0000000) + 768 * 4], (boot_page_table1 - 0xC0000000 + 0x003)
; Set cr3 to the address of the boot_page_directory.
mov ecx, (boot_page_directory - 0xC0000000)
mov cr3, ecx
; Enable paging and the write-protect bit.
mov ecx, cr0
or ecx, 0x80010000
mov cr0, ecx
; Jump to higher half with an absolute jump.
lea ecx, [higher_half_start]
jmp ecx
section .text
higher_half_start:
; At this point, paging is fully set up and enabled.
; Unmap the identity mapping as it is now unnecessary.
mov dword [boot_page_directory + 0], 0
; Reload crc3 to force a TLB flush so the changes to take effect.
mov ecx, cr3
mov cr3, ecx
; Set up the stack.
mov esp, stack_top
; Enter the high-level kernel.
call kernel_main
; Infinite loop if the system has nothing more to do.
cli
halt_loop:
hlt
jmp halt_loop
; -----------------------------------------------------------------------------
; SECTION (note) - Inform the linker that the stack does not need to be executable
; -----------------------------------------------------------------------------
section .note.GNU-stack
Code: Select all
# =========================
# Tools
# =========================
CC = gcc
AS = nasm
LD = ld
QEMU = qemu-system-i386
# =========================
# Directories
# =========================
SOURCE_DIR = src
INCLUDE_DIR = include
BUILD_DIR = build
ISO_DIR = iso
# =========================
# Files
# =========================
KERNEL_ELF = $(BUILD_DIR)/mykernel.elf
KERNEL_BIN = $(BUILD_DIR)/mykernel.bin
ISO_IMAGE = $(BUILD_DIR)/mykernel.iso
VIRTUAL_DISK = $(BUILD_DIR)/vrdisk.img
# =========================
# Flags
# =========================
CFLAGS = -m32 -nostdlib -fno-builtin -fno-exceptions -fno-leading-underscore -I$(INCLUDE_DIR)
ASFLAGS = -f elf32
LDFLAGS = -m elf_i386
# =========================
# Sources (1-level deep)
# =========================
SOURCES_C := $(wildcard $(SOURCE_DIR)/*.c $(SOURCE_DIR)/*/*.c)
SOURCES_S := $(wildcard $(SOURCE_DIR)/*.asm $(SOURCE_DIR)/*/*.asm)
OBJECTS_C := $(patsubst $(SOURCE_DIR)/%.c, $(BUILD_DIR)/%.o, $(SOURCES_C))
OBJECTS_S := $(patsubst $(SOURCE_DIR)/%.asm,$(BUILD_DIR)/%.o, $(SOURCES_S))
OBJECTS := $(OBJECTS_S) $(OBJECTS_C)
# =========================
# Default target
# =========================
all: $(KERNEL_ELF) $(KERNEL_BIN)
# =========================
# Compile C files
# =========================
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
# =========================
# Assemble ASM files
# =========================
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.asm
@mkdir -p $(dir $@)
$(AS) $(ASFLAGS) $< -o $@
# =========================
# Link kernel ELF
# =========================
$(KERNEL_ELF): linker.ld $(OBJECTS)
@mkdir -p $(BUILD_DIR)
$(LD) $(LDFLAGS) -T $< -o $@ $(OBJECTS)
# =========================
# Raw binary
# =========================
$(KERNEL_BIN): $(KERNEL_ELF)
objcopy -O binary $< $@
# =========================
# ISO
# =========================
iso: $(KERNEL_ELF)
@mkdir -p $(ISO_DIR)/boot
cp $(KERNEL_ELF) $(ISO_DIR)/boot/mykernel.elf
grub-mkrescue --output=$(ISO_IMAGE) $(ISO_DIR)
# =========================
# Run
# =========================
run: iso $(VIRTUAL_DISK)
$(QEMU) -m 4G -cdrom $(ISO_IMAGE) -hda $(VIRTUAL_DISK)
# =========================
# Debug
# enter gdb in wsl
# gdb /mnt/c/Users/DorSh/Projects/MyOSv3/build/mykernel.elf
# target remote :1234
# =========================
debug: CFLAGS += -g
debug: iso $(VIRTUAL_DISK)
$(QEMU) -m 4G -cdrom $(ISO_IMAGE) -s -S -hda $(VIRTUAL_DISK)
# =========================
# Virtual disk
# =========================
$(VIRTUAL_DISK):
@mkdir -p $(BUILD_DIR)
qemu-img create $@ 1G
# =========================
# Cleanup
# =========================
clean:
rm -rf $(BUILD_DIR) $(ISO_DIR)/boot/mykernel.elf

