diff options
author | Ian Moffett <ian@osmora.org> | 2025-09-14 15:28:12 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-09-14 15:28:12 -0400 |
commit | 3be168d44e469adcbb3d283999fce4bfe7ffa261 (patch) | |
tree | 35194fb3db1948b98f50c57cfcb660a958183d1d /src/sys | |
parent | 3b9e8a1aa73fed52268def39400b063030d0bc5c (diff) |
os: vm: Add physical memory manager
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'src/sys')
-rw-r--r-- | src/sys/include/vm/physseg.h | 73 | ||||
-rw-r--r-- | src/sys/include/vm/vm.h | 2 | ||||
-rw-r--r-- | src/sys/os/Makefile | 1 | ||||
-rw-r--r-- | src/sys/os/os_init.c | 2 | ||||
-rw-r--r-- | src/sys/vm/vm_init.c | 42 | ||||
-rw-r--r-- | src/sys/vm/vm_seg.c | 264 |
6 files changed, 384 insertions, 0 deletions
diff --git a/src/sys/include/vm/physseg.h b/src/sys/include/vm/physseg.h new file mode 100644 index 0000000..bd44f0a --- /dev/null +++ b/src/sys/include/vm/physseg.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Ian Marco Moffett and L5 engineers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _VM_PHYSSEG_H_ +#define _VM_PHYSSEG_H_ 1 + +#include <sys/types.h> + +/* + * Represents physical memory stats gathered + * during initialization or during refresh. + */ +struct physmem_stat { + size_t pages_free; + size_t pages_used; +}; + +/* + * Initialize physical memory and get initial physical + * memory stats. + * + * @stat: Stats are written here + * + * Returns zero on success, otherwise a less than zero + * value on failure. + */ +int vm_seg_init(struct physmem_stat *stat); + +/* + * Allocate one or more physical frames and + * get the [physical] address. + * + * @count: Number of frames to allocate + * + * Returns zero on failure (e.g., out of memory) + */ +uintptr_t vm_alloc_frame(size_t count); + +/* + * Free one or more physical frames + * + * @base: Base address to start freeing at + * @count: Number of frames to free + */ +void vm_free_frame(uintptr_t base, size_t count); + +#endif /* !_VM_PHYSSEG_H_ */ diff --git a/src/sys/include/vm/vm.h b/src/sys/include/vm/vm.h index ba76b98..476ff27 100644 --- a/src/sys/include/vm/vm.h +++ b/src/sys/include/vm/vm.h @@ -38,4 +38,6 @@ #define VIRT_TO_PHYS(VIRT) ((uintptr_t)(VIRT) - VM_HIGHER_HALF) #define DEFAULT_PAGESIZE 4096 +void vm_init(void); + #endif /* !_VM_H_ */ diff --git a/src/sys/os/Makefile b/src/sys/os/Makefile index c0bd363..f5d1530 100644 --- a/src/sys/os/Makefile +++ b/src/sys/os/Makefile @@ -6,6 +6,7 @@ override PROMPT := printf "%s\t\t%s\n" TARGET_LIB = ../target/libkern.a CFLAGS = -I../include/ -I../include/lib/ -I../target/header/ $(MI_CFLAGS) -O0 CFILES = $(shell find . -name "*.c") +CFILES += $(shell find ../vm -name "*.c") CFILES += $(shell find ../lib -name "*.c") DEPS = $(CFILES:.c=.d) diff --git a/src/sys/os/os_init.c b/src/sys/os/os_init.c index 52d8979..bf4257d 100644 --- a/src/sys/os/os_init.c +++ b/src/sys/os/os_init.c @@ -2,6 +2,7 @@ #include <sys/panic.h> #include <sys/syslog.h> #include <sys/cpuvar.h> +#include <vm/vm.h> struct pcore g_bsp; @@ -13,6 +14,7 @@ main(void) { cpu_conf(&g_bsp); printf("booting l5 lunos v0.0.1...\n"); + vm_init(); panic("end of kernel reached\n"); for (;;); } diff --git a/src/sys/vm/vm_init.c b/src/sys/vm/vm_init.c new file mode 100644 index 0000000..f9c33e6 --- /dev/null +++ b/src/sys/vm/vm_init.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Ian Marco Moffett and L5 engineers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/panic.h> +#include <vm/vm.h> +#include <vm/physseg.h> + +static struct physmem_stat stat; + +void +vm_init(void) +{ + if (vm_seg_init(&stat) < 0) { + panic("vm_init: vm_seg_init() failed\n"); + } +} diff --git a/src/sys/vm/vm_seg.c b/src/sys/vm/vm_seg.c new file mode 100644 index 0000000..94a00ee --- /dev/null +++ b/src/sys/vm/vm_seg.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2025 Ian Marco Moffett and L5 engineers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/syslog.h> +#include <sys/panic.h> +#include <os/spinlock.h> +#include <vm/physseg.h> +#include <vm/vm.h> +#include <limine.h> +#include <string.h> + +#define BYTES_PER_MIB 8388608 + +static size_t pages_free = 0; +static size_t pages_used = 0; +static size_t pages_total = 0; +static size_t highest_frame_idx = 0; +static size_t bitmap_size = 0; +static size_t bitmap_free_start = 0; +static ssize_t last_idx = 0; + +static uint8_t *bitmap; +static struct limine_memmap_response *resp = NULL; +static struct spinlock lock = {0}; + +static struct limine_memmap_request mmap_req = { + .id = LIMINE_MEMMAP_REQUEST, + .revision = 0 +}; + +static const char *memstr_tab[] = { + [LIMINE_MEMMAP_USABLE] = "usable", + [LIMINE_MEMMAP_RESERVED] = "reserved", + [LIMINE_MEMMAP_ACPI_RECLAIMABLE] = "ACPI reclaimable", + [LIMINE_MEMMAP_ACPI_NVS] = "ACPI NVS", + [LIMINE_MEMMAP_BAD_MEMORY] = "bad", + [LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE] = "bootloader reclaimable", + [LIMINE_MEMMAP_KERNEL_AND_MODULES] = "ethos0", + [LIMINE_MEMMAP_FRAMEBUFFER] = "vram" +}; + +/* + * Populate physical memory bitmap. + */ +static void +physmem_populate_bitmap(void) +{ + struct limine_memmap_entry *ent; + + for (size_t i = 0; i < resp->entry_count; ++i) { + ent = resp->entries[i]; + pages_total += ent->length / DEFAULT_PAGESIZE; + + if (ent->type != LIMINE_MEMMAP_USABLE) { + /* This memory is not usable */ + pages_used += ent->length / DEFAULT_PAGESIZE; + continue; + } + + if (bitmap_free_start == 0) { + bitmap_free_start = ent->base / DEFAULT_PAGESIZE; + } + + for (size_t j = 0; j < ent->length; j += DEFAULT_PAGESIZE) { + clrbit(bitmap, (ent->base + j) / DEFAULT_PAGESIZE); + } + + pages_free += ent->length / DEFAULT_PAGESIZE; + } +} + +/* + * Allocate physical memory for the bitmap + * we'll use to keep track of free memory. + */ +static void +physmem_alloc_bitmap(void) +{ + struct limine_memmap_entry *ent; + uintptr_t highest_addr = 0; + + for (size_t i = 0; i < resp->entry_count; ++i) { + ent = resp->entries[i]; + + if (ent->type != LIMINE_MEMMAP_USABLE) { + /* This memory is not usable */ + continue; + } + + if (ent->length >= bitmap_size) { + bitmap = PHYS_TO_VIRT(ent->base); + memset(bitmap, 0xFF, bitmap_size); + ent->length -= bitmap_size; + ent->base += bitmap_size; + return; + } + + highest_addr = MAX(highest_addr, ent->base + ent->length); + } +} + +/* + * Init the physical memory bitmap. + */ +static void +physmem_init_bitmap(void) +{ + uintptr_t highest_addr = 0; + struct limine_memmap_entry *ent; + uintptr_t start, end; + const char *typestr = "bad"; + + for (size_t i = 0; i < resp->entry_count; ++i) { + ent = resp->entries[i]; + + if (ent->type < NELEM(memstr_tab)) { + typestr = memstr_tab[ent->type]; + } + + start = ent->base; + end = ent->base + ent->length; + printf("sysmem: [%p -> %p]: %s\n", start, end, typestr); + + if (ent->type != LIMINE_MEMMAP_USABLE) { + continue; + } + + highest_addr = MAX(highest_addr, ent->base + ent->length); + } + + highest_frame_idx = highest_addr / DEFAULT_PAGESIZE; + bitmap_size = ALIGN_UP(highest_frame_idx / 8, DEFAULT_PAGESIZE); + + physmem_alloc_bitmap(); + physmem_populate_bitmap(); +} + +/* + * Allocate page frames. + * + * @count: Number of frames to allocate. + */ +static uintptr_t +__vm_alloc_frame(size_t count) +{ + size_t frames = 0; + ssize_t idx = -1; + uintptr_t ret = 0; + + for (size_t i = last_idx; i < highest_frame_idx; ++i) { + if (!testbit(bitmap, i)) { + if (idx < 0) + idx = i; + if (++frames >= count) + break; + + continue; + } + + idx = -1; + frames = 0; + } + + if (idx < 0 || frames != count) { + ret = 0; + goto done; + } + + for (size_t i = idx; i < idx + count; ++i) { + setbit(bitmap, i); + } + ret = idx * DEFAULT_PAGESIZE; + last_idx = idx; + memset(PHYS_TO_VIRT(ret), 0, count * DEFAULT_PAGESIZE); +done: + return ret; +} + +/* + * Central frame allocation routine + */ +uintptr_t +vm_alloc_frame(size_t count) +{ + uintptr_t ret; + + spinlock_acquire(&lock); + if ((ret = __vm_alloc_frame(count)) == 0) { + last_idx = 0; + ret = __vm_alloc_frame(count); + } + + if (ret == 0) { + panic("out of memory\n"); + } + + pages_used += count; + pages_free -= count; + spinlock_release(&lock); + return ret; +} + +/* + * Central frame freeing routine + */ +void +vm_free_frame(uintptr_t base, size_t count) +{ + size_t stop_at = base + (count * DEFAULT_PAGESIZE); + + base = ALIGN_UP(base, DEFAULT_PAGESIZE); + + spinlock_acquire(&lock); + for (uintptr_t p = base; p < stop_at; p += DEFAULT_PAGESIZE) { + clrbit(bitmap, p / DEFAULT_PAGESIZE); + } + pages_used -= count; + pages_free += count; + spinlock_release(&lock); +} + +int +vm_seg_init(struct physmem_stat *stat) +{ + resp = mmap_req.response; + physmem_init_bitmap(); + + stat->pages_free = pages_free; + stat->pages_used = pages_used; + printf( + "physseg initialized [free: %d, used: %d]\n", + pages_free, + pages_used + ); + return 0; +} |