diff --git a/Makefile b/Makefile index e8d4009..f86e94e 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ OBJ_NAMES := src/os/main.o src/os/test.o os_entry.o src/lib/video/VGA_text.o \ src/lib/device/serial.o src/lib/device/ps2.o src/lib/device/keyboard.o \ src/lib/container/ring_buffer.o \ src/lib/stdlib/stdio.o src/lib/stdlib/stdlib.o src/lib/stdlib/string.o \ - src/lib/pit/pit.o + src/lib/pit/pit.o src/lib/stdlib/palloc.o .PHONY: clean qemu test diff --git a/docs/boot/detecting_memory.md b/docs/boot/detecting_memory.md new file mode 100644 index 0000000..1740c2b --- /dev/null +++ b/docs/boot/detecting_memory.md @@ -0,0 +1,47 @@ +# Detecting Memory + +## Preliminaries + +Memory in x86 is standardized up to 1MB. Past that, other BIOS processes use +chunks of memory in unpredictable locations. To maximize the amount +of useable memory in our system, we need to query the BIOS. + +## Detecting Memory + +"Low Memory" is memory below 1MB. BIOS has a couple of functions to detect +these addresses, as a contiguous block, but is limited in the amount of +memory we are able to get, so we choose to omit it entirely. + +"Upper Memory" is memory above 1MB, typically up to just below 4GB. While +memory detection can move past that in 64-bit systems, this amount is +sufficient for our use cases. The BIOS function used to detect upper memory +across most devices is `int 0x15 EAX=0xe820`. Each call to this function +stores a structure in memory that looks as follows: + +``` +struct memChunk { + uint64_t base; // base addr of mem. chunk + uint64_t len; // len of mem. chunk + uint32_t type; // what mem. chunk is used for +} +``` + +From there, our program builds an array of these structures in memory, +for use later in our operating system. A dynamic allocator can read this +array after we transition to 32-bit mode to build a dynamic allocator. + +#### Important Addresses + +- `0x1000`: location of the kernel. +- `0x7c00`: location of boot sector. +- `0x9000`: location of stack at boot. +- `0x90000`: location of stack at kernel entry. + +![Memory Layout Diagram](boot_memory_diagram.png) + +#### Further Reading + +[BIOS](https://wiki.osdev.org/BIOS) +[GDT](https://wiki.osdev.org/Global_Descriptor_Table) +[Bootloader Guide](https://wiki.osdev.org/Rolling_Your_Own_Bootloader) +[BIOS int 0x13](https://wiki.osdev.org/Disk_access_using_the_BIOS_(INT_13h)) diff --git a/src/boot/boot_sect.asm b/src/boot/boot_sect.asm index cc04946..ee97c12 100644 --- a/src/boot/boot_sect.asm +++ b/src/boot/boot_sect.asm @@ -6,13 +6,15 @@ begin: mov [BOOT_DRIVE], dl mov bp, 0x9000 mov sp, bp - jmp load_kernel + jmp detect_mem %include "src/boot/gdt.asm" %include "src/boot/enter_pm.asm" +%include "src/boot/detect_mem.asm" [bits 16] load_kernel: + mov dl, [BOOT_DRIVE] ;reset dl mov ah, 2 ;read BIOS chs mov al, 42 ;sectors to read mov cl, 0x02 ;start at sector 2 @@ -27,11 +29,10 @@ begin_pm: call OS_OFFSET hlt - times 509 - ($ - $$) db 0 ;padding BOOT_DRIVE db 0 ;0x7dfd ;above is data that can always be found at 0x7dfd - n during boot process -dw 0xaa55 ;magic boot sector number \ No newline at end of file +dw 0xaa55 ;magic boot sector number diff --git a/src/boot/detect_mem.asm b/src/boot/detect_mem.asm new file mode 100644 index 0000000..e9361b1 --- /dev/null +++ b/src/boot/detect_mem.asm @@ -0,0 +1,60 @@ +;code below is largely taken from osdev.org, all of which is on the Public Domain +;see https://wiki.osdev.org/Detecting_Memory_(x86) for more details +;for license information, see https://wiki.osdev.org/OSDev_Wiki:License + +num_ent equ 0x8000 ;stores number of entries +struct_base equ 0x8004 ;base ptr of structure + +[bits 16] +detect_mem: + mov di, struct_base + xor ebx, ebx + xor bp, bp + jmp detect_chunk + +[bits 16] +detect_chunk: + mov edx, 0x0534D4150 + mov eax, 0xe820 + mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry + mov ecx, 24 ; ask for 24 bytes + int 0x15 + + cmp eax, edx ; on success, eax must have been reset to "SMAP" + jne failed + + cmp bp, 0 + je short check_first + jmp detect_chunk_2 + +detect_chunk_2: + inc bp + add di, 24 + + cmp bp, 10 + jne detect_chunk + + jmp post_process + +[bits 16] +check_first: + jc short failed ; carry set on first call means "unsupported function" + mov edx, 0x0534D4150 ; Some BIOSes apparently trash this register? + test ebx, ebx ; ebx = 0 implies list is only 1 entry long (worthless) + je short failed + jmp short detect_chunk_2 + +[bits 16] +failed: + stc + ret + +[bits 16] +skip_ent: + test ebx, ebx ;second 0 indicates end of list + jne short post_process + +[bits 16] +post_process: + mov [num_ent], bp + jmp load_kernel diff --git a/src/lib/stdlib/palloc.c b/src/lib/stdlib/palloc.c new file mode 100644 index 0000000..fe15065 --- /dev/null +++ b/src/lib/stdlib/palloc.c @@ -0,0 +1,74 @@ +#include "palloc.h" + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" + +#include + +#define MAX_CHUNKS 100 + +void handle_chunk(Chunk *c); +void sort_chunks(int len, Chunk *array); +void merge_chunks(Chunk *chunks, size_t len); +int compar(const void *ina, const void *inb); + +void init_palloc() { + // last entry is repeat of first + size_t len = *(uint32_t *)MEM_STRUCT_ADDR - 1; + + if (len > MAX_CHUNKS) + len = MAX_CHUNKS; + + Chunk *chunks = (Chunk *)(MEM_STRUCT_ADDR + sizeof(uint32_t)); + + isort(chunks, len, sizeof(Chunk), compar); + // merge_chunks(chunks, len); + // remove_duplicates(chunks, len) +} + +// sorts first by type, then by base address +// +// Types: +// - type 1: usable memory +// - type > 1: unusable (some types are reclaimable, but is left unimplemented +// for now) +int compar(const void *ina, const void *inb) { + Chunk a = *(Chunk *)ina; + Chunk b = *(Chunk *)inb; + + if (a.type < b.type) + return -1; + else if (a.type > b.type) + return 1; + else if (a.base_lower < b.base_lower) + return -1; + else if (a.base_lower == b.base_lower) { + ((Chunk *)inb)->type = + 2; // a bit janky for sure: changes duplicate to unusable type + return -1; + } + return 1; +} + +// void merge_chunks(Chunk *chunks, size_t len) { +// if (len < 2) +// return; +// +// for (int i = 0; i < len - 1; i++) { +// Chunk curr = chunks[i]; +// Chunk next = chunks[i+1]; +// +// if (curr.base_lower + curr.len_lower < next.base_lower) +// continue; +// +// // check if two chunks intersect (after sort) +// else if (curr.base_lower >= next.base_lower) // This should never +// happen????? +// return; +// +// else if (curr.base_lower + curr.len_lower >= next.base_lower) { +// +// } +// } +// } diff --git a/src/lib/stdlib/palloc.h b/src/lib/stdlib/palloc.h new file mode 100644 index 0000000..a2887cf --- /dev/null +++ b/src/lib/stdlib/palloc.h @@ -0,0 +1,20 @@ +#include + +// RAM addr of structure describing useable memory +#define MEM_STRUCT_ADDR 0x8000 + +// for the purposes of our os, which is 32-bit, we +// will rely soley on the lower 32-bits +typedef struct { + uint32_t base_lower; + uint32_t base_upper; + uint32_t len_lower; + uint32_t len_upper; + uint32_t type; + uint32_t attrs; // ACPI v3.0 Extended Attributes bitfield +} Chunk; + +void init_palloc(); + +// Returns a PAGE_SIZE page of contiguous, useable memory +void *palloc(); diff --git a/src/os/main.c b/src/os/main.c index 8172a40..cead3bd 100644 --- a/src/os/main.c +++ b/src/os/main.c @@ -2,12 +2,14 @@ #include "device/serial.h" #include "hard/idt.h" #include "pit/pit.h" +#include "stdlib/palloc.h" #include "test.h" #include "video/VGA_text.h" int os_main() { makeInterruptTable(); init_pit(); + init_palloc(); serialInit(); ps2Init();