Attempt 2 #2
@@ -7,6 +7,7 @@ add_executable(kernel
|
||||
idt.c
|
||||
isr.c
|
||||
pic.c
|
||||
pmm.c
|
||||
interrupts.S
|
||||
kernel.c
|
||||
)
|
||||
|
||||
12
src/kernel.c
12
src/kernel.c
@@ -5,6 +5,7 @@
|
||||
#include "idt.h"
|
||||
#include "pic.h"
|
||||
#include "port_io.h"
|
||||
#include "pmm.h"
|
||||
|
||||
void offset_print(const char *str)
|
||||
{
|
||||
@@ -47,6 +48,17 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
||||
init_pic();
|
||||
offset_print("PIC initialized\n");
|
||||
|
||||
init_pmm(addr);
|
||||
offset_print("PMM initialized\n");
|
||||
|
||||
phys_addr_t p1 = pmm_alloc_page(PMM_ZONE_NORMAL);
|
||||
offset_print("Allocated page at: ");
|
||||
print_hex(p1);
|
||||
|
||||
phys_addr_t p2 = pmm_alloc_page(PMM_ZONE_DMA);
|
||||
offset_print("Allocated DMA page at: ");
|
||||
print_hex(p2);
|
||||
|
||||
/* Enable interrupts */
|
||||
asm volatile("sti");
|
||||
offset_print("Interrupts enabled\n");
|
||||
|
||||
@@ -4,6 +4,8 @@ SECTIONS
|
||||
{
|
||||
. = 1M;
|
||||
|
||||
_kernel_start = .;
|
||||
|
||||
.text BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
KEEP(*(.multiboot))
|
||||
@@ -25,4 +27,6 @@ SECTIONS
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
_kernel_end = .;
|
||||
}
|
||||
|
||||
146
src/pmm.c
Normal file
146
src/pmm.c
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "pmm.h"
|
||||
#include <multiboot2.h>
|
||||
|
||||
extern uint32_t _kernel_start;
|
||||
extern uint32_t _kernel_end;
|
||||
|
||||
#define MAX_PHYSICAL_MEMORY 0xFFFFFFFF // 4GB
|
||||
#define BITMAP_SIZE (MAX_PHYSICAL_MEMORY / PAGE_SIZE / 32)
|
||||
#define FRAMES_PER_INDEX 32
|
||||
|
||||
static uint32_t memory_bitmap[BITMAP_SIZE];
|
||||
static uint32_t memory_size = 0;
|
||||
// static uint32_t used_frames = 0; // Unused for now
|
||||
|
||||
static void set_frame(phys_addr_t frame_addr) {
|
||||
uint32_t frame = frame_addr / PAGE_SIZE;
|
||||
uint32_t idx = frame / FRAMES_PER_INDEX;
|
||||
uint32_t off = frame % FRAMES_PER_INDEX;
|
||||
memory_bitmap[idx] |= (1 << off);
|
||||
}
|
||||
|
||||
static void clear_frame(phys_addr_t frame_addr) {
|
||||
uint32_t frame = frame_addr / PAGE_SIZE;
|
||||
uint32_t idx = frame / FRAMES_PER_INDEX;
|
||||
uint32_t off = frame % FRAMES_PER_INDEX;
|
||||
memory_bitmap[idx] &= ~(1 << off);
|
||||
}
|
||||
|
||||
static uint32_t first_free_frame(size_t start_frame, size_t end_frame) {
|
||||
for (size_t i = start_frame; i < end_frame; i++) {
|
||||
uint32_t idx = i / FRAMES_PER_INDEX;
|
||||
uint32_t off = i % FRAMES_PER_INDEX;
|
||||
if (!(memory_bitmap[idx] & (1 << off))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
|
||||
void init_pmm(uint32_t multiboot_addr) {
|
||||
struct multiboot_tag *tag;
|
||||
uint32_t addr = multiboot_addr;
|
||||
|
||||
/* Initialize all memory as used (1) initially */
|
||||
for (int i = 0; i < BITMAP_SIZE; i++) {
|
||||
memory_bitmap[i] = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if (addr & 7) {
|
||||
// offset_print("Multiboot alignment error\n"); // Need offset_print here? No, kernel handles prints
|
||||
return; // Alignment issue
|
||||
}
|
||||
|
||||
uint32_t size = *(uint32_t *)addr;
|
||||
|
||||
for (tag = (struct multiboot_tag *)(addr + 8);
|
||||
tag->type != MULTIBOOT_TAG_TYPE_END;
|
||||
tag = (struct multiboot_tag *)((multiboot_uint8_t *)tag + ((tag->size + 7) & ~7))) {
|
||||
|
||||
if (tag->type == MULTIBOOT_TAG_TYPE_BASIC_MEMINFO) {
|
||||
struct multiboot_tag_basic_meminfo *meminfo = (struct multiboot_tag_basic_meminfo *)tag;
|
||||
memory_size = meminfo->mem_upper;
|
||||
} else if (tag->type == MULTIBOOT_TAG_TYPE_MMAP) {
|
||||
multiboot_memory_map_t *mmap;
|
||||
struct multiboot_tag_mmap *tag_mmap = (struct multiboot_tag_mmap *)tag;
|
||||
|
||||
for (mmap = tag_mmap->entries;
|
||||
(multiboot_uint8_t *)mmap < (multiboot_uint8_t *)tag + tag->size;
|
||||
mmap = (multiboot_memory_map_t *)((unsigned long)mmap + tag_mmap->entry_size)) {
|
||||
|
||||
if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) {
|
||||
uint64_t start = mmap->addr;
|
||||
uint64_t len = mmap->len;
|
||||
uint64_t end = start + len;
|
||||
|
||||
if (start % PAGE_SIZE != 0) {
|
||||
start = (start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
for (uint64_t addr_iter = start; addr_iter < end; addr_iter += PAGE_SIZE) {
|
||||
if (addr_iter + PAGE_SIZE <= end && addr_iter < MAX_PHYSICAL_MEMORY) {
|
||||
clear_frame((phys_addr_t)addr_iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t kstart = (uint32_t)&_kernel_start;
|
||||
uint32_t kend = (uint32_t)&_kernel_end;
|
||||
|
||||
kstart &= ~(PAGE_SIZE - 1);
|
||||
kend = (kend + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
||||
|
||||
for (uint32_t i = kstart; i < kend; i += PAGE_SIZE) {
|
||||
set_frame(i);
|
||||
}
|
||||
|
||||
// Mark multiboot structure
|
||||
uint32_t mb_start = multiboot_addr;
|
||||
uint32_t mb_end = multiboot_addr + size;
|
||||
mb_start &= ~(PAGE_SIZE - 1);
|
||||
mb_end = (mb_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
||||
|
||||
for (uint32_t i = mb_start; i < mb_end; i += PAGE_SIZE) {
|
||||
set_frame(i);
|
||||
}
|
||||
|
||||
// Mark first page as used to avoid returning 0 (NULL)
|
||||
set_frame(0);
|
||||
}
|
||||
|
||||
phys_addr_t pmm_alloc_page(pmm_zone_t zone) {
|
||||
uint32_t start_frame = 0;
|
||||
uint32_t end_frame = BITMAP_SIZE * FRAMES_PER_INDEX;
|
||||
uint32_t frame = (uint32_t)-1;
|
||||
|
||||
// 16MB boundary is at 16*1024*1024 / 4096 = 4096 frames
|
||||
uint32_t dma_limit = 4096;
|
||||
|
||||
if (zone == PMM_ZONE_DMA) {
|
||||
end_frame = dma_limit;
|
||||
frame = first_free_frame(start_frame, end_frame);
|
||||
} else {
|
||||
start_frame = dma_limit;
|
||||
frame = first_free_frame(start_frame, end_frame);
|
||||
|
||||
// Fallback to DMA if NORMAL failed
|
||||
if (frame == (uint32_t)-1) {
|
||||
frame = first_free_frame(0, dma_limit);
|
||||
}
|
||||
}
|
||||
|
||||
if (frame != (uint32_t)-1) {
|
||||
phys_addr_t addr = frame * PAGE_SIZE;
|
||||
set_frame(addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
return 0; // OOM
|
||||
}
|
||||
|
||||
void pmm_free_page(phys_addr_t addr) {
|
||||
clear_frame(addr);
|
||||
}
|
||||
36
src/pmm.h
Normal file
36
src/pmm.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef PMM_H
|
||||
#define PMM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
typedef uintptr_t phys_addr_t;
|
||||
|
||||
typedef enum {
|
||||
PMM_ZONE_DMA, // < 16 MB
|
||||
PMM_ZONE_NORMAL, // Any other memory
|
||||
PMM_ZONE_HIGHMEM // > 4 GB (if 64-bit or PAE, but we are 32-bit for now) - actually sticking to DMA and NORMAL is enough for 32-bit typically
|
||||
} pmm_zone_t;
|
||||
|
||||
/*
|
||||
* Initialize the physical memory manager.
|
||||
* @param multiboot_addr Address of the multiboot info structure
|
||||
*/
|
||||
void init_pmm(uint32_t multiboot_addr);
|
||||
|
||||
/*
|
||||
* Allocate a physical page.
|
||||
* @param zone The preferred zone to allocate from.
|
||||
* @return Physical address of the allocated page, or 0 if out of memory.
|
||||
*/
|
||||
phys_addr_t pmm_alloc_page(pmm_zone_t zone);
|
||||
|
||||
/*
|
||||
* Free a physical page.
|
||||
* @param addr Physical address of the page to free.
|
||||
*/
|
||||
void pmm_free_page(phys_addr_t addr);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user