mirror of
https://github.com/Plexi09/Nadir-Kernel.git
synced 2026-10-20 15:07:27 +02:00
mm: add bitmap PMM over E820 usable frames
This commit is contained in:
parent
36fa8d7655
commit
d14465458d
2 changed files with 250 additions and 0 deletions
53
include/pmm.h
Normal file
53
include/pmm.h
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* Nadir physical memory manager
|
||||||
|
*
|
||||||
|
* Runs in 64-bit long mode at Ring 0 (CPL 0) under the stage-2 identity
|
||||||
|
* map; physical addresses are used as-is. Called once from kmain with
|
||||||
|
* interrupts disabled (IF=0), before sti, so no IRQ handler can allocate
|
||||||
|
* concurrently. No faults are possible beyond a programming bug; with no
|
||||||
|
* IDT yet at the call point a wild access would triple-fault.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NADIR_PMM_H
|
||||||
|
#define NADIR_PMM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "e820.h"
|
||||||
|
|
||||||
|
/* 4KB frames over the identity-mapped first 1GB: hard capacity limit of
|
||||||
|
* this allocator (256K frames, 32KB bitmap in .bss). Regions above 1GB
|
||||||
|
* reported by E820 are ignored. */
|
||||||
|
#define PMM_FRAME_SIZE 4096U
|
||||||
|
#define PMM_MAX_BYTES 0x40000000ULL
|
||||||
|
#define PMM_MAX_FRAMES (PMM_MAX_BYTES / PMM_FRAME_SIZE)
|
||||||
|
|
||||||
|
/* Memory-map globals: filled by the stage-2 entry (copy from the
|
||||||
|
* stage-1 staging buffer) before kmain runs. pmm_init reads them. */
|
||||||
|
extern uint32_t memmap_count;
|
||||||
|
extern struct e820_entry memmap_entries[E820_MAX];
|
||||||
|
|
||||||
|
/* Build the bitmap from the E820 map: type-1 (usable) frames start free,
|
||||||
|
* everything else starts reserved, then the kernel image, page tables,
|
||||||
|
* staging buffer, low 1MB, stack and VGA regions are reserved.
|
||||||
|
* Preconditions: count <= E820_MAX, entries points at count valid
|
||||||
|
* entries, called once with IF=0 before any alloc/free.
|
||||||
|
* Failure modes: none — an empty/corrupt map just leaves 0 free frames. */
|
||||||
|
void pmm_init(uint32_t count, const struct e820_entry *entries);
|
||||||
|
|
||||||
|
/* Hand out one 4KB frame: returns its physical base address, or 0 when
|
||||||
|
* out of memory (frame 0 is always reserved, so 0 is never valid).
|
||||||
|
* Preconditions: pmm_init ran. Failure modes: returns 0 on OOM. */
|
||||||
|
uint64_t pmm_alloc_frame(void);
|
||||||
|
|
||||||
|
/* Return a frame from pmm_alloc_frame to the free pool. Silently ignores
|
||||||
|
* unaligned, out-of-range, or already-free addresses.
|
||||||
|
* Preconditions: pmm_init ran. Failure modes: none (no fault, no panic). */
|
||||||
|
void pmm_free_frame(uint64_t addr);
|
||||||
|
|
||||||
|
/* Live counters for stats printouts.
|
||||||
|
* Preconditions: pmm_init ran (0 before that). Failure modes: none. */
|
||||||
|
uint64_t pmm_total_count(void);
|
||||||
|
uint64_t pmm_free_count(void);
|
||||||
|
uint64_t pmm_usable_count(void);
|
||||||
|
|
||||||
|
#endif /* NADIR_PMM_H */
|
||||||
197
kernel/mm/pmm.c
Normal file
197
kernel/mm/pmm.c
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
/* Nadir physical memory manager (PMM-only)
|
||||||
|
*
|
||||||
|
* very address it touches arrives as a plain
|
||||||
|
* physical range (E820 entries from the caller, reservation bounds from
|
||||||
|
* the linker/boot chain). Runs in 64-bit long mode at Ring 0 (CPL 0)
|
||||||
|
* under the stage-2 identity map, so physical addresses are dereferenced
|
||||||
|
* as-is. pmm_init runs once with IF=0 before sti; alloc/free never block
|
||||||
|
* and never touch hardware, so they are safe under any IF state.
|
||||||
|
* Fault behavior: no page fault is possible inside the first 1GB
|
||||||
|
* identity window; a caller passing a wild E820 pointer faults before we
|
||||||
|
* run, not in here. Bitmap: bit set = used/reserved, bit clear = free.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "e820.h"
|
||||||
|
#include "pmm.h"
|
||||||
|
|
||||||
|
/* Filled by the stage-2 entry (copy from the stage-1 staging buffer at
|
||||||
|
* 0x5000/0x5020) after BSS zeroing, before kmain runs. */
|
||||||
|
uint32_t memmap_count;
|
||||||
|
struct e820_entry memmap_entries[E820_MAX];
|
||||||
|
|
||||||
|
/* Kernel image bounds from the linker script (linked at 0x10000). */
|
||||||
|
extern char __kernel_start[];
|
||||||
|
extern char __kernel_end[];
|
||||||
|
|
||||||
|
/* One bit per 4KB frame over the first 1GB: 256K frames = 32KB in .bss. */
|
||||||
|
static uint8_t bitmap[PMM_MAX_FRAMES / 8U];
|
||||||
|
|
||||||
|
static uint64_t g_usable;
|
||||||
|
static uint64_t g_free;
|
||||||
|
|
||||||
|
/* WHY bit ops instead of a byte-per-frame array: 32KB of .bss keeps the
|
||||||
|
* kernel image small (sectors are loaded from floppy LBA 1..N, so every
|
||||||
|
* KB costs boot time); the shift/mask cost is negligible next to a VGA
|
||||||
|
* print. */
|
||||||
|
static void set_bit(uint64_t frame)
|
||||||
|
{
|
||||||
|
bitmap[frame / 8U] |= (uint8_t)(1U << (frame % 8U));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_bit(uint64_t frame)
|
||||||
|
{
|
||||||
|
bitmap[frame / 8U] &= (uint8_t)~(1U << (frame % 8U));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_bit(uint64_t frame)
|
||||||
|
{
|
||||||
|
return (bitmap[frame / 8U] & (uint8_t)(1U << (frame % 8U))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark every frame in [base, base+len) reserved. Out-of-window and
|
||||||
|
* already-reserved frames are skipped, so overlapping/low regions are
|
||||||
|
* safe to reserve twice. */
|
||||||
|
static void reserve_range(uint64_t base, uint64_t len)
|
||||||
|
{
|
||||||
|
uint64_t end;
|
||||||
|
uint64_t frame;
|
||||||
|
|
||||||
|
if (len == 0U) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
end = base + len;
|
||||||
|
if (end < base) {
|
||||||
|
end = UINT64_MAX;
|
||||||
|
}
|
||||||
|
if (base >= PMM_MAX_BYTES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (end > PMM_MAX_BYTES) {
|
||||||
|
end = PMM_MAX_BYTES;
|
||||||
|
}
|
||||||
|
for (frame = base / PMM_FRAME_SIZE; frame < end / PMM_FRAME_SIZE; frame++) {
|
||||||
|
if (test_bit(frame) == 0) {
|
||||||
|
set_bit(frame);
|
||||||
|
g_free--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_init(uint32_t count, const struct e820_entry *entries)
|
||||||
|
{
|
||||||
|
uint64_t i;
|
||||||
|
uint64_t frame;
|
||||||
|
uint64_t start;
|
||||||
|
uint64_t end;
|
||||||
|
|
||||||
|
/* Start fully reserved; usable E820 frames are cleared below. This
|
||||||
|
* way unknown memory can never be handed out by accident. */
|
||||||
|
for (i = 0; i < sizeof(bitmap); i++) {
|
||||||
|
bitmap[i] = 0xFFU;
|
||||||
|
}
|
||||||
|
g_usable = 0;
|
||||||
|
g_free = 0;
|
||||||
|
|
||||||
|
if (entries == 0) {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
if (count > E820_MAX) {
|
||||||
|
count = E820_MAX;
|
||||||
|
}
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (entries[i].type != E820_USABLE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (entries[i].length == 0U) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
start = entries[i].base;
|
||||||
|
end = start + entries[i].length;
|
||||||
|
if (end < start) {
|
||||||
|
end = UINT64_MAX;
|
||||||
|
}
|
||||||
|
if (start >= PMM_MAX_BYTES) {
|
||||||
|
continue; /* above the identity-mapped window: ignored */
|
||||||
|
}
|
||||||
|
if (end > PMM_MAX_BYTES) {
|
||||||
|
end = PMM_MAX_BYTES;
|
||||||
|
}
|
||||||
|
/* Align the range to whole frames */
|
||||||
|
start = (start + PMM_FRAME_SIZE - 1U) / PMM_FRAME_SIZE;
|
||||||
|
end = end / PMM_FRAME_SIZE;
|
||||||
|
for (frame = start; frame < end; frame++) {
|
||||||
|
if (test_bit(frame) != 0) {
|
||||||
|
clear_bit(frame);
|
||||||
|
g_usable++;
|
||||||
|
g_free++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Second pass: carve out every non-usable E820 range. WHY a separate
|
||||||
|
* pass instead of skipping overlaps up front: BIOS maps routinely
|
||||||
|
* stack a reserved entry (e.g. ACPI data) inside a larger usable one,
|
||||||
|
* and reserve_range is idempotent, so usable-then-reserved converges
|
||||||
|
* on the right bitmap whatever order the firmware lists. */
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (entries[i].type != E820_USABLE) {
|
||||||
|
reserve_range(entries[i].base, entries[i].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reserve_range(0x00000000ULL, 0x00100000ULL);
|
||||||
|
reserve_range((uint64_t)(uintptr_t)__kernel_start,
|
||||||
|
(uint64_t)(uintptr_t)__kernel_end - (uint64_t)(uintptr_t)__kernel_start);
|
||||||
|
reserve_range(0x00070000ULL, 0x00004000ULL);
|
||||||
|
reserve_range(0x00005000ULL, 0x00000620ULL);
|
||||||
|
reserve_range(0x0008C000ULL, 0x00004000ULL);
|
||||||
|
reserve_range(0x000B8000ULL, 0x00001000ULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pmm_alloc_frame(void)
|
||||||
|
{
|
||||||
|
uint64_t frame;
|
||||||
|
|
||||||
|
for (frame = 0; frame < PMM_MAX_FRAMES; frame++) {
|
||||||
|
if (test_bit(frame) == 0) {
|
||||||
|
set_bit(frame);
|
||||||
|
g_free--;
|
||||||
|
return frame * PMM_FRAME_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; /* frame 0 is always reserved: 0 unambiguously means OOM */
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_free_frame(uint64_t addr)
|
||||||
|
{
|
||||||
|
uint64_t frame;
|
||||||
|
|
||||||
|
if ((addr % PMM_FRAME_SIZE) != 0U) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (addr >= PMM_MAX_BYTES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frame = addr / PMM_FRAME_SIZE;
|
||||||
|
if (test_bit(frame) != 0) {
|
||||||
|
clear_bit(frame);
|
||||||
|
g_free++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pmm_total_count(void)
|
||||||
|
{
|
||||||
|
return PMM_MAX_FRAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pmm_free_count(void)
|
||||||
|
{
|
||||||
|
return g_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pmm_usable_count(void)
|
||||||
|
{
|
||||||
|
return g_usable;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue