Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure
Source tree reorganization: - Move StarForth v3 engine to v3/ (src/, include/, Makefile) - Move kernel to kernel/ (src/, include/, linker/, Makefile) - Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md) - Move FABRIC-0..4.md to docs/fabric/ - Move ONTOLOGY.md and ROADMAP.md to docs/ Board infrastructure: - Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/ - Each board has board.mk (ISA, CPU flags, boot recipe) and README.md - Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET - make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board - ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet) - scripts/mkdiskimage.sh builds disk images for all boards Docs pipeline: - docs/book/ with LaTeX master (main.tex) and Makefile - pandoc converts Markdown to LaTeX at build time - Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks) - make docs builds single PDF (754 pages, 0 missing characters) - make docs TARGET=<board> adds board appendix - build/docs/<book|board>/meta.tex stamps git commit into PDF Bug fixes: - 42 include paths that only worked by accident now use correct relative paths - clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build) - Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved - Doxyfile, .clang-tidy, README.md, Kconfig paths updated Verified: - Hosted v3 build passes 1012 tests, 0 failures - SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE) - Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes - make clean TARGET=<board> removes only that board and its ISA objects - make all builds all boards, hosted v3, and docs in one run Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# src/starkernel/memory/
|
||||
|
||||
- `pmm.c` — physical memory manager (bitmap allocator).
|
||||
- `vmm.c` — virtual memory manager (4-level x86_64 paging).
|
||||
- `kmalloc.c` — kernel heap allocator (16 MB heap).
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* kmalloc.c - Simple kernel heap allocator
|
||||
*
|
||||
* Reserves a fixed heap from PMM and serves aligned allocations with a
|
||||
* doubly-linked free list plus coalescing. The allocator avoids libc helpers
|
||||
* and keeps metadata inside the heap itself.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "kmalloc.h"
|
||||
#include "pmm.h"
|
||||
|
||||
#define KMALLOC_DEFAULT_HEAP_SIZE (2ULL * 1024ULL * 1024ULL * 1024ULL) /* 2 GiB — floor for 256+ baby VMs */
|
||||
#define KMALLOC_MIN_ALIGN 16u
|
||||
#define KMALLOC_MAGIC 0x4B4D414Cu /* 'KMAL' */
|
||||
|
||||
typedef struct heap_block {
|
||||
size_t size; /* payload bytes available in this block */
|
||||
size_t requested; /* user-requested size (for stats) */
|
||||
int free; /* 1 if free, 0 if allocated */
|
||||
struct heap_block *next;
|
||||
struct heap_block *prev;
|
||||
} heap_block_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
heap_block_t *block;
|
||||
} kmalloc_prefix_t;
|
||||
|
||||
static uint8_t *heap_base = NULL;
|
||||
static uint64_t heap_size = 0;
|
||||
static heap_block_t *heap_head = NULL;
|
||||
static kmalloc_stats_t heap_stats = {0};
|
||||
static int heap_initialized = 0;
|
||||
|
||||
uintptr_t kmalloc_heap_base_addr(void)
|
||||
{
|
||||
return (uintptr_t)heap_base;
|
||||
}
|
||||
|
||||
uintptr_t kmalloc_heap_end_addr(void)
|
||||
{
|
||||
if (!heap_base) {
|
||||
return 0;
|
||||
}
|
||||
return (uintptr_t)(heap_base + heap_size);
|
||||
}
|
||||
|
||||
static size_t align_up_size(size_t value, size_t align)
|
||||
{
|
||||
if (align == 0) {
|
||||
return value;
|
||||
}
|
||||
return (value + align - 1u) & ~(align - 1u);
|
||||
}
|
||||
|
||||
static size_t normalize_alignment(size_t align)
|
||||
{
|
||||
if (align < KMALLOC_MIN_ALIGN) {
|
||||
align = KMALLOC_MIN_ALIGN;
|
||||
}
|
||||
size_t v = KMALLOC_MIN_ALIGN;
|
||||
while (v < align) {
|
||||
v <<= 1;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static void update_free_bytes(void)
|
||||
{
|
||||
if (heap_stats.total_bytes >= heap_stats.used_bytes) {
|
||||
heap_stats.free_bytes = heap_stats.total_bytes - heap_stats.used_bytes;
|
||||
} else {
|
||||
heap_stats.free_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void account_allocation(size_t requested)
|
||||
{
|
||||
heap_stats.used_bytes += requested;
|
||||
if (heap_stats.used_bytes > heap_stats.peak_bytes) {
|
||||
heap_stats.peak_bytes = heap_stats.used_bytes;
|
||||
}
|
||||
update_free_bytes();
|
||||
}
|
||||
|
||||
static void account_free(size_t requested)
|
||||
{
|
||||
if (heap_stats.used_bytes >= requested) {
|
||||
heap_stats.used_bytes -= requested;
|
||||
} else {
|
||||
heap_stats.used_bytes = 0;
|
||||
}
|
||||
update_free_bytes();
|
||||
}
|
||||
|
||||
static int pointer_within_heap(const void *ptr)
|
||||
{
|
||||
uintptr_t addr = (uintptr_t)ptr;
|
||||
uintptr_t start = (uintptr_t)heap_base;
|
||||
uintptr_t end = start + (uintptr_t)heap_size;
|
||||
return addr >= start && addr < end;
|
||||
}
|
||||
|
||||
static void coalesce_neighbors(heap_block_t *block)
|
||||
{
|
||||
while (block->next && block->next->free) {
|
||||
heap_block_t *next = block->next;
|
||||
block->size += sizeof(heap_block_t) + next->size;
|
||||
block->next = next->next;
|
||||
if (block->next) {
|
||||
block->next->prev = block;
|
||||
}
|
||||
}
|
||||
|
||||
while (block->prev && block->prev->free) {
|
||||
heap_block_t *prev = block->prev;
|
||||
prev->size += sizeof(heap_block_t) + block->size;
|
||||
prev->next = block->next;
|
||||
if (block->next) {
|
||||
block->next->prev = prev;
|
||||
}
|
||||
block = prev;
|
||||
}
|
||||
|
||||
if (!block->prev) {
|
||||
heap_head = block;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t min_splittable_payload(void)
|
||||
{
|
||||
return sizeof(kmalloc_prefix_t) + (2u * KMALLOC_MIN_ALIGN);
|
||||
}
|
||||
|
||||
static int can_split(size_t remainder)
|
||||
{
|
||||
return remainder > (sizeof(heap_block_t) + min_splittable_payload());
|
||||
}
|
||||
|
||||
static void *allocate_from_block(heap_block_t *block, size_t requested, size_t align)
|
||||
{
|
||||
size_t effective_align = normalize_alignment(align);
|
||||
size_t payload_size = align_up_size(requested, KMALLOC_MIN_ALIGN);
|
||||
|
||||
uint8_t *payload_start = (uint8_t *)(block + 1);
|
||||
uintptr_t aligned_user =
|
||||
align_up_size((uintptr_t)(payload_start + sizeof(kmalloc_prefix_t)), effective_align);
|
||||
|
||||
size_t prefix_offset = (size_t)(aligned_user - (uintptr_t)payload_start);
|
||||
size_t total_needed = prefix_offset + payload_size;
|
||||
|
||||
if (total_needed > block->size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t remainder = block->size - total_needed;
|
||||
if (can_split(remainder)) {
|
||||
uint8_t *new_block_addr = payload_start + total_needed;
|
||||
heap_block_t *new_block = (heap_block_t *)(void *)new_block_addr;
|
||||
new_block->size = remainder - sizeof(heap_block_t);
|
||||
new_block->requested = 0;
|
||||
new_block->free = 1;
|
||||
new_block->next = block->next;
|
||||
new_block->prev = block;
|
||||
if (new_block->next) {
|
||||
new_block->next->prev = new_block;
|
||||
}
|
||||
block->next = new_block;
|
||||
block->size = total_needed;
|
||||
}
|
||||
|
||||
block->free = 0;
|
||||
block->requested = requested;
|
||||
|
||||
kmalloc_prefix_t *prefix = (kmalloc_prefix_t *)(void *)(aligned_user - sizeof(kmalloc_prefix_t));
|
||||
prefix->magic = KMALLOC_MAGIC;
|
||||
prefix->block = block;
|
||||
|
||||
account_allocation(requested);
|
||||
return (void *)aligned_user;
|
||||
}
|
||||
|
||||
int kmalloc_init(uint64_t heap_size_bytes)
|
||||
{
|
||||
if (heap_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pmm_is_initialized()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t requested_size = (heap_size_bytes == 0) ? KMALLOC_DEFAULT_HEAP_SIZE : heap_size_bytes;
|
||||
if (requested_size < PMM_PAGE_SIZE) {
|
||||
requested_size = PMM_PAGE_SIZE;
|
||||
}
|
||||
|
||||
uint64_t pages = requested_size / PMM_PAGE_SIZE;
|
||||
if (requested_size % PMM_PAGE_SIZE) {
|
||||
pages += 1;
|
||||
}
|
||||
|
||||
/* Bisect down to largest contiguous region PMM can serve.
|
||||
* Floor: 64 MiB — below that the kernel cannot function usefully. */
|
||||
#define KMALLOC_MIN_PAGES ((64u * 1024u * 1024u) / PMM_PAGE_SIZE)
|
||||
uint64_t paddr = 0;
|
||||
while (pages >= KMALLOC_MIN_PAGES) {
|
||||
paddr = pmm_alloc_contiguous(pages);
|
||||
if (paddr != 0) break;
|
||||
pages >>= 1;
|
||||
}
|
||||
if (paddr == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
heap_base = (uint8_t *)(uintptr_t)paddr;
|
||||
heap_size = pages * (uint64_t)PMM_PAGE_SIZE;
|
||||
|
||||
heap_head = (heap_block_t *)(void *)heap_base;
|
||||
heap_head->size = heap_size - sizeof(heap_block_t);
|
||||
heap_head->requested = 0;
|
||||
heap_head->free = 1;
|
||||
heap_head->next = NULL;
|
||||
heap_head->prev = NULL;
|
||||
|
||||
heap_stats.total_bytes = heap_head->size;
|
||||
heap_stats.used_bytes = 0;
|
||||
heap_stats.peak_bytes = 0;
|
||||
heap_stats.free_bytes = heap_head->size;
|
||||
|
||||
heap_initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kmalloc_is_initialized(void)
|
||||
{
|
||||
return heap_initialized;
|
||||
}
|
||||
|
||||
void *kmalloc(size_t size)
|
||||
{
|
||||
return kmalloc_aligned(size, KMALLOC_MIN_ALIGN);
|
||||
}
|
||||
|
||||
void *kmalloc_aligned(size_t size, size_t align)
|
||||
{
|
||||
if (!heap_initialized || size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
heap_block_t *cur = heap_head;
|
||||
size_t effective_align = normalize_alignment(align);
|
||||
|
||||
while (cur) {
|
||||
if (cur->free) {
|
||||
void *ptr = allocate_from_block(cur, size, effective_align);
|
||||
if (ptr) {
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void kfree(void *ptr)
|
||||
{
|
||||
if (!heap_initialized || !ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pointer_within_heap(ptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
kmalloc_prefix_t *prefix =
|
||||
(kmalloc_prefix_t *)((uint8_t *)ptr - sizeof(kmalloc_prefix_t));
|
||||
if (prefix->magic != KMALLOC_MAGIC || !prefix->block) {
|
||||
return;
|
||||
}
|
||||
|
||||
heap_block_t *block = prefix->block;
|
||||
if (block->free) {
|
||||
return;
|
||||
}
|
||||
|
||||
account_free(block->requested);
|
||||
block->requested = 0;
|
||||
block->free = 1;
|
||||
coalesce_neighbors(block);
|
||||
}
|
||||
|
||||
kmalloc_stats_t kmalloc_get_stats(void)
|
||||
{
|
||||
return heap_stats;
|
||||
}
|
||||
|
||||
void kmalloc_debug_census(size_t *out_largest_free, size_t *out_free_count,
|
||||
size_t *out_used_count, size_t *out_total_blocks)
|
||||
{
|
||||
size_t largest = 0, free_count = 0, used_count = 0, total = 0;
|
||||
heap_block_t *cur = heap_head;
|
||||
while (cur) {
|
||||
total++;
|
||||
if (cur->free) {
|
||||
free_count++;
|
||||
if (cur->size > largest) largest = cur->size;
|
||||
} else {
|
||||
used_count++;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
if (out_largest_free) *out_largest_free = largest;
|
||||
if (out_free_count) *out_free_count = free_count;
|
||||
if (out_used_count) *out_used_count = used_count;
|
||||
if (out_total_blocks) *out_total_blocks = total;
|
||||
}
|
||||
|
||||
void kmalloc_debug_census_bytes(size_t *out_total_free_bytes, size_t *out_total_used_bytes)
|
||||
{
|
||||
size_t free_bytes = 0, used_bytes = 0;
|
||||
heap_block_t *cur = heap_head;
|
||||
while (cur) {
|
||||
if (cur->free) {
|
||||
free_bytes += cur->size;
|
||||
} else {
|
||||
used_bytes += cur->size;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
if (out_total_free_bytes) *out_total_free_bytes = free_bytes;
|
||||
if (out_total_used_bytes) *out_total_used_bytes = used_bytes;
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* pmm.c - Physical Memory Manager (bitmap allocator)
|
||||
*
|
||||
* Builds a 1-bit-per-page bitmap from the UEFI memory map and provides
|
||||
* primitive page allocation for early kernel bring-up. The allocator favors
|
||||
* simplicity and debuggability; performance tuning can happen once paging and
|
||||
* interrupts are in place.
|
||||
*/
|
||||
|
||||
#include "pmm.h"
|
||||
#include "console.h"
|
||||
|
||||
#define PMM_MAX_BITMAP_BYTES (2u * 1024u * 1024u) /* Tracks up to ~64 GiB */
|
||||
#define PMM_BITMAP_BITS (PMM_MAX_BITMAP_BYTES * 8ull)
|
||||
|
||||
/* Do not hand out pages below this floor (1 MiB) */
|
||||
static const uint64_t pmm_alloc_floor_page = 0x100000u / PMM_PAGE_SIZE;
|
||||
|
||||
static uint8_t pmm_bitmap[PMM_MAX_BITMAP_BYTES];
|
||||
static uint64_t pmm_total_pages = 0;
|
||||
static uint64_t pmm_free_pages = 0;
|
||||
static int pmm_initialized = 0;
|
||||
static uint64_t pmm_tracked_pages = 0;
|
||||
|
||||
extern char __kernel_start[];
|
||||
extern char __kernel_end[];
|
||||
|
||||
static void pmm_memset(void *dst, uint8_t value, size_t size) {
|
||||
uint8_t *ptr = (uint8_t *)dst;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
ptr[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64_t pmm_bytes_to_pages(uint64_t bytes) {
|
||||
uint64_t pages = bytes / PMM_PAGE_SIZE;
|
||||
if (bytes % PMM_PAGE_SIZE) {
|
||||
pages += 1;
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
static inline void bitmap_set(uint64_t bit) {
|
||||
pmm_bitmap[bit / 8u] |= (uint8_t)(1u << (bit % 8u));
|
||||
}
|
||||
|
||||
static inline void bitmap_clear(uint64_t bit) {
|
||||
pmm_bitmap[bit / 8u] &= (uint8_t)~(1u << (bit % 8u));
|
||||
}
|
||||
|
||||
static inline int bitmap_test(uint64_t bit) {
|
||||
return (pmm_bitmap[bit / 8u] >> (bit % 8u)) & 1u;
|
||||
}
|
||||
|
||||
static void bitmap_mark_range(uint64_t start_page, uint64_t page_count, int used) {
|
||||
for (uint64_t i = 0; i < page_count; ++i) {
|
||||
uint64_t page = start_page + i;
|
||||
if (page >= pmm_tracked_pages) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (used) {
|
||||
if (!bitmap_test(page)) {
|
||||
bitmap_set(page);
|
||||
if (pmm_free_pages > 0) {
|
||||
pmm_free_pages--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (bitmap_test(page)) {
|
||||
bitmap_clear(page);
|
||||
pmm_free_pages++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t find_contiguous_free(uint64_t pages_needed) {
|
||||
uint64_t run_start = 0;
|
||||
uint64_t run_length = 0;
|
||||
|
||||
for (uint64_t page = pmm_alloc_floor_page; page < pmm_tracked_pages; ++page) {
|
||||
if (!bitmap_test(page)) {
|
||||
if (run_length == 0) {
|
||||
run_start = page;
|
||||
}
|
||||
run_length++;
|
||||
|
||||
if (run_length == pages_needed) {
|
||||
return run_start;
|
||||
}
|
||||
} else {
|
||||
run_length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
static int is_usable_memory(uint32_t type) {
|
||||
return type == EfiConventionalMemory;
|
||||
}
|
||||
|
||||
static int is_ram_type(uint32_t type) {
|
||||
return is_usable_memory(type) ||
|
||||
type == EfiRuntimeServicesCode ||
|
||||
type == EfiRuntimeServicesData ||
|
||||
type == EfiACPIReclaimMemory ||
|
||||
type == EfiACPIMemoryNVS;
|
||||
}
|
||||
|
||||
static void recompute_free_pages(void) {
|
||||
uint64_t free_count = 0;
|
||||
for (uint64_t page = 0; page < pmm_tracked_pages; ++page) {
|
||||
if (!bitmap_test(page)) {
|
||||
free_count++;
|
||||
}
|
||||
}
|
||||
pmm_free_pages = free_count;
|
||||
}
|
||||
|
||||
int pmm_init(BootInfo *boot_info)
|
||||
{
|
||||
if (!boot_info ||
|
||||
!boot_info->memory_map ||
|
||||
boot_info->memory_map_descriptor_size == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR *map = boot_info->memory_map;
|
||||
UINTN map_size = boot_info->memory_map_size;
|
||||
UINTN desc_size = boot_info->memory_map_descriptor_size;
|
||||
UINTN entry_count = map_size / desc_size;
|
||||
|
||||
uint64_t max_ram_end = 0;
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Pass 1: determine highest physical address of RAM-like regions
|
||||
* ------------------------------------------------------------ */
|
||||
for (UINTN i = 0; i < entry_count; ++i) {
|
||||
EFI_MEMORY_DESCRIPTOR *desc =
|
||||
(EFI_MEMORY_DESCRIPTOR *)((uint8_t *)map + i * desc_size);
|
||||
|
||||
if (!is_ram_type(desc->Type))
|
||||
continue;
|
||||
|
||||
uint64_t region_end =
|
||||
desc->PhysicalStart +
|
||||
desc->NumberOfPages * (uint64_t)PMM_PAGE_SIZE;
|
||||
|
||||
if (region_end > max_ram_end)
|
||||
max_ram_end = region_end;
|
||||
}
|
||||
|
||||
if (max_ram_end == 0)
|
||||
return -1;
|
||||
|
||||
pmm_tracked_pages = pmm_bytes_to_pages(max_ram_end);
|
||||
if (pmm_tracked_pages > PMM_BITMAP_BITS)
|
||||
pmm_tracked_pages = PMM_BITMAP_BITS;
|
||||
|
||||
if (pmm_tracked_pages == 0)
|
||||
return -1;
|
||||
|
||||
uint64_t bitmap_bytes = (pmm_tracked_pages + 7) / 8;
|
||||
pmm_memset(pmm_bitmap, 0xFF, (size_t)bitmap_bytes);
|
||||
|
||||
pmm_total_pages = 0;
|
||||
pmm_free_pages = 0;
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Pass 2: count RAM pages (tracked universe)
|
||||
* ------------------------------------------------------------ */
|
||||
for (UINTN i = 0; i < entry_count; ++i) {
|
||||
EFI_MEMORY_DESCRIPTOR *desc =
|
||||
(EFI_MEMORY_DESCRIPTOR *)((uint8_t *)map + i * desc_size);
|
||||
|
||||
if (!is_ram_type(desc->Type))
|
||||
continue;
|
||||
|
||||
uint64_t start_page = desc->PhysicalStart / PMM_PAGE_SIZE;
|
||||
uint64_t end_page = start_page + desc->NumberOfPages;
|
||||
|
||||
if (start_page >= pmm_tracked_pages)
|
||||
continue;
|
||||
|
||||
if (end_page > pmm_tracked_pages)
|
||||
end_page = pmm_tracked_pages;
|
||||
|
||||
if (end_page > start_page)
|
||||
pmm_total_pages += (end_page - start_page);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Pass 3: free usable memory (respect allocation floor)
|
||||
* ------------------------------------------------------------ */
|
||||
for (UINTN i = 0; i < entry_count; ++i) {
|
||||
EFI_MEMORY_DESCRIPTOR *desc =
|
||||
(EFI_MEMORY_DESCRIPTOR *)((uint8_t *)map + i * desc_size);
|
||||
|
||||
if (!is_usable_memory(desc->Type))
|
||||
continue;
|
||||
|
||||
uint64_t start_page = desc->PhysicalStart / PMM_PAGE_SIZE;
|
||||
uint64_t end_page = start_page + desc->NumberOfPages;
|
||||
|
||||
if (start_page >= pmm_tracked_pages)
|
||||
continue;
|
||||
|
||||
if (end_page > pmm_tracked_pages)
|
||||
end_page = pmm_tracked_pages;
|
||||
|
||||
if (end_page <= pmm_alloc_floor_page)
|
||||
continue;
|
||||
|
||||
if (start_page < pmm_alloc_floor_page)
|
||||
start_page = pmm_alloc_floor_page;
|
||||
|
||||
if (end_page > start_page) {
|
||||
uint64_t pages = end_page - start_page;
|
||||
bitmap_mark_range(start_page, pages, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Reserve low memory (defensive)
|
||||
* ------------------------------------------------------------ */
|
||||
if (pmm_alloc_floor_page > 0) {
|
||||
bitmap_mark_range(0, pmm_alloc_floor_page, 1);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Reserve kernel image (page-aligned, physical)
|
||||
* ------------------------------------------------------------ */
|
||||
uint64_t kstart = (uint64_t)__kernel_start;
|
||||
uint64_t kend = (uint64_t)__kernel_end;
|
||||
|
||||
uint64_t kstart_page = kstart / PMM_PAGE_SIZE;
|
||||
uint64_t kend_page =
|
||||
(kend + PMM_PAGE_SIZE - 1) / PMM_PAGE_SIZE;
|
||||
|
||||
if (kend_page > kstart_page) {
|
||||
uint64_t kpages = kend_page - kstart_page;
|
||||
bitmap_mark_range(kstart_page, kpages, 1);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Keep page 0 reserved (NULL guard)
|
||||
* ------------------------------------------------------------ */
|
||||
bitmap_mark_range(0, 1, 1);
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Reserve the UEFI memory map buffer itself (BootInfo memory_map)
|
||||
* ------------------------------------------------------------ */
|
||||
if (boot_info->memory_map && boot_info->memory_map_size > 0) {
|
||||
uint64_t mm_phys = (uint64_t)(uintptr_t)boot_info->memory_map;
|
||||
uint64_t mm_pages = pmm_bytes_to_pages(boot_info->memory_map_size);
|
||||
uint64_t mm_start_page = mm_phys / PMM_PAGE_SIZE;
|
||||
bitmap_mark_range(mm_start_page, mm_pages, 1);
|
||||
}
|
||||
|
||||
recompute_free_pages();
|
||||
|
||||
pmm_initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pmm_is_initialized(void) {
|
||||
return pmm_initialized;
|
||||
}
|
||||
|
||||
uint64_t pmm_alloc_page(void) {
|
||||
if (!pmm_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t start = find_contiguous_free(1);
|
||||
if (start == UINT64_MAX) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bitmap_mark_range(start, 1, 1);
|
||||
return start * (uint64_t)PMM_PAGE_SIZE;
|
||||
}
|
||||
|
||||
uint64_t pmm_alloc_contiguous(uint64_t num_pages) {
|
||||
if (!pmm_initialized || num_pages == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t start = find_contiguous_free(num_pages);
|
||||
if (start == UINT64_MAX) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bitmap_mark_range(start, num_pages, 1);
|
||||
return start * (uint64_t)PMM_PAGE_SIZE;
|
||||
}
|
||||
|
||||
void pmm_free_page(uint64_t paddr) {
|
||||
if (!pmm_initialized || (paddr % PMM_PAGE_SIZE) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
pmm_free_contiguous(paddr, 1);
|
||||
}
|
||||
|
||||
void pmm_free_contiguous(uint64_t paddr, uint64_t num_pages) {
|
||||
if (!pmm_initialized || (paddr % PMM_PAGE_SIZE) != 0 || num_pages == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t start_page = paddr / PMM_PAGE_SIZE;
|
||||
if (start_page >= pmm_tracked_pages) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (start_page + num_pages > pmm_tracked_pages) {
|
||||
num_pages = pmm_tracked_pages - start_page;
|
||||
}
|
||||
|
||||
bitmap_mark_range(start_page, num_pages, 0);
|
||||
}
|
||||
|
||||
pmm_stats_t pmm_get_stats(void) {
|
||||
pmm_stats_t stats = {0};
|
||||
stats.total_pages = pmm_total_pages;
|
||||
stats.free_pages = pmm_free_pages;
|
||||
stats.used_pages = (pmm_total_pages > pmm_free_pages) ? (pmm_total_pages - pmm_free_pages) : 0;
|
||||
stats.total_bytes = stats.total_pages * (uint64_t)PMM_PAGE_SIZE;
|
||||
stats.used_bytes = stats.used_pages * (uint64_t)PMM_PAGE_SIZE;
|
||||
stats.free_bytes = stats.free_pages * (uint64_t)PMM_PAGE_SIZE;
|
||||
return stats;
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* vmm.c - 4-level paging Virtual Memory Manager (x86_64)
|
||||
*
|
||||
* Minimal bring-up: identity maps the first 16 MiB, supports single-page
|
||||
* map/unmap/query, and switches to the new page table root.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "vmm.h"
|
||||
#include "pmm.h"
|
||||
#include "console.h"
|
||||
#include "uefi.h"
|
||||
|
||||
#define PTE_PRESENT (1ull << 0)
|
||||
#define PTE_WRITABLE (1ull << 1)
|
||||
#define PTE_USER (1ull << 2)
|
||||
#define PTE_PWT (1ull << 3)
|
||||
#define PTE_PCD (1ull << 4)
|
||||
#define PTE_ACCESSED (1ull << 5)
|
||||
#define PTE_DIRTY (1ull << 6)
|
||||
#define PTE_PS (1ull << 7)
|
||||
#define PTE_GLOBAL (1ull << 8)
|
||||
#define PTE_NX (1ull << 63)
|
||||
|
||||
#define ADDR_MASK 0x000FFFFFFFFFF000ull
|
||||
|
||||
static uint64_t vmm_root_pml4_phys = 0;
|
||||
static const uint64_t lapic_phys_base = 0xFEE00000ull;
|
||||
static const uint64_t hpet_phys_base = 0xFED00000ull;
|
||||
|
||||
static int is_ram_type(uint32_t type) {
|
||||
return type == EfiConventionalMemory ||
|
||||
type == EfiLoaderCode ||
|
||||
type == EfiLoaderData ||
|
||||
type == EfiBootServicesCode ||
|
||||
type == EfiBootServicesData ||
|
||||
type == EfiRuntimeServicesCode ||
|
||||
type == EfiRuntimeServicesData ||
|
||||
type == EfiACPIReclaimMemory ||
|
||||
type == EfiACPIMemoryNVS;
|
||||
}
|
||||
|
||||
static inline uint64_t *paddr_to_virt(uint64_t paddr) {
|
||||
return (uint64_t *)(uintptr_t)paddr;
|
||||
}
|
||||
|
||||
static uint64_t alloc_page_zeroed(void) {
|
||||
uint64_t paddr = pmm_alloc_page();
|
||||
if (paddr == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t *ptr = paddr_to_virt(paddr);
|
||||
for (size_t i = 0; i < (VMM_PAGE_SIZE / sizeof(uint64_t)); ++i) {
|
||||
ptr[i] = 0;
|
||||
}
|
||||
return paddr;
|
||||
}
|
||||
|
||||
static inline uint16_t pml4_index(uint64_t vaddr) { return (uint16_t)((vaddr >> 39) & 0x1FF); }
|
||||
static inline uint16_t pdpt_index(uint64_t vaddr) { return (uint16_t)((vaddr >> 30) & 0x1FF); }
|
||||
static inline uint16_t pd_index(uint64_t vaddr) { return (uint16_t)((vaddr >> 21) & 0x1FF); }
|
||||
static inline uint16_t pt_index(uint64_t vaddr) { return (uint16_t)((vaddr >> 12) & 0x1FF); }
|
||||
|
||||
static uint64_t *get_or_alloc_table(uint64_t *parent, uint16_t idx) {
|
||||
uint64_t entry = parent[idx];
|
||||
if (entry & PTE_PRESENT) {
|
||||
return paddr_to_virt(entry & ADDR_MASK);
|
||||
}
|
||||
|
||||
uint64_t child_phys = alloc_page_zeroed();
|
||||
if (child_phys == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parent[idx] = child_phys | PTE_PRESENT | PTE_WRITABLE;
|
||||
return paddr_to_virt(child_phys);
|
||||
}
|
||||
|
||||
static uint64_t *get_table(uint64_t *parent, uint16_t idx) {
|
||||
uint64_t entry = parent[idx];
|
||||
if (entry & PTE_PRESENT) {
|
||||
return paddr_to_virt(entry & ADDR_MASK);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint64_t make_pte(uint64_t paddr, uint64_t flags) {
|
||||
uint64_t pte = paddr & ADDR_MASK;
|
||||
if (flags & VMM_FLAG_PRESENT) pte |= PTE_PRESENT;
|
||||
if (flags & VMM_FLAG_WRITABLE) pte |= PTE_WRITABLE;
|
||||
if (flags & VMM_FLAG_USER) pte |= PTE_USER;
|
||||
if (flags & VMM_FLAG_CACHE_DISABLE) pte |= PTE_PCD;
|
||||
if (!(flags & VMM_FLAG_NX)) pte &= ~PTE_NX;
|
||||
else pte |= PTE_NX;
|
||||
return pte;
|
||||
}
|
||||
|
||||
static inline void invlpg(uint64_t vaddr) {
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
__asm__ volatile ("invlpg (%0)" :: "r"(vaddr) : "memory");
|
||||
#else
|
||||
(void)vaddr;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void load_cr3(uint64_t pml4_phys) {
|
||||
#if defined(__x86_64__)
|
||||
__asm__ volatile("mov %0, %%cr3" :: "r"(pml4_phys) : "memory");
|
||||
#else
|
||||
(void)pml4_phys;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint64_t *resolve_pt(uint64_t vaddr, uint16_t *idx_out) {
|
||||
if (vmm_root_pml4_phys == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t *pml4 = paddr_to_virt(vmm_root_pml4_phys);
|
||||
uint64_t *pdpt = get_table(pml4, pml4_index(vaddr));
|
||||
if (!pdpt) return NULL;
|
||||
|
||||
uint64_t *pd = get_table(pdpt, pdpt_index(vaddr));
|
||||
if (!pd) return NULL;
|
||||
|
||||
uint64_t *pt = get_table(pd, pd_index(vaddr));
|
||||
if (!pt) return NULL;
|
||||
|
||||
if (idx_out) {
|
||||
*idx_out = pt_index(vaddr);
|
||||
}
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
int vmm_map_page(uint64_t vaddr, uint64_t paddr, uint64_t flags) {
|
||||
if (vmm_root_pml4_phys == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t *pml4 = paddr_to_virt(vmm_root_pml4_phys);
|
||||
uint64_t *pdpt = get_or_alloc_table(pml4, pml4_index(vaddr));
|
||||
if (!pdpt) return -1;
|
||||
|
||||
uint64_t *pd = get_or_alloc_table(pdpt, pdpt_index(vaddr));
|
||||
if (!pd) return -1;
|
||||
|
||||
uint64_t *pt = get_or_alloc_table(pd, pd_index(vaddr));
|
||||
if (!pt) return -1;
|
||||
|
||||
uint16_t idx = pt_index(vaddr);
|
||||
if (pt[idx] & PTE_PRESENT) {
|
||||
uint64_t existing_pa = pt[idx] & ADDR_MASK;
|
||||
uint64_t new_pte = make_pte(paddr, flags | VMM_FLAG_PRESENT);
|
||||
/* Idempotent mapping: same PA + compatible flags => success */
|
||||
if (existing_pa == (paddr & ADDR_MASK)) {
|
||||
/* Require WRITABLE compatibility (no silent downgrade/upgrade) */
|
||||
int was_writable = (pt[idx] & PTE_WRITABLE) != 0;
|
||||
int want_writable = (new_pte & PTE_WRITABLE) != 0;
|
||||
if (was_writable == want_writable) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
console_println("VMM map conflict");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pt[idx] = make_pte(paddr, flags | VMM_FLAG_PRESENT);
|
||||
invlpg(vaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vmm_unmap_page(uint64_t vaddr) {
|
||||
if (vmm_root_pml4_phys == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t *pml4 = paddr_to_virt(vmm_root_pml4_phys);
|
||||
uint64_t *pdpt = get_table(pml4, pml4_index(vaddr));
|
||||
if (!pdpt) return -1;
|
||||
|
||||
uint64_t *pd = get_table(pdpt, pdpt_index(vaddr));
|
||||
if (!pd) return -1;
|
||||
|
||||
uint64_t *pt = get_table(pd, pd_index(vaddr));
|
||||
if (!pt) return -1;
|
||||
|
||||
uint16_t idx = pt_index(vaddr);
|
||||
if (!(pt[idx] & PTE_PRESENT)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pt[idx] = 0;
|
||||
invlpg(vaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t vmm_get_paddr(uint64_t vaddr) {
|
||||
if (vmm_root_pml4_phys == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t *pml4 = paddr_to_virt(vmm_root_pml4_phys);
|
||||
uint64_t *pdpt = get_table(pml4, pml4_index(vaddr));
|
||||
if (!pdpt) return 0;
|
||||
|
||||
uint64_t *pd = get_table(pdpt, pdpt_index(vaddr));
|
||||
if (!pd) return 0;
|
||||
|
||||
uint64_t *pt = get_table(pd, pd_index(vaddr));
|
||||
if (!pt) return 0;
|
||||
|
||||
uint16_t idx = pt_index(vaddr);
|
||||
if (!(pt[idx] & PTE_PRESENT)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (pt[idx] & ADDR_MASK) | (vaddr & 0xFFF);
|
||||
}
|
||||
|
||||
int vmm_map_range(uint64_t vaddr, uint64_t paddr, uint64_t size, uint64_t flags) {
|
||||
uint64_t pages = (size + VMM_PAGE_SIZE - 1) / VMM_PAGE_SIZE;
|
||||
for (uint64_t i = 0; i < pages; ++i) {
|
||||
if (vmm_map_page(vaddr + i * VMM_PAGE_SIZE, paddr + i * VMM_PAGE_SIZE, flags) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vmm_query_page(uint64_t vaddr, vmm_page_info_t *info) {
|
||||
if (info) {
|
||||
info->present = 0;
|
||||
info->writable = 0;
|
||||
info->executable = 0;
|
||||
}
|
||||
|
||||
uint16_t idx = 0;
|
||||
uint64_t *pt = resolve_pt(vaddr, &idx);
|
||||
if (!pt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t entry = pt[idx];
|
||||
if (!(entry & PTE_PRESENT)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (info) {
|
||||
info->present = 1;
|
||||
info->writable = (entry & PTE_WRITABLE) ? 1 : 0;
|
||||
info->executable = (entry & PTE_NX) ? 0 : 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vmm_init(BootInfo *boot_info) {
|
||||
if (!pmm_is_initialized()) {
|
||||
console_println("VMM init failed: PMM not ready");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!boot_info || !boot_info->memory_map || boot_info->memory_map_descriptor_size == 0) {
|
||||
console_println("VMM init failed: missing BootInfo");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vmm_root_pml4_phys = alloc_page_zeroed();
|
||||
if (vmm_root_pml4_phys == 0) {
|
||||
console_println("VMM init failed: no memory for PML4");
|
||||
return -1;
|
||||
}
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR *map = boot_info->memory_map;
|
||||
UINTN map_size = boot_info->memory_map_size;
|
||||
UINTN desc_size = boot_info->memory_map_descriptor_size;
|
||||
UINTN entry_count = map_size / desc_size;
|
||||
|
||||
for (UINTN i = 0; i < entry_count; ++i) {
|
||||
EFI_MEMORY_DESCRIPTOR *desc = (EFI_MEMORY_DESCRIPTOR *)((uint8_t *)map + i * desc_size);
|
||||
if (!is_ram_type(desc->Type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t start = desc->PhysicalStart;
|
||||
uint64_t size = desc->NumberOfPages * VMM_PAGE_SIZE;
|
||||
|
||||
if (vmm_map_range(start, start, size, VMM_FLAG_WRITABLE) != 0) {
|
||||
console_println("VMM init failed: mapping RAM region");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Map LAPIC and HPET identity — cache-disabled (PCD) as required for MMIO */
|
||||
if (vmm_map_range(lapic_phys_base, lapic_phys_base, VMM_PAGE_SIZE,
|
||||
VMM_FLAG_WRITABLE | VMM_FLAG_CACHE_DISABLE) != 0) {
|
||||
console_println("VMM init failed: map LAPIC MMIO");
|
||||
return -1;
|
||||
}
|
||||
if (vmm_map_range(hpet_phys_base, hpet_phys_base, VMM_PAGE_SIZE,
|
||||
VMM_FLAG_WRITABLE | VMM_FLAG_CACHE_DISABLE) != 0) {
|
||||
console_println("VMM init failed: map HPET MMIO");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Map GOP framebuffer identity — MMIO not covered by the RAM walk above.
|
||||
* QEMU's emulated GOP linear framebuffer sits in a reserved memory range
|
||||
* (EfiMemoryMappedIO) that is excluded from the RAM walk. Map it WB here
|
||||
* so the VT100 renderer can write to it after load_cr3(). CACHE_DISABLE
|
||||
* is omitted deliberately: QEMU's VGA emulation is coherent and UC- mapping
|
||||
* causes #GP on some PAT/MTRR combinations. */
|
||||
if (boot_info->framebuffer.base != NULL && boot_info->framebuffer.size > 0) {
|
||||
uint64_t fb_phys = (uint64_t)(uintptr_t)boot_info->framebuffer.base;
|
||||
uint64_t fb_size = (uint64_t)boot_info->framebuffer.size;
|
||||
if (vmm_map_range(fb_phys, fb_phys, fb_size, VMM_FLAG_WRITABLE) != 0) {
|
||||
console_println("VMM init failed: map GOP framebuffer");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
load_cr3(vmm_root_pml4_phys);
|
||||
console_println("VMM initialized (mapped RAM, CR3 switched)");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user