Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Palloc #54

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 47 additions & 0 deletions docs/boot/detecting_memory.md
Original file line number Diff line number Diff line change
@@ -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
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh my goodness you forgot a ; !!!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😱

```

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))
7 changes: 4 additions & 3 deletions src/boot/boot_sect.asm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
dw 0xaa55 ;magic boot sector number
60 changes: 60 additions & 0 deletions src/boot/detect_mem.asm
Original file line number Diff line number Diff line change
@@ -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
74 changes: 74 additions & 0 deletions src/lib/stdlib/palloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "palloc.h"

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#include <stdint.h>

#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) {
//
// }
// }
// }
20 changes: 20 additions & 0 deletions src/lib/stdlib/palloc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <stdint.h>

// 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();
2 changes: 2 additions & 0 deletions src/os/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Loading