This is my first post on the forum, but I've been tinkering with OS development on and off for some years now --- often making use of the many great resources on this site. So thank you all for that
So, on to my problem, and the reason I finally post on this forum. I'm currently trying to make a simple 64-bit kernel, and using EFI to boot it. I got booting and "hello world"-ing to work, but I am stuck with a bug that I just can't explain. It is related to getting the memory map using GetMemoryMap, and later exiting the boot services. Before I explain any further, let me post my code.
Code: Select all
typedef struct {
EFI_MEMORY_DESCRIPTOR* map_ptr;
UINTN map_size;
UINTN map_key;
UINTN desc_size;
UINT32 desc_ver;
} memory_map_t;
void memory_init(EFI_HANDLE img_handle, EFI_SYSTEM_TABLE* sys_table) {
EFI_STATUS status;
volatile memory_map_t memory_map = {0};
EFI_BOOT_SERVICES* boot_srv = sys_table->BootServices;
EFI_RUNTIME_SERVICES* run_srv = sys_table->RuntimeServices;
status = boot_srv->GetMemoryMap(
(UINTN*)&memory_map.map_size,
NULL, NULL, NULL, NULL
);
if(status != EFI_BUFFER_TOO_SMALL) {
Print(L"Error (%d) obtaining memory map.\r\n", status);
return;
}
while(1) {
Print(L"Trying to allocate size 0x%X\r\n", memory_map.map_size);
status = boot_srv->AllocatePool(
EfiLoaderData,
memory_map.map_size,
(void**)&memory_map.map_ptr
);
if(status != EFI_SUCCESS) {
Print(L"Error (%d) allocating memory.\r\n", status);
return;
}
status = boot_srv->GetMemoryMap(
(UINTN*)&memory_map.map_size,
(EFI_MEMORY_DESCRIPTOR*)&memory_map.map_ptr,
(UINTN*)&memory_map.map_key,
(UINTN*)&memory_map.desc_size,
(UINT32*)&memory_map.desc_ver
);
Print(L"Map key: 0x%X: %d\r\n", &memory_map.map_key, memory_map.map_key);
if(status == EFI_BUFFER_TOO_SMALL) {
status = boot_srv->FreePool((void*)memory_map.map_ptr);
if(status != EFI_SUCCESS) {
Print(L"Error (%d) freeing memory: 0x%X\r\n", status, memory_map.map_ptr);
return;
}
Print(L"Too small! Freed.\r\n");
} else if(status != EFI_SUCCESS) {
Print(L"Error (%d) obtaining memory map.\r\n", status);
return;
} else {
Print(L"Big enough --- Location 0x%X\r\n", memory_map.map_ptr);
break;
}
}
Print(L"Obtained memory map:\r\n");
Print(L" map_size 0x%X\r\n", memory_map.map_size);
Print(L" map_ptr 0x%X\r\n", memory_map.map_ptr);
Print(L" map_key 0x%X\r\n", memory_map.map_key);
Print(L" desc_size 0x%X\r\n", memory_map.desc_size);
Print(L" desc_ver 0x%X\r\n\n", memory_map.desc_ver);
status = boot_srv->ExitBootServices(img_handle, memory_map.map_key);
if(status != EFI_SUCCESS) {
Print(L"Error (%d) exiting boot services\r\n", status);
return;
} else {
Print(L"Success\r\n");
}
run_srv->SetVirtualAddressMap(
memory_map.map_size,
memory_map.desc_size,
memory_map.desc_ver,
memory_map.map_ptr
);
Print(L"Exited boot services\r\n");
}
As can be seen, I have created a structure to hold the memory map data. I create an instance of it on the stack at the beginning of the function, before I use GetMemoryMap to obtain the required size of the memory map. I allocate the size and check if it is big enough, if it isn't, I try again until it can fit the memory map (each iteration freeing the unused memory).
However, it seems that the order of the variable definitions ends up having an effect on the program (even though it in theory shouldn't). If I move the declaration of the memory_map variable down two lines (to just below the definition of boot_srv and run_srv), my program crashes when calling ExitBootServices, in other words, it prints neither a success nor an error message. My first thought was that the compiler might be doing something to "optimize" my code, but the optimization should be off (default is -O0), and I have marked my memory_map variably as volatile.
I am really intrigued by this behaviour, but I can't for the life of me work out why the code behaves that way. Anyone have any ideas or experience with similar behaviour?
All that my efi_main function does is set the graphics mode and call memory_init. I have added both my efi_main and makefile below for completeness.
Code: Select all
EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) {
EFI_STATUS status;
InitializeLib(ImageHandle, SystemTable);
SIMPLE_TEXT_OUTPUT_INTERFACE* TextOut = SystemTable->ConOut;
TextOut->Reset(TextOut, 0);
UINTN Colums;
UINTN Rows;
int i = 100;
do {
i--;
status = TextOut->QueryMode(TextOut, i, &Colums, &Rows);
} while(status == EFI_UNSUPPORTED);
status = TextOut->SetMode(TextOut, i);
TextOut->SetAttribute(TextOut, EFI_WHITE);
TextOut->OutputString(TextOut, L"Welcome to ");
TextOut->SetAttribute(TextOut, EFI_LIGHTCYAN);
TextOut->OutputString(TextOut, L"MatteOS");
TextOut->SetAttribute(TextOut, EFI_WHITE);
TextOut->OutputString(TextOut, L" UEFI!\r\n\n");
Print(L"TTY size: %dx%d\r\n\n", Colums, Rows);
memory_init(ImageHandle, SystemTable);
while(1);
return EFI_SUCCESS;
}
Code: Select all
SRC_DIR = src
OBJ_DIR = obj
OUT_DIR = out
STATIC = $(OBJ_DIR)/static
DYNAMIC = $(OBJ_DIR)/dynamic
INC = $(shell find . -name *.h -printf '%P\n')
SRC = $(shell find . -name *.c -printf '%P\n')
OBJ = $(patsubst $(SRC_DIR)/%.c, $(STATIC)/%.o, $(SRC))
OUT_KERN = $(OUT_DIR)/kern.efi
OUT_IMG = $(OUT_DIR)/disk.img
EFI_INC = -I/usr/include/efi/ -I/usr/include/efi/x86_64/ \
-I/usr/include/efi/protocol/
CFLAGS = -fshort-wchar -Wall -fpic -nostdlib -fno-builtin -nodefaultlibs \
-ffreestanding -fno-exceptions -fno-stack-protector \
-mno-red-zone -DHAVE_USE_MS_ABI -DEFI_FUNCTION_WRAPPER
EFI_LIB = -L /usr/lib/gnuefi -L /usr/lib /usr/lib/crt0-efi-x86_64.o
LDFLAGS = -nostdlib -znocombreloc -T /usr/lib/elf_x86_64_efi.lds -shared \
-Bsymbolic $(EFI_LIB)
LIBS = -lefi -lgnuefi
OBJFLAGS = -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela \
-j .reloc --target=efi-app-x86_64
QEMUFLAGS = -cpu qemu64 -bios OVMF.fd -enable-kvm -m 64 -device VGA -net none \
-display gtk -ctrl-grab
all: dirs $(OUT_KERN)
dirs:
mkdir -p $(OBJ_DIR) $(OUT_DIR)
clean:
rm -rf $(OBJ_DIR) $(OUT_DIR)
$(OUT_KERN): $(DYNAMIC)/kern.so
objcopy $(OBJFLAGS) $< $@
$(DYNAMIC)/kern.so: $(OBJ)
mkdir -p $(shell dirname $@)
ld $(LDFLAGS) $^ -o $@ $(LIBS)
$(OBJ): $(STATIC)/%.o: $(SRC_DIR)/%.c $(INC)
mkdir -p $(shell dirname $@)
clang $(EFI_INC) $(CFLAGS) -c -o $@ $<
run: all $(OUT_IMG)
qemu-system-x86_64 -drive file=$(OUT_IMG),if=ide $(QEMUFLAGS)
$(OUT_IMG): $(OUT_KERN)
dd if=/dev/zero of=$@ bs=4k count=16k
mkfs.fat $@
mmd -i $@ ::/EFI
mmd -i $@ ::/EFI/BOOT
mcopy -i $@ $^ ::/EFI/BOOT/BOOTX64.efi