aboutsummaryrefslogtreecommitdiff
path: root/kernel/mm/physical_mm/physical_mm.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/mm/physical_mm/physical_mm.c')
-rw-r--r--kernel/mm/physical_mm/physical_mm.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/kernel/mm/physical_mm/physical_mm.c b/kernel/mm/physical_mm/physical_mm.c
new file mode 100644
index 0000000..d7a5c31
--- /dev/null
+++ b/kernel/mm/physical_mm/physical_mm.c
@@ -0,0 +1,170 @@
+/*
+ * bubbl
+ * Copyright (C) 2025 Raghuram Subramani <raghus2247@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Bitmap Based Allocation:
+ * https://web.archive.org/web/20190316115948/http://www.brokenthorn.com/Resources/OSDev17.html
+ */
+
+/* TODO: Stack based allocation? */
+
+#include <kernel/halt.h>
+#include <kernel/spinlock.h>
+#include <libk/stdio.h>
+#include <mm/memory_map.h>
+#include <mm/physical_mm.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+uint32_t kernel_start;
+uint32_t kernel_end;
+
+uint32_t l_block_count = 0;
+uint32_t l_total_free_blocks = 0;
+uint32_t l_memory_map[MAX_BLOCKS / BITMAP_ENTRY_SIZE];
+
+spinlock_t l_lock;
+
+ALWAYS_INLINE static void
+log_memory_map(free_memory_regions_t *free_memory_regions)
+{
+ printk("\nphysical_mm", "Free Memory Regions:");
+ for (int i = 0; i < free_memory_regions->n_regions; i++)
+ printk("physical_mm",
+ "start: 0x%.08x | length: 0x%.08x",
+ free_memory_regions->region_list[i]->addr_low,
+ free_memory_regions->region_list[i]->len_low);
+
+ printk("\nphysical_mm", "Kernel region:");
+ printk("physical_mm", "Start: 0x%x", &kernel_start);
+ printk("physical_mm", "End: 0x%x", &kernel_end);
+ printk("physical_mm",
+ "Size: 0x%x",
+ ((uint32_t) &kernel_end) - ((uint32_t) &kernel_start));
+}
+
+ALWAYS_INLINE static void
+initialize_region(uint32_t start, uint32_t length)
+{
+ /* Get the location of the start address in the bitmap */
+ uint32_t bit = start / BLOCK_SIZE;
+ uint32_t n_blocks = length / BLOCK_SIZE;
+
+ for (; n_blocks > 0; n_blocks--)
+ if (pmm_test_bit(bit, l_memory_map))
+ pmm_set_usable(bit++, &l_total_free_blocks, l_memory_map);
+
+ /* First block is always used (first 64KiB) */
+ if (!pmm_test_bit(0, l_memory_map))
+ pmm_set_used(0, &l_total_free_blocks, l_memory_map);
+}
+
+ALWAYS_INLINE static void
+deinitialize_region(uint32_t start, uint32_t length)
+{
+ uint32_t bit = start / BLOCK_SIZE;
+ uint32_t n_blocks = length / BLOCK_SIZE;
+
+ if (length % BLOCK_SIZE > 0)
+ n_blocks++;
+
+ for (; n_blocks > 0; n_blocks--)
+ pmm_set_used(bit++, &l_total_free_blocks, l_memory_map);
+}
+
+ALWAYS_INLINE static uint32_t
+find_free_block(void)
+{
+ /* TODO: Why doesn't using block_count instead of MAX_BLOCKS work? */
+ for (uint32_t i = 0; i < MAX_BLOCKS / BITMAP_ENTRY_SIZE; i++)
+ /* At least one block in the entry isn't in use */
+ if (l_memory_map[i] != 0xffffffff)
+ /* Test each bit to see if it's zero */
+ for (uint32_t j = 0; j < BITMAP_ENTRY_SIZE; j++)
+ if (!pmm_test_bit(i * BITMAP_ENTRY_SIZE + j, l_memory_map))
+ return i * BITMAP_ENTRY_SIZE + j;
+
+ /* Shouldn't be reached, since we're keeping track of the number of free
+ * blocks */
+ ASSERT_NOT_REACHED();
+ return -1;
+}
+
+void
+pmm_initialize(void)
+{
+ free_memory_regions_t *free_memory_regions = mmap_get_free_regions();
+ log_memory_map(free_memory_regions);
+
+ spinlock_acquire(&l_lock);
+
+ /* All blocks are initially used */
+ /* TODO: Move this block to a place after block_count is set. This is why
+ * using block_count instead of MAX_BLOCKS wasn't working. */
+ for (uint32_t i = 0; i < MAX_BLOCKS / BITMAP_ENTRY_SIZE; i++)
+ l_memory_map[i] = 0xffffffff;
+
+ for (int i = 0; i < free_memory_regions->n_regions; i++) {
+ multiboot_memory_map_t *region = free_memory_regions->region_list[i];
+ initialize_region(region->addr_low, region->len_low);
+ }
+
+ uint32_t kernel_size = ((uint32_t) &kernel_end) - ((uint32_t) &kernel_start);
+ deinitialize_region((uint32_t) &kernel_start, kernel_size);
+
+ /* Deinitialize first 8MiB */
+ deinitialize_region(0, 8 * MiB);
+
+ spinlock_release(&l_lock);
+
+ /* Manually loop through and calculate the number of free blocks. */
+ for (uint32_t i = 0; i < MAX_BLOCKS / BITMAP_ENTRY_SIZE; i++)
+ /* At least one block in the entry isn't in use */
+ if (l_memory_map[i] != 0xffffffff)
+ /* Test each bit to see if it's zero */
+ for (uint32_t j = 0; j < BITMAP_ENTRY_SIZE; j++)
+ if (!pmm_test_bit(i * BITMAP_ENTRY_SIZE + j, l_memory_map))
+ l_total_free_blocks++;
+
+ printk("physical_mm", "Total free blocks: 0x%x", l_total_free_blocks);
+}
+
+void *
+pmm_allocate_block(void)
+{
+ if (l_total_free_blocks == 0) {
+ printk("physical_mm", "No more free blocks!");
+ return NULL;
+ }
+
+ spinlock_acquire(&l_lock);
+
+ uint32_t block = find_free_block();
+ pmm_set_used(block, &l_total_free_blocks, l_memory_map);
+
+ spinlock_release(&l_lock);
+
+ uint32_t physical_address = block * BLOCK_SIZE;
+ return (void *) physical_address;
+}
+
+void
+pmm_free_block(void *physical_address)
+{
+ uint32_t block = ((uint32_t) physical_address) / BLOCK_SIZE;
+ pmm_set_usable(block, &l_total_free_blocks, l_memory_map);
+}