summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mos/sys/inc/mm/pmm.h35
-rw-r--r--mos/sys/kern/Makefile1
-rw-r--r--mos/sys/kern/kern_init.c4
-rw-r--r--mos/sys/mm/pmm.c279
4 files changed, 319 insertions, 0 deletions
diff --git a/mos/sys/inc/mm/pmm.h b/mos/sys/inc/mm/pmm.h
new file mode 100644
index 0000000..b7c49dc
--- /dev/null
+++ b/mos/sys/inc/mm/pmm.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2025, Ian Moffett.
+ * Provided under the BSD-3 clause.
+ */
+
+#ifndef _MM_PMM_H_
+#define _MM_PMM_H_ 1
+
+#include <sdk/types.h>
+
+/*
+ * Allocate one or more physical memory frames
+ *
+ * @count: Number of frames to allocate
+ * @flags: Optional flags
+ *
+ * Returns the base of the page aligned area on success
+ * and zero on failure.
+ */
+UPTR mm_alloc_frame(USIZE count);
+
+/*
+ * Deallocate a previously allocated region of frames
+ *
+ * @base: Base of frames
+ * @count: Number of frames to free
+ */
+void mm_free_frame(UPTR base, USIZE count);
+
+/*
+ * Initialize the physical memory manager
+ */
+void pmm_init(void);
+
+#endif /* !_MM_PMM_H_ */
diff --git a/mos/sys/kern/Makefile b/mos/sys/kern/Makefile
index 9c08e6e..32d6dc5 100644
--- a/mos/sys/kern/Makefile
+++ b/mos/sys/kern/Makefile
@@ -5,6 +5,7 @@
CFILES = $(shell find . -name "*.c")
CFILES += $(shell find ../bpt -name "*.c")
+CFILES += $(shell find ../mm -name "*.c")
OFILES = $(CFILES:.c=.o)
SYS_CFLAGS =
diff --git a/mos/sys/kern/kern_init.c b/mos/sys/kern/kern_init.c
index d47f302..2502dc2 100644
--- a/mos/sys/kern/kern_init.c
+++ b/mos/sys/kern/kern_init.c
@@ -6,6 +6,7 @@
#include <kern/trace.h>
#include <kern/bpt.h>
#include <mu/uart.h>
+#include <mm/pmm.h>
#define write_boot_header() \
trace_raw( \
@@ -29,4 +30,7 @@ kern_main(void)
/* Initialize the boot protocol translation layer */
bpt_init();
+
+ /* Initialize the physical memory manager */
+ pmm_init();
}
diff --git a/mos/sys/mm/pmm.c b/mos/sys/mm/pmm.c
new file mode 100644
index 0000000..56c0c0a
--- /dev/null
+++ b/mos/sys/mm/pmm.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2025, Ian Moffett.
+ * Provided under the BSD-3 clause.
+ */
+
+#include <sdk/types.h>
+#include <sdk/param.h>
+#include <sdk/units.h>
+#include <sdk/string.h>
+#include <kern/bpt.h>
+#include <kern/trace.h>
+#include <kern/spinlock.h>
+#include <mm/pmm.h>
+#include <mm/vmm.h>
+
+#define dtrace(fmt, ...) trace("pmm: " fmt, ##__VA_ARGS__)
+
+/* Memory statistics */
+static USIZE mem_total = 0;
+static USIZE mem_free = 0;
+static UPTR usable_top = 0;
+
+/* Bitmap */
+static UBYTE *bitmap = NULL;
+static USIZE bitmap_size = 0;
+static USIZE last_bit = 0;
+static SPINLOCK bitmap_lock;
+
+/*
+ * Table to convert numeric memory types into string
+ * values
+ */
+static const char *memtype_tab[] = {
+ [BPT_MEM_USABLE] = "usable",
+ [BPT_MEM_RESERVED] = "reserved",
+ [BPT_MEM_ACPI_RECLAIM] = "acpi reclaim",
+ [BPT_MEM_ACPI_NVS] = "acpi nvs",
+ [BPT_MEM_BAD] = "bad!",
+ [BPT_MEM_BOOTLOADER] = "bootloader",
+ [BPT_MEM_KERNEL] = "kernel",
+ [BPT_MEM_FRAMEBUFFER] = "framebuffer"
+};
+
+/*
+ * Print out storage size for stat dumps
+ */
+static inline void
+print_mem_size(const char *title, USIZE length)
+{
+ if (title == NULL) {
+ title = "bad";
+ }
+
+ if (length >= UNIT_GIB) {
+ dtrace("%s :: %d gib\n", title, length / UNIT_GIB);
+ } else if (length >= UNIT_MIB) {
+ dtrace("%s :: %d mib\n", title, length / UNIT_MIB);
+ } else {
+ dtrace("%s :: %d bytes\n", title, length);
+ }
+}
+
+/*
+ * Mark a range in the bitmap as allocated or free
+ *
+ * @start: Range start
+ * @end: Range end
+ * @alloc: If true, mark as allocated (1)
+ *
+ * 1: ALLOCATED
+ * 0: FREE
+ */
+static void
+bitmap_set_range(UPTR start, UPTR end, BOOL alloc)
+{
+ /* Clamp range to page boundary */
+ start = ALIGN_DOWN(start, PAGESIZE);
+ end = ALIGN_UP(end, PAGESIZE);
+
+ for (UPTR p = start; p < end; p += PAGESIZE) {
+ if (alloc) {
+ SETBIT(bitmap, p / PAGESIZE);
+ } else {
+ CLRBIT(bitmap, p / PAGESIZE);
+ }
+ }
+}
+
+static void
+pmm_fill_bitmap(void)
+{
+ BPT_MEMENTRY entry;
+ MOS_STATUS status;
+ UPTR start, end;
+
+ /* Fill it all as allocated */
+ memset(bitmap, 0xFF, bitmap_size);
+
+ /* Go through each segment of memory */
+ for (USIZE i = 0;; ++i) {
+ status = bpt_get_mementry(i, &entry);
+
+ /* End of list? */
+ if (status != STATUS_SUCCESS) {
+ break;
+ }
+
+ if (entry.type == BPT_MEM_USABLE) {
+ start = entry.base;
+ end = start + entry.length;
+ bitmap_set_range(start, end, false);
+ }
+ }
+}
+
+static void
+pmm_alloc_bitmap(void)
+{
+ BPT_MEMENTRY entry;
+ MOS_STATUS status;
+
+ /* Go through each segment of memory */
+ for (USIZE i = 0;; ++i) {
+ status = bpt_get_mementry(i, &entry);
+
+ /* End of list? */
+ if (status != STATUS_SUCCESS) {
+ break;
+ }
+
+ if (entry.length < bitmap_size) {
+ continue;
+ }
+
+ bitmap = pma_to_vma(entry.base);
+ break;
+ }
+
+ pmm_fill_bitmap();
+}
+
+/*
+ * Allocate one or more physical memory frames
+ */
+static UPTR
+pmem_alloc(USIZE count)
+{
+ SSIZE start_idx = -1;
+ USIZE frames_found = 0;
+ UPTR start, end;
+ USIZE max_bit;
+
+ max_bit = usable_top / PAGESIZE;
+ for (USIZE i = last_bit; i < max_bit; ++i) {
+ if (!TESTBIT(bitmap, i)) {
+ if (start_idx < 0)
+ start_idx = i;
+ if ((++frames_found) >= count)
+ break;
+
+ continue;
+ }
+
+ start_idx = -1;
+ }
+
+ if (start_idx < 0) {
+ return 0;
+ }
+
+ start = start_idx * PAGESIZE;
+ end = start + (count * PAGESIZE);
+ bitmap_set_range(start, end, true);
+ return start;
+}
+
+/*
+ * Probe the physical memory and discover attributes
+ * and such
+ */
+static void
+pmm_probe(void)
+{
+ BPT_MEMENTRY entry;
+ MOS_STATUS status;
+ UPTR base, end;
+ const char *typestr;
+
+ /* Go through each segment of memory */
+ for (USIZE i = 0;; ++i) {
+ status = bpt_get_mementry(i, &entry);
+
+ /* End of list? */
+ if (status != STATUS_SUCCESS) {
+ break;
+ }
+
+ /* Drop bad entries */
+ if (entry.type >= NELEM(memtype_tab)) {
+ continue;
+ }
+
+ /* Get information about the area */
+ base = entry.base;
+ end = entry.base + entry.length;
+ typestr = memtype_tab[entry.type];
+ mem_total += entry.length;
+
+ dtrace(
+ "[%p - %p) :: %s\n",
+ base,
+ end,
+ typestr == NULL
+ ? "bad entry"
+ : typestr
+ );
+
+ /* Usable memory types from here on out */
+ if (entry.type != BPT_MEM_USABLE) {
+ continue;
+ }
+
+ /* Get the highest usable address */
+ if (end > usable_top) {
+ usable_top = end;
+ }
+
+ mem_free += entry.length;
+ }
+
+ /* Compute the length of the bitmap */
+ bitmap_size = usable_top / PAGESIZE;
+ bitmap_size = ALIGN_UP(bitmap_size, 8) / 8;
+
+ /* Print memory stats */
+ print_mem_size("memory free", mem_free);
+ print_mem_size("memory total", mem_total);
+ print_mem_size("bitmap size", bitmap_size);
+ dtrace("usable memory top <@> :: %p\n", usable_top);
+}
+
+UPTR
+mm_alloc_frame(USIZE count)
+{
+ UPTR phys;
+
+ spinlock_acquire(&bitmap_lock, SPINLOCK_IRQMUT);
+ phys = pmem_alloc(count);
+ if (phys == 0) {
+ last_bit = 0;
+ phys = pmem_alloc(count);
+ }
+
+ spinlock_release(&bitmap_lock);
+ return phys;
+}
+
+void
+mm_free_frame(UPTR base, USIZE count)
+{
+ UPTR range_end;
+
+ spinlock_acquire(&bitmap_lock, SPINLOCK_IRQMUT);
+ range_end = base + (count * PAGESIZE);
+ bitmap_set_range(base, range_end, false);
+ spinlock_release(&bitmap_lock);
+}
+
+void
+pmm_init(void)
+{
+ spinlock_init("pmm", &bitmap_lock);
+
+ dtrace("begin memory probe\n");
+ pmm_probe();
+
+ pmm_alloc_bitmap();
+ dtrace("bitmap allocated OK\n");
+}