2 Commits

Author SHA1 Message Date
8cf7fa7f31 Create pmm.c
Add implementation for the physical memory manager pmm
2026-01-28 10:21:46 -08:00
64d69d505f Create pmm.h 2026-01-28 10:14:01 -08:00
13 changed files with 246 additions and 492 deletions

View File

@@ -8,13 +8,8 @@ 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))
@@ -34,7 +29,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) -DMEMMAP_BASE=$(MEMMAP_BASE) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm
$(AS) $(ASFLAGS) -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
@@ -44,7 +39,7 @@ $(BUILD_DIR)/asm_%.o: kernel/%.asm
$(AS) $(ASFLAGS) -o $@ $<
$(BUILD_DIR)/%.o: kernel/%.c
$(CC) -DMEMMAP_BASE=$(MEMMAP_BASE) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $<
$(CC) -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 $@ $<

View File

@@ -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
- Jumps to stage2
- Set CR0.PE (enable protected mode) and jump to stage 2
## 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

View File

@@ -40,8 +40,11 @@ _start:
call enable_a20
jc a20_error ; Jump if A20 enable fails
; Jump to s2
jmp 0x7e00
; Setup Global Descriptor Table
call setup_gdt
; Switch to protected mode and jump to second stage at 0x08:0x7E00
call switch_to_pm
disk_error:
mov si, disk_error_msg
@@ -238,6 +241,30 @@ 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:

View File

@@ -1,80 +1,10 @@
[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
@@ -88,8 +18,9 @@ pm_entry:
mov ax, 0x08
mov cs, ax
; Stack
; Stack (must be identity-mapped)
mov esp, 0x90000
call load_kernel
jmp eax

View File

@@ -1,16 +1,19 @@
#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;
while (count < entries_found && count < max_entries) {
map[count] = bios_data[count];
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;
count++;
}

View File

@@ -7,7 +7,6 @@ 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);

View File

@@ -1,109 +0,0 @@
#include "pci.h"
#include "io.h"
/* --- Configuration Access Functions --- */
uint32_t pci_config_read_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t address = (uint32_t)((uint32_t)1 << 31) |
((uint32_t)bus << 16) |
((uint32_t)slot << 11) |
((uint32_t)func << 8) |
(offset & 0xFC);
outl(PCI_CONFIG_ADDRESS, address);
return inl(PCI_CONFIG_DATA);
}
void pci_config_write_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t data) {
uint32_t address = (uint32_t)((uint32_t)1 << 31) |
((uint32_t)bus << 16) |
((uint32_t)slot << 11) |
((uint32_t)func << 8) |
(offset & 0xFC);
outl(PCI_CONFIG_ADDRESS, address);
outl(PCI_CONFIG_DATA, data);
}
/* To read a word or byte, we read the Dword and shift/mask */
uint16_t pci_config_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t dword = pci_config_read_dword(bus, slot, func, offset);
return (uint16_t)((dword >> ((offset & 2) * 8)) & 0xFFFF);
}
uint8_t pci_config_read_byte(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t dword = pci_config_read_dword(bus, slot, func, offset);
return (uint8_t)((dword >> ((offset & 3) * 8)) & 0xFF);
}
/* --- BAR Decoding Logic --- */
pci_bar_t pci_get_bar(uint8_t bus, uint8_t slot, uint8_t func, uint8_t bar_index) {
pci_bar_t bar = {0};
uint8_t offset = PCI_REG_BAR0 + (bar_index * 4);
uint32_t initial_val = pci_config_read_dword(bus, slot, func, offset);
// The Size Masking Trick
pci_config_write_dword(bus, slot, func, offset, 0xFFFFFFFF);
uint32_t mask = pci_config_read_dword(bus, slot, func, offset);
pci_config_write_dword(bus, slot, func, offset, initial_val); // Restore
if (initial_val & 0x1) {
// I/O Space BAR
bar.is_io = true;
bar.base_address = initial_val & 0xFFFFFFFC;
bar.size = ~(mask & 0xFFFFFFFC) + 1;
} else {
// Memory Space BAR
bar.is_io = false;
bar.base_address = initial_val & 0xFFFFFFF0;
bar.is_prefetchable = (initial_val & 0x8) != 0;
bar.size = ~(mask & 0xFFFFFFF0) + 1;
}
return bar;
}
/* --- Enumeration and Discovery --- */
void pci_check_function(uint8_t bus, uint8_t slot, uint8_t func) {
uint16_t vendor_id = pci_config_read_word(bus, slot, func, PCI_REG_VENDOR_ID);
if (vendor_id == 0xFFFF) return;
uint16_t device_id = pci_config_read_word(bus, slot, func, PCI_REG_DEVICE_ID);
uint8_t class_code = pci_config_read_byte(bus, slot, func, PCI_REG_CLASS);
/* Optional: Set Master Latency Timer if it is 0.
A value of 32 (0x20) or 64 (0x40) is typical.
*/
uint8_t latency = pci_config_read_byte(bus, slot, func, PCI_REG_LATENCY_TIMER);
if (latency == 0) {
// pci_config_write_byte would be needed here, or write a dword with the byte modified
uint32_t reg_0c = pci_config_read_dword(bus, slot, func, 0x0C);
reg_0c |= (0x20 << 8); // Set latency to 32
pci_config_write_dword(bus, slot, func, 0x0C, reg_0c);
}
// Replace with your kernel's print/logging function
// printf("Found PCI Device: %x:%x Class: %x at %d:%d:%d\n", vendor_id, device_id, class_code, bus, slot, func);
}
void pci_init(void) {
for (uint16_t bus = 0; bus < 256; bus++) {
for (uint8_t slot = 0; slot < 32; slot++) {
// Check Function 0 first
uint16_t vendor = pci_config_read_word(bus, slot, 0, PCI_REG_VENDOR_ID);
if (vendor == 0xFFFF) continue;
pci_check_function(bus, slot, 0);
// Check if this is a multi-function device
uint8_t header_type = pci_config_read_byte(bus, slot, 0, PCI_REG_HEADER_TYPE);
if (header_type & 0x80) {
// Check functions 1-7
for (uint8_t func = 1; func < 8; func++) {
pci_check_function(bus, slot, func);
}
}
}
}
}

View File

@@ -1,60 +0,0 @@
#ifndef PCI_H
#define PCI_H
#include <stdint.h>
#include <stdbool.h>
/* I/O Ports for PCI Configuration Mechanism #1 */
#define PCI_CONFIG_ADDRESS 0xCF8
#define PCI_CONFIG_DATA 0xCFC
/* Common PCI Configuration Register Offsets */
#define PCI_REG_VENDOR_ID 0x00
#define PCI_REG_DEVICE_ID 0x02
#define PCI_REG_COMMAND 0x04
#define PCI_REG_STATUS 0x06
#define PCI_REG_REVISION_ID 0x08
#define PCI_REG_PROG_IF 0x09
#define PCI_REG_SUBCLASS 0x0A
#define PCI_REG_CLASS 0x0B
#define PCI_REG_CACHE_LINE_SIZE 0x0C
#define PCI_REG_LATENCY_TIMER 0x0D
#define PCI_REG_HEADER_TYPE 0x0E
#define PCI_REG_BIST 0x0F
#define PCI_REG_BAR0 0x10
#define PCI_REG_BAR1 0x14
#define PCI_REG_BAR2 0x18
#define PCI_REG_BAR3 0x1C
#define PCI_REG_BAR4 0x20
#define PCI_REG_BAR5 0x24
#define PCI_REG_INTERRUPT_LINE 0x3C
typedef struct {
uint32_t base_address;
uint32_t size;
bool is_io;
bool is_prefetchable; // Only for Memory BARs
} pci_bar_t;
typedef struct {
uint8_t bus;
uint8_t device;
uint8_t function;
uint16_t vendor_id;
uint16_t device_id;
uint8_t class_code;
uint8_t subclass;
uint8_t interrupt_line;
} pci_dev_t;
/* Function Prototypes */
uint32_t pci_config_read_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
void pci_config_write_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t data);
uint16_t pci_config_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
uint8_t pci_config_read_byte(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
pci_bar_t pci_get_bar(uint8_t bus, uint8_t slot, uint8_t func, uint8_t bar_index);
void pci_init(void);
#endif

21
kernel/pmm.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef PMM_H
#define PMM_H
#include <stdint.h>
#include <stddef.h>
#include "memmap.h"
#include "paging.h" // For PAGE_SIZE
#define BLOCKS_PER_BYTE 8
void pmm_init(memory_map_entry_t* mmap, uint32_t mmap_size, uintptr_t bitmap_addr);
void pmm_mark_used(uintptr_t addr);
void pmm_mark_free(uintptr_t addr);
void* pmm_alloc_block();
void pmm_free_block(void* addr);
uint32_t pmm_get_used_block_count();
uint32_t pmm_get_free_block_count();
#endif

View File

@@ -1,107 +0,0 @@
#include "ps2.h"
#include "io.h"
/* --- Controller Synchronization --- */
// Wait until the controller is ready to receive a byte
static void ps2_wait_write() {
while (inb(PS2_STATUS_REG) & PS2_STATUS_INPUT);
}
// Wait until the controller has a byte for us to read
static void ps2_wait_read() {
while (!(inb(PS2_STATUS_REG) & PS2_STATUS_OUTPUT));
}
/* --- Initialization --- */
void ps2_write_device(uint8_t command) {
ps2_wait_write();
outb(PS2_DATA_PORT, command);
}
void ps2_write_mouse(uint8_t data) {
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_WRITE_MOUSE); // "Next byte goes to mouse"
ps2_wait_write();
outb(PS2_DATA_PORT, data);
}
void ps2_init(void) {
// 1. Disable Devices
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_DISABLE_KB);
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_DISABLE_MS);
// 2. Flush Output Buffer
while (inb(PS2_STATUS_REG) & PS2_STATUS_OUTPUT) {
inb(PS2_DATA_PORT);
}
// 3. Set Controller Configuration Byte
// Bit 0: KB Interrupt, Bit 1: Mouse Interrupt, Bit 6: Translation
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_READ_CONFIG);
ps2_wait_read();
uint8_t status = inb(PS2_DATA_PORT);
status |= (1 << 0) | (1 << 1); // Enable IRQ 1 and IRQ 12
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_WRITE_CONFIG);
ps2_wait_write();
outb(PS2_DATA_PORT, status);
// 4. Enable Devices
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_ENABLE_KB);
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_ENABLE_MS);
// 5. Initialize Mouse (The mouse won't send IRQs until you tell it to)
ps2_write_mouse(MOUSE_CMD_SET_DEFAULTS);
ps2_wait_read(); inb(PS2_DATA_PORT); // Read ACK (0xFA)
ps2_write_mouse(MOUSE_CMD_ENABLE_SCAN);
ps2_wait_read(); inb(PS2_DATA_PORT); // Read ACK (0xFA)
}
/* --- IRQ Handlers --- */
// Called from IRQ 1 (Keyboard)
void ps2_keyboard_handler(void) {
uint8_t scancode = inb(PS2_DATA_PORT);
// Process scancode (e.g., put it into a circular buffer)
}
// Called from IRQ 12 (Mouse)
static uint8_t mouse_cycle = 0;
static uint8_t mouse_bytes[3];
void ps2_mouse_handler(void) {
uint8_t status = inb(PS2_STATUS_REG);
// Ensure this is actually mouse data
if (!(status & PS2_STATUS_MOUSE)) return;
mouse_bytes[mouse_cycle++] = inb(PS2_DATA_PORT);
if (mouse_cycle == 3) {
mouse_cycle = 0;
// Byte 0: Flags (Buttons, Signs)
// Byte 1: X Delta
// Byte 2: Y Delta
mouse_state_t state;
state.left_button = (mouse_bytes[0] & 0x01);
state.right_button = (mouse_bytes[0] & 0x02);
state.middle_button = (mouse_bytes[0] & 0x04);
// Handle negative deltas (signed 9-bit logic)
state.x_delta = (int8_t)mouse_bytes[1];
state.y_delta = (int8_t)mouse_bytes[2];
// Update your kernel's internal mouse position here
}
}

View File

@@ -1,45 +0,0 @@
#ifndef PS2_H
#define PS2_H
#include <stdint.h>
#include <stdbool.h>
/* I/O Ports */
#define PS2_DATA_PORT 0x60
#define PS2_STATUS_REG 0x64
#define PS2_COMMAND_REG 0x64
/* Status Register Bits */
#define PS2_STATUS_OUTPUT 0x01 // 1 = Data ready to be read
#define PS2_STATUS_INPUT 0x02 // 1 = Controller busy, don't write yet
#define PS2_STATUS_SYS 0x04 // System flag
#define PS2_STATUS_CMD_DATA 0x08 // 0 = Data written to 0x60, 1 = Cmd to 0x64
#define PS2_STATUS_MOUSE 0x20 // 1 = Mouse data, 0 = Keyboard data
/* Controller Commands */
#define PS2_CMD_READ_CONFIG 0x20
#define PS2_CMD_WRITE_CONFIG 0x60
#define PS2_CMD_DISABLE_MS 0xA7
#define PS2_CMD_ENABLE_MS 0xA8
#define PS2_CMD_DISABLE_KB 0xAD
#define PS2_CMD_ENABLE_KB 0xAE
#define PS2_CMD_WRITE_MOUSE 0xD4
/* Mouse Commands */
#define MOUSE_CMD_SET_DEFAULTS 0xF6
#define MOUSE_CMD_ENABLE_SCAN 0xF4
typedef struct {
int8_t x_delta;
int8_t y_delta;
bool left_button;
bool right_button;
bool middle_button;
} mouse_state_t;
/* Public API */
void ps2_init(void);
void ps2_keyboard_handler(void);
void ps2_mouse_handler(void);
#endif

View File

@@ -25,8 +25,7 @@ void thread_init(void) {
}
// Create a new thread
void thread_create(Thread* thread __attribute__((unused)),
void (*start_routine)(void*), void* arg) {
void thread_create(Thread *thread __attribute__((unused)), void (*start_routine)(void *), void *arg) {
if (num_threads >= MAX_THREADS) {
my_printf("Error: Maximum thread count reached.\n");
return;
@@ -41,13 +40,11 @@ void thread_create(Thread* thread __attribute__((unused)),
thread_table[index].arg = arg;
thread_table[index].stack_size = THREAD_STACK_SIZE;
thread_table[index].stack = (uint32_t*)malloc(THREAD_STACK_SIZE);
thread_table[index].stack_top =
thread_table[index].stack + THREAD_STACK_SIZE / sizeof(uint32_t);
thread_table[index].stack_top = thread_table[index].stack + THREAD_STACK_SIZE / sizeof(uint32_t);
// Initialize the stack (simulate pushing the function's return address)
uint32_t *stack_top = thread_table[index].stack_top;
*(--stack_top) =
(uint32_t)start_routine; // Return address (the thread's entry point)
*(--stack_top) = (uint32_t)start_routine; // Return address (the thread's entry point)
*(--stack_top) = (uint32_t)arg; // Argument to pass to the thread
// Set the thread's state to ready
@@ -63,8 +60,7 @@ void thread_create(Thread* thread __attribute__((unused)),
void thread_yield(void) {
// Find the next thread in a round-robin manner
uint32_t next_thread = (current_thread + 1) % num_threads;
while (next_thread != current_thread &&
thread_table[next_thread].state != THREAD_READY) {
while (next_thread != current_thread && thread_table[next_thread].state != THREAD_READY) {
next_thread = (next_thread + 1) % num_threads;
}
@@ -76,8 +72,7 @@ void thread_yield(void) {
// Exit the current thread
void thread_exit(void) {
thread_table[current_thread].state =
THREAD_BLOCKED; // Mark the thread as blocked (finished)
thread_table[current_thread].state = THREAD_BLOCKED; // Mark the thread as blocked (finished)
free(thread_table[current_thread].stack); // Free the thread's stack
num_threads--; // Decrease thread count
@@ -99,18 +94,18 @@ void scheduler(void) {
}
}
// Context switch to the next thread (assembly would go here to save/load
// registers)
// Context switch to the next thread (assembly would go here to save/load registers)
void context_switch(Thread *next) {
// For simplicity, context switching in this example would involve
// saving/restoring registers. In a real system, you would need to save the
// CPU state (registers) and restore the next thread's state.
// For simplicity, context switching in this example would involve saving/restoring registers.
// In a real system, you would need to save the CPU state (registers) and restore the next thread's state.
my_printf("Switching to thread...\n");
next->start_routine(next->arg); // Start running the next thread
}
// Simple mutex functions (spinlock)
void mutex_init(void) { mutex_locked = 0; }
void mutex_init(void) {
mutex_locked = 0;
}
void mutex_lock(void) {
while (__sync_lock_test_and_set(&mutex_locked, 1)) {
@@ -118,4 +113,6 @@ void mutex_lock(void) {
}
}
void mutex_unlock(void) { __sync_lock_release(&mutex_locked); }
void mutex_unlock(void) {
__sync_lock_release(&mutex_locked);
}

102
pmm.c Normal file
View File

@@ -0,0 +1,102 @@
#include "pmm.h"
#include "memory.h" // For memset
static uint32_t* pmm_bitmap = NULL;
static uint32_t max_blocks = 0;
static uint32_t used_blocks = 0;
// Internal bitmap helpers
static inline void bitmap_set(uint32_t bit) {
pmm_bitmap[bit / 32] |= (1 << (bit % 32));
}
static inline void bitmap_unset(uint32_t bit) {
pmm_bitmap[bit / 32] &= ~(1 << (bit % 32));
}
static inline int bitmap_test(uint32_t bit) {
return pmm_bitmap[bit / 32] & (1 << (bit % 32));
}
void pmm_init(memory_map_entry_t* mmap, uint32_t mmap_size, uintptr_t bitmap_addr) {
// 1. Calculate total memory from mmap to find max_blocks
uint64_t total_mem = 0;
for (uint32_t i = 0; i < mmap_size; i++) {
if (mmap[i].type == 1) { // Available RAM
total_mem = mmap[i].base_addr + mmap[i].length;
}
}
max_blocks = (uint32_t)(total_mem / PAGE_SIZE);
used_blocks = max_blocks;
pmm_bitmap = (uint32_t*)bitmap_addr;
// 2. Default all memory to "Reserved" (1s)
memset(pmm_bitmap, 0xFF, max_blocks / BLOCKS_PER_BYTE);
// 3. Mark only the regions reported as Type 1 (Available) as free (0s)
for (uint32_t i = 0; i < mmap_size; i++) {
if (mmap[i].type == 1) {
uint32_t start_block = (uint32_t)(mmap[i].base_addr / PAGE_SIZE);
uint32_t block_count = (uint32_t)(mmap[i].length / PAGE_SIZE);
for (uint32_t j = 0; j < block_count; j++) {
bitmap_unset(start_block + j);
used_blocks--;
}
}
}
// 4. Critical: Re-protect the first 1MB (BIOS/VGA/Real Mode stuff)
for (uint32_t i = 0; i < (1024 * 1024) / PAGE_SIZE; i++) {
pmm_mark_used(i * PAGE_SIZE);
}
// 5. Critical: Re-protect the Kernel + Page Tables
// Since your paging tables are at 0x200000 and linker at 1MB,
// mark everything from 0x100000 to roughly 0x400000 as used for safety.
for (uint32_t i = 0x100000 / PAGE_SIZE; i < 0x400000 / PAGE_SIZE; i++) {
pmm_mark_used(i * PAGE_SIZE);
}
// 6. Protect the bitmap itself
uint32_t bitmap_size_blocks = (max_blocks / BLOCKS_PER_BYTE) / PAGE_SIZE + 1;
for(uint32_t i = 0; i < bitmap_size_blocks; i++) {
pmm_mark_used(bitmap_addr + (i * PAGE_SIZE));
}
}
void pmm_mark_used(uintptr_t addr) {
uint32_t block = addr / PAGE_SIZE;
if (!bitmap_test(block)) {
bitmap_set(block);
used_blocks++;
}
}
void pmm_mark_free(uintptr_t addr) {
uint32_t block = addr / PAGE_SIZE;
if (bitmap_test(block)) {
bitmap_unset(block);
used_blocks--;
}
}
void* pmm_alloc_block() {
for (uint32_t i = 0; i < max_blocks / 32; i++) {
if (pmm_bitmap[i] != 0xFFFFFFFF) {
for (int j = 0; j < 32; j++) {
if (!bitmap_test(i * 32 + j)) {
uint32_t addr = (i * 32 + j) * PAGE_SIZE;
pmm_mark_used(addr);
return (void*)addr;
}
}
}
}
return NULL; // OOM
}
void pmm_free_block(void* addr) {
pmm_mark_free((uintptr_t)addr);
}