From 4456e23f7c484490a88b454bbc494c1819db20ee Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Sun, 10 Aug 2025 19:01:26 -0400 Subject: [PATCH 01/10] aarch64: Add core state structure for vCPU emulation Introduce the basic data structures required to manage the architectural state of an emulated ARMv8 guest. This is a foundational patch for a forthcoming emulator framework. The core of this change is the `vcpu_state_t` structure, which holds the essential user-visible state of a single virtual CPU (vCPU), including the general-purpose registers, stack pointer, program counter, and PSTATE. The state for all vCPUs is aligned to the CPU L1 cache line. This design choice ensures that there is no false sharing between physical host cores running separate vCPU emulation threads. Signed-off-by: Ronald Caesar --- core/aarch64/isa.cpp | 5 ++++- core/aarch64/isa.h | 44 +++++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/core/aarch64/isa.cpp b/core/aarch64/isa.cpp index ed4ef51..791e31b 100755 --- a/core/aarch64/isa.cpp +++ b/core/aarch64/isa.cpp @@ -3,6 +3,9 @@ void cpuTest() { + aarch64::vcpu_state_t vcpu_states[CPU_CORES] = {}; + + // Outdated Code CPU cpu; cpu.pc = 0; @@ -14,4 +17,4 @@ void cpuTest() LOG_INFO(ARM, "{}", cpu.read_byte(0)); cpu.print_debug_information(); -} \ No newline at end of file +} diff --git a/core/aarch64/isa.h b/core/aarch64/isa.h index 1e5236d..0d75500 100644 --- a/core/aarch64/isa.h +++ b/core/aarch64/isa.h @@ -3,38 +3,40 @@ #pragma once #include +#include #include "Base/Logging/Log.h" namespace aarch64 { -#define GPR_REGISTERS 32 -#define ZERO_REGISTER_INDEX 31 +/* AArch64 R0-R30 */ +#define GP_REGISTERS 31 +#define CACHE_LINE_SIZE 64 +#define CPU_CORES 8 -#define FPR_REGISTERS 32 - -typedef struct +/* + * vcpu_state_t - Holds the architectural state for an emulated vCPU. + * @r: General purpose registers R0-R30. + * @pc: Program Counter. + * @sp: Stack Pointer. + * @pstate: Process State Register (NZCV flags, EL, etc.). + * + * This structure is aligned to the L1 cache line size to prevent false + * sharing when multiple host threads are emulating vCPUs on different + * physical cores. + */ +typedef struct alignas(CACHE_LINE_SIZE) { - uint64_t gpr[GPR_REGISTERS]; - unsigned __int128 fpr[FPR_REGISTERS]; + uint64_t r[GP_REGISTERS]; uint64_t pc; uint64_t sp; -} isa_t; - -uint64_t read_X(uint64_t* registers, size_t n); -void adr(uint64_t* registers, size_t n, uint64_t pc, uint64_t offset); -//========================================================= -// Access Floating Point Registers -//========================================================= - -uint8_t B(unsigned __int128 registers, size_t n); -uint16_t H(unsigned __int128 registers, size_t n); -uint32_t S(unsigned __int128 registers, size_t n); -uint64_t D(unsigned __int128 registers, size_t n); -unsigned __int128 Q(unsigned __int128 registers, size_t n); - + uint32_t pstate; +} vcpu_state_t; } // namespace aarch64 +//========================================================= +// OUTDATED CODE +//========================================================= struct CPU { u64 regs[31] = {0}; // X0–X30 From f15417802df3e59f108015c21fe122896051a4b5 Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Tue, 12 Aug 2025 06:05:31 -0400 Subject: [PATCH 02/10] aarch64: Correct vCPU register state and add FP/SIMD support The initial vCPU state for AArch64 had a couple of architectural inaccuracies that this commit corrects. First, AArch64 has 32 general-purpose registers (X0-X31), not 31. The stack pointer (SP) is not a separate special-purpose register but is an alias for register X31. The dedicated `sp` field in vcpu_state_t was therefore redundant and architecturally incorrect. This change increases GP_REGISTERS to 32 and removes the separate `sp` field. The SP should be managed via `r[31]`. Second, to support floating-point and SIMD instructions, the vCPU state must include the vector registers. This adds the definitions and storage for the 32 128-bit FP/SIMD registers (V0-V31). Signed-off-by: Ronald Caesar --- core/aarch64/isa.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/aarch64/isa.h b/core/aarch64/isa.h index 0d75500..85b77d2 100644 --- a/core/aarch64/isa.h +++ b/core/aarch64/isa.h @@ -9,16 +9,20 @@ namespace aarch64 { -/* AArch64 R0-R30 */ -#define GP_REGISTERS 31 +/* AArch64 R0-R31 */ +#define GP_REGISTERS 32 + +/* AArch64 V0-V31 */ +#define FP_REGISTERS 32 + #define CACHE_LINE_SIZE 64 #define CPU_CORES 8 /* * vcpu_state_t - Holds the architectural state for an emulated vCPU. - * @r: General purpose registers R0-R30. + * @v: The 128-bit vector registers V0-V31. + * @r: General purpose registers R0-R31. * @pc: Program Counter. - * @sp: Stack Pointer. * @pstate: Process State Register (NZCV flags, EL, etc.). * * This structure is aligned to the L1 cache line size to prevent false @@ -27,9 +31,9 @@ namespace aarch64 */ typedef struct alignas(CACHE_LINE_SIZE) { + unsigned __int128 v[FP_REGISTERS]; uint64_t r[GP_REGISTERS]; uint64_t pc; - uint64_t sp; uint32_t pstate; } vcpu_state_t; } // namespace aarch64 From 55af4bebdabeba2c6a4acfbc7bb9f01ffbb8845e Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Wed, 13 Aug 2025 15:00:48 -0400 Subject: [PATCH 03/10] aarch64/mem: Add fast GPA-to-HVA translation helper In emulating guests with a simple, flat memory model, we frequently need to translate a guest physical address (GPA) into a host virtual address (HVA). This is a hot path operation that must be efficient as possible. This commit introduces gpa_to_hva(), a static inline helper function designed for this purpose. The implementation relies on the fundamental pre-condition that the guest's physical RAM is backed by a single, contiguous region of host virtual memory (typically acquired via mmap). It treats the GPA not as a pointer but as a direct byte offset from the base of this host mapping. This approach is optimal for performance for two key reasons: 1. The translation is a single pointer-offset calculation, which typically compiles to a single LEA intruction on x86-64. 2. It preserves memory access locality. When a guest performs sequential accesses, the host's accesses are also sequential, allowing the host CPU's hardware prefetcher to function effectively. This helper provides the fast path for simple RAM accesses. More complex address spaces involving discontiguous memory or MMIO regions will require a slower, lookup-based translation mechanism. This function is not intended for those cases. Signed-off-by: Ronald Caesar --- core/aarch64/isa.cpp | 16 ++++++++++++++++ core/aarch64/isa.h | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) mode change 100755 => 100644 core/aarch64/isa.cpp diff --git a/core/aarch64/isa.cpp b/core/aarch64/isa.cpp old mode 100755 new mode 100644 index 791e31b..50a1aaf --- a/core/aarch64/isa.cpp +++ b/core/aarch64/isa.cpp @@ -1,9 +1,25 @@ #include "isa.h" #include "Base/Assert.h" +#include "memory/arena.h" + +static inline void* aarch64::memory::gpa_to_hva(aarch64::memory::guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT(gpa < memory->size); + void* hva = memory->base + gpa; + return hva; +} void cpuTest() { aarch64::vcpu_state_t vcpu_states[CPU_CORES] = {}; + memory::arena_t guest_memory_arena = memory::arena_init(GUEST_RAM_SIZE); + ASSERT(nullptr != guest_memory_arena.data); + + aarch64::memory::guest_memory_t guest_ram = {}; + guest_ram.base = guest_memory_arena.data; + guest_ram.size = guest_memory_arena.capacity; // Outdated Code CPU cpu; diff --git a/core/aarch64/isa.h b/core/aarch64/isa.h index 85b77d2..57bb237 100644 --- a/core/aarch64/isa.h +++ b/core/aarch64/isa.h @@ -2,8 +2,8 @@ #pragma once -#include #include +#include #include "Base/Logging/Log.h" @@ -16,6 +16,7 @@ namespace aarch64 #define FP_REGISTERS 32 #define CACHE_LINE_SIZE 64 +#define GUEST_RAM_SIZE 10240 // 10KiB #define CPU_CORES 8 /* @@ -36,6 +37,45 @@ typedef struct alignas(CACHE_LINE_SIZE) uint64_t pc; uint32_t pstate; } vcpu_state_t; + +namespace memory +{ +/* + * guest_memory_t - Describes a contiguous block of guest physical RAM. + * @base: Pointer to the start of the host-allocated memory block. + * @size: The size of the memory block in bytes. + */ +typedef struct +{ + void* base; + uint64_t size; +} guest_memory_t; + +/* + * gpa_to_hva() - Translate a Guest Physical Address to a Host Virtual Address. + * @memory: The guest memory region to translate within. + * @gpa: The Guest Physical Address (offset) to translate. + * + * This function provides a fast, direct translation for a flat guest memory + * model. It relies on the critical pre-condition that the guest's physical + * RAM is backed by a single, contiguous block of virtual memory in the host's + * userspace (typically allocated with mmap()). + * + * In this model, memory->base is the Host Virtual Address (HVA) of the start of + * the backing host memory. The provided Guest Physical Address (gpa) is not + * treated as a pointer, but as a simple byte offset from the start of the guest's + * physical address space (PAS). + * + * The translatiom is therefore a single pointer-offset calculation. This establishes + * a direct 1:1 mapping between the guest's PAS and the host's virtual memory block. + * + * The function asserts that GPA is within bounds. The caller is responsible for + * ensuring the validity of the GPA prior to calling. + * + * Return: A valid host virtual address pointer corresponding to the GPA. + */ +static inline void* gpa_to_hva(guest_memory_t* memory, uint64_t gpa); +} // namespace memory } // namespace aarch64 //========================================================= From c550b00ddeec80f94666d03372b89d5cdab6f416 Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Wed, 13 Aug 2025 15:40:01 -0400 Subject: [PATCH 04/10] aarch64/mem: Fixed pointer arithmatic warning Signed-off-by: Ronald Caesar --- core/aarch64/isa.cpp | 6 +++--- core/aarch64/isa.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/aarch64/isa.cpp b/core/aarch64/isa.cpp index 50a1aaf..05814a9 100644 --- a/core/aarch64/isa.cpp +++ b/core/aarch64/isa.cpp @@ -2,12 +2,12 @@ #include "Base/Assert.h" #include "memory/arena.h" -static inline void* aarch64::memory::gpa_to_hva(aarch64::memory::guest_memory_t* memory, uint64_t gpa) +static inline uint8_t* aarch64::memory::gpa_to_hva(aarch64::memory::guest_memory_t* memory, uint64_t gpa) { ASSERT(nullptr != memory); ASSERT(nullptr != memory->base); ASSERT(gpa < memory->size); - void* hva = memory->base + gpa; + uint8_t* hva = memory->base + gpa; return hva; } @@ -18,7 +18,7 @@ void cpuTest() ASSERT(nullptr != guest_memory_arena.data); aarch64::memory::guest_memory_t guest_ram = {}; - guest_ram.base = guest_memory_arena.data; + guest_ram.base = static_cast(guest_memory_arena.data); guest_ram.size = guest_memory_arena.capacity; // Outdated Code diff --git a/core/aarch64/isa.h b/core/aarch64/isa.h index 57bb237..ed7a15d 100644 --- a/core/aarch64/isa.h +++ b/core/aarch64/isa.h @@ -47,7 +47,7 @@ namespace memory */ typedef struct { - void* base; + uint8_t* base; uint64_t size; } guest_memory_t; @@ -74,7 +74,7 @@ typedef struct * * Return: A valid host virtual address pointer corresponding to the GPA. */ -static inline void* gpa_to_hva(guest_memory_t* memory, uint64_t gpa); +static inline uint8_t* gpa_to_hva(guest_memory_t* memory, uint64_t gpa); } // namespace memory } // namespace aarch64 From 883d676dd08cc206b28ef6e72b67546e0ffb37d3 Mon Sep 17 00:00:00 2001 From: Sinan Karakaya Date: Thu, 14 Aug 2025 02:01:17 +0200 Subject: [PATCH 05/10] feat(aarch64): added system registers to vcpu_state_t --- core/aarch64/isa.h | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/core/aarch64/isa.h b/core/aarch64/isa.h index ed7a15d..ea3fb72 100644 --- a/core/aarch64/isa.h +++ b/core/aarch64/isa.h @@ -20,15 +20,20 @@ namespace aarch64 #define CPU_CORES 8 /* - * vcpu_state_t - Holds the architectural state for an emulated vCPU. - * @v: The 128-bit vector registers V0-V31. - * @r: General purpose registers R0-R31. + * vcpu_state_t - Holds the architectural and selected system-register state for an emulated vCPU. + * @v: 128-bit SIMD/FP vector registers V0–V31. + * @r: General-purpose registers X0–X31 (X31 as SP/ZR as appropriate). * @pc: Program Counter. - * @pstate: Process State Register (NZCV flags, EL, etc.). + * @pstate: Process State Register (NZCV, DAIF, EL, etc.). * - * This structure is aligned to the L1 cache line size to prevent false - * sharing when multiple host threads are emulating vCPUs on different - * physical cores. + * System registers (subset mirrored for fast-path emulation at EL0): + * - ctr_el0, dczid_el0: Cache/type identification. + * - tpidrro_el0, tpidr_el0: Thread pointers (host-mapped TLS pointers). + * - cntfrq_el0, cntpct_el0, cntvct_el0, cntv_ctl_el0, cntv_cval_el0: Generic timers/counters. + * - pmccntr_el0, pmcr_el0: PMU cycle counter and control. + * + * This structure is aligned to the L1 cache line size to prevent false sharing + * when multiple host threads are emulating vCPUs on different physical cores. */ typedef struct alignas(CACHE_LINE_SIZE) { @@ -36,6 +41,25 @@ typedef struct alignas(CACHE_LINE_SIZE) uint64_t r[GP_REGISTERS]; uint64_t pc; uint32_t pstate; + + // ========================= System Registers ================================== + // Basics + uint32_t ctr_elo; // cache-type register + uint32_t dczid_elo; // data cache zero-ID + const uint64_t* tpidrro_e10; // thread pointer ID register, read-only + uint64_t* tpidr_e10; // thread pointer ID register + + // Counters + uint64_t cntfreq_elo; // counter frequency + uint64_t cntvct_el0; // Virtual counter - CRITICAL for timing + uint64_t cntpct_el0; // Physical counter + uint32_t cntv_ctl_el0; // Virtual timer control + uint64_t cntv_cval_el0; // Virtual timer compare value + + // Performance monitoring (if games use them): + uint64_t pmccntr_el0; // Cycle counter + uint32_t pmcr_el0; // Performance monitor control + // ============================================================================= } vcpu_state_t; namespace memory From e7b534998021f2d63dfecd457d2fee48c3651eb7 Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Thu, 14 Aug 2025 19:56:21 -0400 Subject: [PATCH 06/10] aarch64/mem: Introduce a dedicated guest memory access layer This commit introduces a proper abstraction layer for all read and write operations. The previous approach of directly calculating a Host Virtual Address (HVA) from a Guest Physical Address (GPA) via gpa_to_hva() forces every part of the emulator that touches guest memory to be aware of the underlying host pointer, which is poor design. This new layer introduces a suite of guest_mem_read{b,w,l,q} and guest_mem_write{b,w,l,q} fuctions. All future memory accesses from the emulated CPU should be performed through these functions. The code has also been moved into the pound::aarch64 namespace for better organization. Signed-off-by: Ronald Caesar --- core/Base/Logging/Filter.cpp | 1 + core/Base/Logging/LogTypes.h | 1 + core/aarch64/isa.cpp | 217 ++++++++++++++++++++++++++++++++--- core/aarch64/isa.h | 89 ++++++++++++-- gui/panels.cpp | 2 +- 5 files changed, 286 insertions(+), 24 deletions(-) diff --git a/core/Base/Logging/Filter.cpp b/core/Base/Logging/Filter.cpp index cdb6913..b0c2e0c 100644 --- a/core/Base/Logging/Filter.cpp +++ b/core/Base/Logging/Filter.cpp @@ -68,6 +68,7 @@ bool ParseFilterRule(Filter &instance, Iterator begin, Iterator end) { CLS(System) \ CLS(Render) \ CLS(ARM) \ + CLS(Memory) \ // GetClassName is a macro defined by Windows.h, grrr... const char* GetLogClassName(Class logClass) { diff --git a/core/Base/Logging/LogTypes.h b/core/Base/Logging/LogTypes.h index c16f36b..fc99703 100644 --- a/core/Base/Logging/LogTypes.h +++ b/core/Base/Logging/LogTypes.h @@ -34,6 +34,7 @@ enum class Class : const u8 { System, // Base System messages Render, // OpenGL and Window messages ARM, + Memory, Count // Total number of logging classes }; diff --git a/core/aarch64/isa.cpp b/core/aarch64/isa.cpp index 05814a9..2ece02a 100644 --- a/core/aarch64/isa.cpp +++ b/core/aarch64/isa.cpp @@ -2,7 +2,11 @@ #include "Base/Assert.h" #include "memory/arena.h" -static inline uint8_t* aarch64::memory::gpa_to_hva(aarch64::memory::guest_memory_t* memory, uint64_t gpa) +// TODO(GloriousTacoo:aarch64) Implement big to little endian conversion for guest_mem read and write functions. + +namespace pound::aarch64 +{ +static inline uint8_t* gpa_to_hva(guest_memory_t* memory, uint64_t gpa) { ASSERT(nullptr != memory); ASSERT(nullptr != memory->base); @@ -11,26 +15,209 @@ static inline uint8_t* aarch64::memory::gpa_to_hva(aarch64::memory::guest_memory return hva; } +/* + * ============================================================================ + * Guest Memory Read Functions + * ============================================================================ + */ + +static inline uint8_t guest_mem_readb(guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT(gpa <= memory->size); + uint8_t* hva = gpa_to_hva(memory, gpa); + return *hva; +} + +static inline uint16_t guest_mem_readw(guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint16_t)) <= memory->size); + // Check if gpa is aligned to 2 bytes. + ASSERT((gpa & 1) == 0); + uint16_t* hva = (uint16_t*)gpa_to_hva(memory, gpa); + return *hva; +} + +static inline uint32_t guest_mem_readl(guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint32_t)) <= memory->size); + // Check if gpa is aligned to 4 bytes. + ASSERT((gpa & 3) == 0); + uint32_t* hva = (uint32_t*)gpa_to_hva(memory, gpa); + return *hva; +} + +static inline uint64_t guest_mem_readq(guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint64_t)) <= memory->size); + // Check if gpa is aligned to 8 bytes. + ASSERT((gpa & 7) == 0); + uint64_t* hva = (uint64_t*)gpa_to_hva(memory, gpa); + return *hva; +} + +/* + * ============================================================================ + * Guest Memory Write Functions + * ============================================================================ + */ + +static inline void guest_mem_writeb(guest_memory_t* memory, uint64_t gpa, uint8_t val) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT(gpa <= memory->size); + uint8_t* hva = gpa_to_hva(memory, gpa); + *hva = val; +} + +static inline void guest_mem_writew(guest_memory_t* memory, uint64_t gpa, uint16_t val) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint16_t)) <= memory->size); + // Check if gpa is aligned to 2 bytes. + ASSERT((gpa & 1) == 0); + uint16_t* hva = (uint16_t*)gpa_to_hva(memory, gpa); + *hva = val; +} + +static inline void guest_mem_writel(guest_memory_t* memory, uint64_t gpa, uint32_t val) +{ + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint32_t)) <= memory->size); + // Check if gpa is aligned to 4 bytes. + ASSERT((gpa & 3) == 0); + uint32_t* hva = (uint32_t*)gpa_to_hva(memory, gpa); + *hva = val; +} + +static inline void guest_mem_writeq(guest_memory_t* memory, uint64_t gpa, uint64_t val) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint64_t)) <= memory->size); + // Check if gpa is aligned to 8 bytes. + ASSERT((gpa & 7) == 0); + uint64_t* hva = (uint64_t*)gpa_to_hva(memory, gpa); + *hva = val; +} + +/** THIS FUNCTION WAS MADE WITH AI AND IS CALLED WHEN RUNNING THE CPU TEST FROM THE GUI! + * + * @brief Runs a comprehensive suite of tests on the guest memory access functions using the project's logging system. + * + * This function systematically tests the read and write capabilities of the memory + * subsystem for all standard data sizes (8, 16, 32, and 64 bits). It verifies + * that data written to memory can be correctly read back. + * + * It specifically checks: + * 1. A standard aligned address in the middle of RAM. + * + * @param memory A pointer to an initialized guest_memory_t struct. + * @return true if all tests pass, false otherwise. + */ +bool test_guest_ram_access(guest_memory_t* memory) +{ + LOG_INFO(Memory, "--- [ Starting Guest RAM Access Test ] ---"); + if (memory == nullptr || memory->base == nullptr || memory->size < 4096) + { + LOG_CRITICAL(Memory, "Invalid memory block provided. Cannot run tests."); + return false; + } + + bool all_tests_passed = true; + +#define RUN_TEST(description, condition) \ + do \ + { \ + char log_buffer[256]; \ + if (condition) \ + { \ + snprintf(log_buffer, sizeof(log_buffer), " [TEST] %-45s... [PASS]", description); \ + LOG_INFO(Memory, log_buffer); \ + } \ + else \ + { \ + snprintf(log_buffer, sizeof(log_buffer), " [TEST] %-45s... [FAIL]", description); \ + LOG_ERROR(Memory, log_buffer); \ + all_tests_passed = false; \ + } \ + } while (0) + +#define VERIFY_ACCESS(size, suffix, addr, write_val) \ + do \ + { \ + guest_mem_write##suffix(memory, addr, write_val); \ + uint##size##_t read_val = guest_mem_read##suffix(memory, addr); \ + bool success = (read_val == write_val); \ + RUN_TEST("Write/Read " #size "-bit", success); \ + if (!success) \ + { \ + char error_buffer[256]; \ + snprintf(error_buffer, sizeof(error_buffer), " -> At GPA 0x%016llx, Expected 0x%016llx, Got 0x%016llx", \ + (unsigned long long)addr, (unsigned long long)write_val, (unsigned long long)read_val); \ + LOG_ERROR(Memory, error_buffer); \ + } \ + } while (0) + + // --- 1. Test a typical, aligned address in the middle of memory --- + LOG_INFO(Memory, "[INFO] Testing standard access at a midrange address (GPA 0x1000)..."); + uint64_t test_addr = 0x1000; + VERIFY_ACCESS(8, b, test_addr + 0, 0xA5); + VERIFY_ACCESS(16, w, test_addr + 2, 0xBEEF); + VERIFY_ACCESS(32, l, test_addr + 4, 0xDEADBEEF); + VERIFY_ACCESS(64, q, test_addr + 8, 0xCAFEBABE01234567); + + // --- 2. Test the very beginning of the memory block --- + LOG_INFO(Memory, "[INFO] Testing boundary access at the start of RAM (GPA 0x0)..."); + VERIFY_ACCESS(64, q, 0x0, 0xFEEDFACEDEADBEEF); + + // --- 3. Test the very end of the memory block --- + LOG_INFO(Memory, "[INFO] Testing boundary access at the end of RAM..."); + + uint64_t end_addr_b = memory->size - 1; + uint64_t end_addr_w = (memory->size - 2) & ~1ULL; + uint64_t end_addr_l = (memory->size - 4) & ~3ULL; + uint64_t end_addr_q = (memory->size - 8) & ~7ULL; + + VERIFY_ACCESS(8, b, end_addr_b, 0xFE); + VERIFY_ACCESS(16, w, end_addr_w, 0xFEFE); + VERIFY_ACCESS(32, l, end_addr_l, 0xFEFEFEFE); + VERIFY_ACCESS(64, q, end_addr_q, 0xFEFEFEFEFEFEFEFE); + + // --- 4. Final Verdict --- + LOG_INFO(Memory, "--- [ Guest RAM Access Test Finished ] ---"); + if (all_tests_passed) + { + LOG_INFO(Memory, ">>> Result: ALL TESTS PASSED"); + } + else + { + LOG_ERROR(Memory, ">>> Result: SOME TESTS FAILED"); + } + LOG_INFO(Memory, "----------------------------------------------"); + + return all_tests_passed; +} + void cpuTest() { - aarch64::vcpu_state_t vcpu_states[CPU_CORES] = {}; + vcpu_state_t vcpu_states[CPU_CORES] = {}; memory::arena_t guest_memory_arena = memory::arena_init(GUEST_RAM_SIZE); ASSERT(nullptr != guest_memory_arena.data); - aarch64::memory::guest_memory_t guest_ram = {}; + guest_memory_t guest_ram = {}; guest_ram.base = static_cast(guest_memory_arena.data); guest_ram.size = guest_memory_arena.capacity; - // Outdated Code - CPU cpu; - cpu.pc = 0; - - // Simple ARMv8 program in memory (MOVZ X0, #5; ADD X0, X0, #3; RET) - // These are placeholders; real encoding will be parsed later - cpu.write_byte(0, 0x05); // MOVZ placeholder - cpu.write_byte(4, 0x03); // ADD placeholder - cpu.write_byte(8, 0xFF); // RET placeholder - - LOG_INFO(ARM, "{}", cpu.read_byte(0)); - cpu.print_debug_information(); + (void)test_guest_ram_access(&guest_ram); } +} // namespace pound::aarch64 diff --git a/core/aarch64/isa.h b/core/aarch64/isa.h index ed7a15d..b69afed 100644 --- a/core/aarch64/isa.h +++ b/core/aarch64/isa.h @@ -7,7 +7,7 @@ #include "Base/Logging/Log.h" -namespace aarch64 +namespace pound::aarch64 { /* AArch64 R0-R31 */ #define GP_REGISTERS 32 @@ -38,8 +38,6 @@ typedef struct alignas(CACHE_LINE_SIZE) uint32_t pstate; } vcpu_state_t; -namespace memory -{ /* * guest_memory_t - Describes a contiguous block of guest physical RAM. * @base: Pointer to the start of the host-allocated memory block. @@ -66,7 +64,7 @@ typedef struct * treated as a pointer, but as a simple byte offset from the start of the guest's * physical address space (PAS). * - * The translatiom is therefore a single pointer-offset calculation. This establishes + * The translation is therefore a single pointer-offset calculation. This establishes * a direct 1:1 mapping between the guest's PAS and the host's virtual memory block. * * The function asserts that GPA is within bounds. The caller is responsible for @@ -75,8 +73,85 @@ typedef struct * Return: A valid host virtual address pointer corresponding to the GPA. */ static inline uint8_t* gpa_to_hva(guest_memory_t* memory, uint64_t gpa); -} // namespace memory -} // namespace aarch64 + +/* + * ============================================================================ + * Guest Memory Read Functions + * ============================================================================ + */ + +/** + * guest_mem_readb() - Read one byte from guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to read from. + * Returns the 8-bit value read from memory. + */ +static inline uint8_t guest_mem_readb(guest_memory_t* memory, uint64_t gpa); + +/** + * guest_mem_readw() - Read a 16-bit word from guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to read from (must be 2-byte aligned). + * Returns the 16-bit value, corrected for host endianness. + */ +static inline uint16_t guest_mem_readw(guest_memory_t* memory, uint64_t gpa); + +/** + * guest_mem_readl() - Read a 32-bit long-word from guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to read from (must be 4-byte aligned). + * Returns the 32-bit value, corrected for host endianness. + */ +static inline uint32_t guest_mem_readl(guest_memory_t* memory, uint64_t gpa); + +/** + * guest_mem_readq() - Read a 64-bit quad-word from guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to read from (must be 8-byte aligned). + * Returns the 64-bit value, corrected for host endianness. + */ +static inline uint64_t guest_mem_readq(guest_memory_t* memory, uint64_t gpa); + +/* + * ============================================================================ + * Guest Memory Write Functions + * ============================================================================ + */ + +/** + * guest_mem_writeb() - Write one byte to guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to write to. + * @val: The 8-bit value to write. + */ +static inline void guest_mem_writeb(guest_memory_t* memory, uint64_t gpa, uint8_t val); + +/** + * guest_mem_writew() - Write a 16-bit word to guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to write to (must be 2-byte aligned). + * @val: The 16-bit value to write (will be converted to guest endianness). + */ +static inline void guest_mem_writew(guest_memory_t* memory, uint64_t gpa, uint16_t val); + +/** + * guest_mem_writel() - Write a 32-bit long-word to guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to write to (must be 4-byte aligned). + * @val: The 32-bit value to write. + */ +static inline void guest_mem_writel(guest_memory_t* memory, uint64_t gpa, uint32_t val); + +/** + * guest_mem_writeq() - Write a 64-bit quad-word to guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to write to (must be 8-byte aligned). + * @val: The 64-bit value to write. + */ +static inline void guest_mem_writeq(guest_memory_t* memory, uint64_t gpa, uint64_t val); + +void cpuTest(); +} // namespace pound::aarch64 //========================================================= // OUTDATED CODE @@ -120,5 +195,3 @@ struct CPU } } }; - -void cpuTest(); diff --git a/gui/panels.cpp b/gui/panels.cpp index 3fde731..895f4cd 100755 --- a/gui/panels.cpp +++ b/gui/panels.cpp @@ -94,7 +94,7 @@ int8_t gui::panel::render_cpu_panel(bool* show_cpu_result_popup) if (::ImGui::Button("Run CPU Test", ImVec2(120, 0))) { - ::cpuTest(); + pound::aarch64::cpuTest(); *show_cpu_result_popup = true; } if (true == *show_cpu_result_popup) From 556ace64e8454d579137516915df5d8e2e48d2aa Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Sat, 16 Aug 2025 12:58:51 -0400 Subject: [PATCH 07/10] memory: Move arena allocator into pound::memory namespace The existing memory arena impelmentation is moved into the pound::memory namespace to align with the pound::aarch64 namespace. Signed-off-by: Ronald Caesar --- core/memory/arena.cpp | 10 ++++++---- core/memory/arena.h | 4 ++-- core/memory/arena_allocator.h | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/core/memory/arena.cpp b/core/memory/arena.cpp index 46e7025..da21688 100644 --- a/core/memory/arena.cpp +++ b/core/memory/arena.cpp @@ -4,7 +4,8 @@ #include #endif -memory::arena_t memory::arena_init(size_t capacity) +namespace pound::memory { +arena_t arena_init(size_t capacity) { // TODO(GloriousTaco:memory): Replace malloc with a windows memory mapping API. @@ -28,7 +29,7 @@ memory::arena_t memory::arena_init(size_t capacity) } // new more memsafe code (ownedbywuigi) (i give up on windows compatibility for now, will stick to the old unsafe code) -const void* memory::arena_allocate(memory::arena_t* arena, const std::size_t size) +const void* arena_allocate(memory::arena_t* arena, const std::size_t size) { ASSERT(arena != nullptr); ASSERT(arena->size + size <= arena->capacity); @@ -36,14 +37,14 @@ const void* memory::arena_allocate(memory::arena_t* arena, const std::size_t siz arena->size += size; return data; } -void memory::arena_reset(memory::arena_t* arena) +void arena_reset(memory::arena_t* arena) { ASSERT(nullptr != arena); ASSERT(nullptr != arena->data); arena->size = 0; (void)std::memset(arena->data, POISON_PATTERN, arena->capacity); } -void memory::arena_free(memory::arena_t* arena) +void arena_free(memory::arena_t* arena) { ASSERT(arena != nullptr); arena->capacity = 0; @@ -51,3 +52,4 @@ void memory::arena_free(memory::arena_t* arena) // TODO(GloriousTaco:memory): Replace free with a memory safe alternative. free(arena->data); } +} diff --git a/core/memory/arena.h b/core/memory/arena.h index be3a5b4..8ea3add 100644 --- a/core/memory/arena.h +++ b/core/memory/arena.h @@ -5,7 +5,7 @@ #include #include -namespace memory +namespace pound::memory { #define POISON_PATTERN 0xAA @@ -108,5 +108,5 @@ void arena_reset(arena_t* arena); */ void arena_free(memory::arena_t* arena); -} // namespace memory +} // namespace pound::memory #endif //POUND_ARENA_H diff --git a/core/memory/arena_allocator.h b/core/memory/arena_allocator.h index a4a3f3c..164ebef 100644 --- a/core/memory/arena_allocator.h +++ b/core/memory/arena_allocator.h @@ -5,7 +5,7 @@ #include #include "arena.h" -namespace memory +namespace pound::memory { /** @brief An STL-compatible allocator that uses a custom arena for memory management. @@ -65,4 +65,4 @@ namespace memory } -#endif // POUND_ARENA_ALLOCATOR_H \ No newline at end of file +#endif // POUND_ARENA_ALLOCATOR_H From 65f589e853a2701eed52b041d96825dbfc103f53 Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Sat, 16 Aug 2025 13:11:40 -0400 Subject: [PATCH 08/10] aarch64/kernel: Add synchronous exception entry logic To handle faults such as data aborts, alignment faults, or supervisor calls, the CPU must transition from the guest's context into a privileged exception handler. This patch emulates the hardware sequence for this entry process. 1. The vcpu_state_t struct includes the essential EL1 system registers required for exception handling (ELR_EL1, SPSR_EL1, ESR_EL1, FAR_EL1, and VBAR_EL1). 2. A new function, take_synchronous_exception(), is introduced. It models the requirements for entering an exception targeting EL1: - Saves the return address (PC) into ELR_EL1. - Saves the current proccess state (PSTATE) into SPSR_EL1. - Contructs the Exception Syndrome Register (ESR_EL1) from the provided Exception Class and ISS. - Saves the faulting address to FAR_EL1 for data aborts. - Updates the live PSTATE to a safe state for the handler. This implementation is intentially partial. The final step of updating the PC to jump to a handler in the guest's vector table (using VBAR_EL1) is stubbed out. The vector table will contain assembly instructions so a functional instruction decoder is required to fully complete the exception handler. Signed-off-by: Ronald Caesar --- core/JIT/jit.cpp | 71 ----------- core/JIT/jit.h | 11 -- core/aarch64/isa.cpp | 142 +++++++--------------- core/aarch64/isa.h | 258 +++++++++++++--------------------------- core/aarch64/memory.cpp | 7 ++ core/aarch64/memory.h | 195 ++++++++++++++++++++++++++++++ core/main.cpp | 6 +- 7 files changed, 326 insertions(+), 364 deletions(-) delete mode 100644 core/JIT/jit.cpp delete mode 100644 core/JIT/jit.h create mode 100644 core/aarch64/memory.cpp create mode 100644 core/aarch64/memory.h diff --git a/core/JIT/jit.cpp b/core/JIT/jit.cpp deleted file mode 100644 index 02ffcdb..0000000 --- a/core/JIT/jit.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2025 Pound Emulator Project. All rights reserved. - -#include "jit.h" - -#include - -#ifdef WIN32 -#include -#else -#include -#endif - -#include - -using JitFunc = void (*)(); - -void JIT::translate_and_run(CPU& cpu) -{ - - // TODO: Create REM Context - create_rem_context(nullptr, nullptr, nullptr, nullptr, nullptr); - -#ifdef WIN32 - u8* code = (u8*)VirtualAlloc(NULL, 64, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); -#else - u8* code = (u8*)mmap(nullptr, 64, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); -#endif - - size_t offset = 0; - - // Decode mock instructions from cpu.memory - if (cpu.memory[0] == 0x05) - { // MOVZ placeholder - code[offset++] = 0x48; // mov rax, imm64 - code[offset++] = 0xB8; - u64 imm = 5; - std::memcpy(&code[offset], &imm, sizeof(imm)); - offset += 8; - } - - if (cpu.memory[4] == 0x03) - { // ADD placeholder - code[offset++] = 0x48; // add rax, imm32 - code[offset++] = 0x05; - u32 addval = 3; - std::memcpy(&code[offset], &addval, sizeof(addval)); - offset += 4; - } - - code[offset++] = 0xC3; // ret - - JitFunc fn = reinterpret_cast(code); - u64 result; -#if defined(__x86_64__) - asm volatile( - "call *%1\n" - "mov %%rax, %0\n" - : "=r"(result) - : "r"(fn) - - : "%rax"); -#elif defined(__aarch64__) - asm volatile("blr %1\n" - "mov %0, x0\n" - : "=r"(result) - : "r"(fn) - : "x0"); -#endif - - cpu.regs[0] = result; -} diff --git a/core/JIT/jit.h b/core/JIT/jit.h deleted file mode 100644 index 7ebac1e..0000000 --- a/core/JIT/jit.h +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2025 Pound Emulator Project. All rights reserved. - -#pragma once - -#include "aarch64/isa.h" - -class JIT -{ - public: - void translate_and_run(CPU& cpu); -}; diff --git a/core/aarch64/isa.cpp b/core/aarch64/isa.cpp index 2ece02a..3504709 100644 --- a/core/aarch64/isa.cpp +++ b/core/aarch64/isa.cpp @@ -1,113 +1,57 @@ #include "isa.h" #include "Base/Assert.h" +#include "memory.h" #include "memory/arena.h" -// TODO(GloriousTacoo:aarch64) Implement big to little endian conversion for guest_mem read and write functions. - namespace pound::aarch64 { -static inline uint8_t* gpa_to_hva(guest_memory_t* memory, uint64_t gpa) +void take_synchronous_exception(vcpu_state_t* vcpu, uint8_t exception_class, uint32_t iss, uint64_t faulting_address) { - ASSERT(nullptr != memory); - ASSERT(nullptr != memory->base); - ASSERT(gpa < memory->size); - uint8_t* hva = memory->base + gpa; - return hva; -} + ASSERT(nullptr != vcpu); + /* An EC holds 6 bits.*/ + ASSERT(0 == (exception_class & 11000000)); + /* An ISS holds 25 bits */ + ASSERT(0 == (iss & 0xFE000000)); -/* - * ============================================================================ - * Guest Memory Read Functions - * ============================================================================ - */ + vcpu->elr_el1 = vcpu->pc; + vcpu->spsr_el1 = vcpu->pstate; + vcpu->esr_el1 = 0; -static inline uint8_t guest_mem_readb(guest_memory_t* memory, uint64_t gpa) -{ - ASSERT(nullptr != memory); - ASSERT(nullptr != memory->base); - ASSERT(gpa <= memory->size); - uint8_t* hva = gpa_to_hva(memory, gpa); - return *hva; -} + /* Bits [31:26] are the Exception Class (EC). */ + /* Bits [25] is the Instruction Length (IL), 1 for a 32-bit instruction. */ + /* Bits [24:0] are the Instruction Specific Syndrome (ISS) */ + const uint64_t esr_il_bit = (1ULL << 25); + vcpu->esr_el1 = ((uint64_t)exception_class << 26) | esr_il_bit | iss; -static inline uint16_t guest_mem_readw(guest_memory_t* memory, uint64_t gpa) -{ - ASSERT(nullptr != memory); - ASSERT(nullptr != memory->base); - ASSERT((gpa + sizeof(uint16_t)) <= memory->size); - // Check if gpa is aligned to 2 bytes. - ASSERT((gpa & 1) == 0); - uint16_t* hva = (uint16_t*)gpa_to_hva(memory, gpa); - return *hva; -} + if ((exception_class == EC_DATA_ABORT) || (exception_class == EC_DATA_ABORT_LOWER_EL)) + { + vcpu->far_el1 = faulting_address; + } -static inline uint32_t guest_mem_readl(guest_memory_t* memory, uint64_t gpa) -{ - ASSERT(nullptr != memory); - ASSERT(nullptr != memory->base); - ASSERT((gpa + sizeof(uint32_t)) <= memory->size); - // Check if gpa is aligned to 4 bytes. - ASSERT((gpa & 3) == 0); - uint32_t* hva = (uint32_t*)gpa_to_hva(memory, gpa); - return *hva; -} + /* The CPU state must be changed to a known safe state for handling */ + vcpu->pstate &= ~0xF0000000; -static inline uint64_t guest_mem_readq(guest_memory_t* memory, uint64_t gpa) -{ - ASSERT(nullptr != memory); - ASSERT(nullptr != memory->base); - ASSERT((gpa + sizeof(uint64_t)) <= memory->size); - // Check if gpa is aligned to 8 bytes. - ASSERT((gpa & 7) == 0); - uint64_t* hva = (uint64_t*)gpa_to_hva(memory, gpa); - return *hva; -} + /* Mask asynchronous exceptions (IRQ, FIQ, SError). We dont want the + * Exception handler to be interruoted by a less important event. */ + const uint32_t PSTATE_IRQ_BIT = (1 << 7); + const uint32_t PSTATE_FIQ_BIT = (1 << 6); + const uint32_t PSTATE_SERROR_BIT = (1 << 8); + vcpu->pstate |= (PSTATE_IRQ_BIT | PSTATE_FIQ_BIT | PSTATE_SERROR_BIT); -/* - * ============================================================================ - * Guest Memory Write Functions - * ============================================================================ - */ + /* Set the target exception level to EL1. The mode field M[3:0] is set + * to 0b0101 for EL1h (using SP_EL1). (page 913 in manual) */ + const uint32_t PSTATE_EL_MASK = 0b1111; + vcpu->pstate &= ~PSTATE_EL_MASK; + const uint32_t PSTATE_EL1H = 0b0101; + vcpu->pstate |= PSTATE_EL1H; -static inline void guest_mem_writeb(guest_memory_t* memory, uint64_t gpa, uint8_t val) -{ - ASSERT(nullptr != memory); - ASSERT(nullptr != memory->base); - ASSERT(gpa <= memory->size); - uint8_t* hva = gpa_to_hva(memory, gpa); - *hva = val; -} - -static inline void guest_mem_writew(guest_memory_t* memory, uint64_t gpa, uint16_t val) -{ - ASSERT(nullptr != memory); - ASSERT(nullptr != memory->base); - ASSERT((gpa + sizeof(uint16_t)) <= memory->size); - // Check if gpa is aligned to 2 bytes. - ASSERT((gpa & 1) == 0); - uint16_t* hva = (uint16_t*)gpa_to_hva(memory, gpa); - *hva = val; -} - -static inline void guest_mem_writel(guest_memory_t* memory, uint64_t gpa, uint32_t val) -{ - ASSERT(nullptr != memory->base); - ASSERT((gpa + sizeof(uint32_t)) <= memory->size); - // Check if gpa is aligned to 4 bytes. - ASSERT((gpa & 3) == 0); - uint32_t* hva = (uint32_t*)gpa_to_hva(memory, gpa); - *hva = val; -} - -static inline void guest_mem_writeq(guest_memory_t* memory, uint64_t gpa, uint64_t val) -{ - ASSERT(nullptr != memory); - ASSERT(nullptr != memory->base); - ASSERT((gpa + sizeof(uint64_t)) <= memory->size); - // Check if gpa is aligned to 8 bytes. - ASSERT((gpa & 7) == 0); - uint64_t* hva = (uint64_t*)gpa_to_hva(memory, gpa); - *hva = val; + /* TODO(GloriousTacoo:arm): DO NOT IMPLEMENT UNTIL THE INSTRUCTION + * DECODER IS FINISHED. + * + * Create an Exception Vector Table, determine + * the address of the exception handler, then update the PC. + * + * vcpu->pc = vcpu->vbar_el1 + offset; */ } /** THIS FUNCTION WAS MADE WITH AI AND IS CALLED WHEN RUNNING THE CPU TEST FROM THE GUI! @@ -124,7 +68,7 @@ static inline void guest_mem_writeq(guest_memory_t* memory, uint64_t gpa, uint64 * @param memory A pointer to an initialized guest_memory_t struct. * @return true if all tests pass, false otherwise. */ -bool test_guest_ram_access(guest_memory_t* memory) +bool test_guest_ram_access(pound::aarch64::memory::guest_memory_t* memory) { LOG_INFO(Memory, "--- [ Starting Guest RAM Access Test ] ---"); if (memory == nullptr || memory->base == nullptr || memory->size < 4096) @@ -211,10 +155,10 @@ bool test_guest_ram_access(guest_memory_t* memory) void cpuTest() { vcpu_state_t vcpu_states[CPU_CORES] = {}; - memory::arena_t guest_memory_arena = memory::arena_init(GUEST_RAM_SIZE); + pound::memory::arena_t guest_memory_arena = pound::memory::arena_init(GUEST_RAM_SIZE); ASSERT(nullptr != guest_memory_arena.data); - guest_memory_t guest_ram = {}; + pound::aarch64::memory::guest_memory_t guest_ram = {}; guest_ram.base = static_cast(guest_memory_arena.data); guest_ram.size = guest_memory_arena.capacity; diff --git a/core/aarch64/isa.h b/core/aarch64/isa.h index ffa23b4..efd3826 100644 --- a/core/aarch64/isa.h +++ b/core/aarch64/isa.h @@ -19,18 +19,34 @@ namespace pound::aarch64 #define GUEST_RAM_SIZE 10240 // 10KiB #define CPU_CORES 8 +/* Data Abort exception taken without a change in Exception level. */ +#define EC_DATA_ABORT 0b100101 + +/* Data Abort exception from a lower Exception level. */ +#define EC_DATA_ABORT_LOWER_EL 0b100100 + /* * vcpu_state_t - Holds the architectural and selected system-register state for an emulated vCPU. - * @v: 128-bit SIMD/FP vector registers V0–V31. - * @r: General-purpose registers X0–X31 (X31 as SP/ZR as appropriate). - * @pc: Program Counter. - * @pstate: Process State Register (NZCV, DAIF, EL, etc.). - * - * System registers (subset mirrored for fast-path emulation at EL0): - * - ctr_el0, dczid_el0: Cache/type identification. - * - tpidrro_el0, tpidr_el0: Thread pointers (host-mapped TLS pointers). - * - cntfrq_el0, cntpct_el0, cntvct_el0, cntv_ctl_el0, cntv_cval_el0: Generic timers/counters. - * - pmccntr_el0, pmcr_el0: PMU cycle counter and control. + * @v: 128-bit SIMD/FP vector registers V0–V31. + * @r: General-purpose registers X0–X31 (X31 as SP/ZR as appropriate). + * @pc: Program Counter. + * @cntfreq_el0: Counter Frequency. + * @cntpct_el0: Physical Counter. + * @cntvct_el0: Virtual Counter - CRITICAL for timing. + * @cntv_cval_el0: Virtual Timer Compare Value. + * @pmccntr_el0: Cycle Counter. + * @tpidr_el0: Thread Pointer ID Register. + * @tpidrro_el0: Thread Pointer ID, read-only. + * @elr_el1: Exception Link Register. + * @esr_el1: Exception Syndrome Register. + * @far_el1: Fault Address Register. + * @vbar_el1: Vector Base Address Register. + * @spsr_el1: Saved Program Status Register. + * @ctr_el0: Cache-Type. + * @cntv_ctl_el0: Virtual Timer Control. + * @dczid_el0: Data Cache Zero ID. + * @pmcr_el0: Performance Monitor Counter. + * @pstate: Process State Register (NZCV, DAIF, EL, etc.). * * This structure is aligned to the L1 cache line size to prevent false sharing * when multiple host threads are emulating vCPUs on different physical cores. @@ -40,182 +56,68 @@ typedef struct alignas(CACHE_LINE_SIZE) unsigned __int128 v[FP_REGISTERS]; uint64_t r[GP_REGISTERS]; uint64_t pc; + uint64_t cntfreq_el0; + uint64_t cntpct_el0; + uint64_t cntvct_el0; + uint64_t cntv_cval_el0; + uint64_t pmccntr_el0; + uint64_t tpidr_el0; + uint64_t tpidrro_el0; + + /* + * Stores the Program Counter of the instruction that was interrupted. + * For a synchronous fault, it's the address of the faulting instruction + * itself. + */ + uint64_t elr_el1; + + /* + * Tells the guest OS why the exception happened. It contains a high + * level Exception Class (EC) (eg, Data Abort) and a low level + * Instruction Specific Syndrome (ISS) with fine-grained details. + * (eg, it was an allignment fault cause by a write operation.) + */ + uint64_t esr_el1; + + /* The memory address that caused a Data Abort exception. */ + uint64_t far_el1; + + /* + * A snapshot of the current PSTATE register before the exception. + * This is for restoring the program's state when returning from an + * exception. + */ + uint64_t spsr_el1; + + /* + * The base address in guest memory where the Exception Vector Table + * can be found. + */ + uint64_t vbar_el1; + + uint32_t ctr_el0; + uint32_t cntv_ctl_el0; + uint32_t dczid_el0; + uint32_t pmcr_el0; uint32_t pstate; - - // ========================= System Registers ================================== - // Basics - uint32_t ctr_elo; // cache-type register - uint32_t dczid_elo; // data cache zero-ID - const uint64_t* tpidrro_e10; // thread pointer ID register, read-only - uint64_t* tpidr_e10; // thread pointer ID register - - // Counters - uint64_t cntfreq_elo; // counter frequency - uint64_t cntvct_el0; // Virtual counter - CRITICAL for timing - uint64_t cntpct_el0; // Physical counter - uint32_t cntv_ctl_el0; // Virtual timer control - uint64_t cntv_cval_el0; // Virtual timer compare value - - // Performance monitoring (if games use them): - uint64_t pmccntr_el0; // Cycle counter - uint32_t pmcr_el0; // Performance monitor control - // ============================================================================= } vcpu_state_t; /* - * guest_memory_t - Describes a contiguous block of guest physical RAM. - * @base: Pointer to the start of the host-allocated memory block. - * @size: The size of the memory block in bytes. - */ -typedef struct -{ - uint8_t* base; - uint64_t size; -} guest_memory_t; - -/* - * gpa_to_hva() - Translate a Guest Physical Address to a Host Virtual Address. - * @memory: The guest memory region to translate within. - * @gpa: The Guest Physical Address (offset) to translate. + * take_synchronous_exception() - Emulates the hardware process of taking a synchronous exception to EL1. * - * This function provides a fast, direct translation for a flat guest memory - * model. It relies on the critical pre-condition that the guest's physical - * RAM is backed by a single, contiguous block of virtual memory in the host's - * userspace (typically allocated with mmap()). + * @vcpu: A pointer to the vCPU state to be modified. + * @exception_class: The high-level Exception Class (EC) code for ESR_EL1. + * @iss: The low-level Instruction Specific Syndrome (ISS) code for ESR_EL1. + * @faulting_address: The faulting address, to be written to FAR_EL1. Only valid for Data/Instruction Aborts. Pass 0 for other exception types. * - * In this model, memory->base is the Host Virtual Address (HVA) of the start of - * the backing host memory. The provided Guest Physical Address (gpa) is not - * treated as a pointer, but as a simple byte offset from the start of the guest's - * physical address space (PAS). + * This function modifies the vCPU state according to the rules for taking a + * synchronous exception from a lower or same exception level that is targeting EL1. + * It saves the necessary return state, populates the syndrome registers, + * updates the processor state for entry into EL1, and calculates the new + * program counter by branching to the appropriate offset in the EL1 vector table. * - * The translation is therefore a single pointer-offset calculation. This establishes - * a direct 1:1 mapping between the guest's PAS and the host's virtual memory block. - * - * The function asserts that GPA is within bounds. The caller is responsible for - * ensuring the validity of the GPA prior to calling. - * - * Return: A valid host virtual address pointer corresponding to the GPA. */ -static inline uint8_t* gpa_to_hva(guest_memory_t* memory, uint64_t gpa); - -/* - * ============================================================================ - * Guest Memory Read Functions - * ============================================================================ - */ - -/** - * guest_mem_readb() - Read one byte from guest memory. - * @memory: The guest memory region. - * @gpa: The Guest Physical Address to read from. - * Returns the 8-bit value read from memory. - */ -static inline uint8_t guest_mem_readb(guest_memory_t* memory, uint64_t gpa); - -/** - * guest_mem_readw() - Read a 16-bit word from guest memory. - * @memory: The guest memory region. - * @gpa: The Guest Physical Address to read from (must be 2-byte aligned). - * Returns the 16-bit value, corrected for host endianness. - */ -static inline uint16_t guest_mem_readw(guest_memory_t* memory, uint64_t gpa); - -/** - * guest_mem_readl() - Read a 32-bit long-word from guest memory. - * @memory: The guest memory region. - * @gpa: The Guest Physical Address to read from (must be 4-byte aligned). - * Returns the 32-bit value, corrected for host endianness. - */ -static inline uint32_t guest_mem_readl(guest_memory_t* memory, uint64_t gpa); - -/** - * guest_mem_readq() - Read a 64-bit quad-word from guest memory. - * @memory: The guest memory region. - * @gpa: The Guest Physical Address to read from (must be 8-byte aligned). - * Returns the 64-bit value, corrected for host endianness. - */ -static inline uint64_t guest_mem_readq(guest_memory_t* memory, uint64_t gpa); - -/* - * ============================================================================ - * Guest Memory Write Functions - * ============================================================================ - */ - -/** - * guest_mem_writeb() - Write one byte to guest memory. - * @memory: The guest memory region. - * @gpa: The Guest Physical Address to write to. - * @val: The 8-bit value to write. - */ -static inline void guest_mem_writeb(guest_memory_t* memory, uint64_t gpa, uint8_t val); - -/** - * guest_mem_writew() - Write a 16-bit word to guest memory. - * @memory: The guest memory region. - * @gpa: The Guest Physical Address to write to (must be 2-byte aligned). - * @val: The 16-bit value to write (will be converted to guest endianness). - */ -static inline void guest_mem_writew(guest_memory_t* memory, uint64_t gpa, uint16_t val); - -/** - * guest_mem_writel() - Write a 32-bit long-word to guest memory. - * @memory: The guest memory region. - * @gpa: The Guest Physical Address to write to (must be 4-byte aligned). - * @val: The 32-bit value to write. - */ -static inline void guest_mem_writel(guest_memory_t* memory, uint64_t gpa, uint32_t val); - -/** - * guest_mem_writeq() - Write a 64-bit quad-word to guest memory. - * @memory: The guest memory region. - * @gpa: The Guest Physical Address to write to (must be 8-byte aligned). - * @val: The 64-bit value to write. - */ -static inline void guest_mem_writeq(guest_memory_t* memory, uint64_t gpa, uint64_t val); +void take_synchronous_exception(vcpu_state_t* vcpu, uint8_t exception_class, uint32_t iss, uint64_t faulting_address); void cpuTest(); } // namespace pound::aarch64 - -//========================================================= -// OUTDATED CODE -//========================================================= -struct CPU -{ - u64 regs[31] = {0}; // X0–X30 - u64 pc = 0; - static constexpr size_t MEM_SIZE = 64 * 1024; - u8 memory[MEM_SIZE]; - - CPU() { std::memset(memory, 0, MEM_SIZE); } - - u64& x(int i) { return regs[i]; } - - u8 read_byte(u64 addr) - { - if (addr >= MEM_SIZE) - { - LOG_INFO(ARM, "{} out of bounds", addr); - } - return memory[addr]; - } - - void write_byte(u64 addr, u8 byte) - { - if (addr >= MEM_SIZE) - { - LOG_INFO(ARM, "{} out of bounds", addr); - } - memory[addr] = byte; - } - - void print_debug_information() - { - LOG_INFO(ARM, "PC = {}", pc); - for (int reg = 0; reg < 31; reg++) - { - uint64_t regis = x(reg); - LOG_INFO(ARM, "X{} = {}", reg, regis); // X0 = 0.. - } - } -}; diff --git a/core/aarch64/memory.cpp b/core/aarch64/memory.cpp new file mode 100644 index 0000000..77473d1 --- /dev/null +++ b/core/aarch64/memory.cpp @@ -0,0 +1,7 @@ +#include "memory.h" +#include "Base/Assert.h" + +namespace pound::aarch64::memory +{ + +} // namespace pound::aarch64::memory diff --git a/core/aarch64/memory.h b/core/aarch64/memory.h new file mode 100644 index 0000000..b0b31c4 --- /dev/null +++ b/core/aarch64/memory.h @@ -0,0 +1,195 @@ +#pragma once + +#include "Base/Assert.h" + +namespace pound::aarch64::memory +{ + +/* + * guest_memory_t - Describes a contiguous block of guest physical RAM. + * @base: Pointer to the start of the host-allocated memory block. + * @size: The size of the memory block in bytes. + */ +typedef struct +{ + uint8_t* base; + uint64_t size; +} guest_memory_t; + +/* + * gpa_to_hva() - Translate a Guest Physical Address to a Host Virtual Address. + * @memory: The guest memory region to translate within. + * @gpa: The Guest Physical Address (offset) to translate. + * + * This function provides a fast, direct translation for a flat guest memory + * model. It relies on the critical pre-condition that the guest's physical + * RAM is backed by a single, contiguous block of virtual memory in the host's + * userspace (typically allocated with mmap()). + * + * In this model, memory->base is the Host Virtual Address (HVA) of the start of + * the backing host memory. The provided Guest Physical Address (gpa) is not + * treated as a pointer, but as a simple byte offset from the start of the guest's + * physical address space (PAS). + * + * The translation is therefore a single pointer-offset calculation. This establishes + * a direct 1:1 mapping between the guest's PAS and the host's virtual memory block. + * + * The function asserts that GPA is within bounds. The caller is responsible for + * ensuring the validity of the GPA prior to calling. + * + * Return: A valid host virtual address pointer corresponding to the GPA. + */ +static inline uint8_t* gpa_to_hva(guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT(gpa < memory->size); + uint8_t* hva = memory->base + gpa; + return hva; +} + +// TODO(GloriousTacoo:aarch64) Implement big to little endian conversion for guest_mem read and write functions. + +/* + * ============================================================================ + * Guest Memory Read Functions + * ============================================================================ + */ + +/* + * guest_mem_readb() - Read one byte from guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to read from. + * Returns the 8-bit value read from memory. + */ +static inline uint8_t guest_mem_readb(guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT(gpa <= memory->size); + uint8_t* hva = gpa_to_hva(memory, gpa); + return *hva; +} + +/* + * guest_mem_readw() - Read a 16-bit word from guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to read from (must be 2-byte aligned). + * Returns the 16-bit value, corrected for host endianness. + */ +static inline uint16_t guest_mem_readw(guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint16_t)) <= memory->size); + // Check if gpa is aligned to 2 bytes. + ASSERT((gpa & 1) == 0); + uint16_t* hva = (uint16_t*)gpa_to_hva(memory, gpa); + return *hva; +} + +/* + * guest_mem_readl() - Read a 32-bit long-word from guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to read from (must be 4-byte aligned). + * Returns the 32-bit value, corrected for host endianness. + */ +static inline uint32_t guest_mem_readl(guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint32_t)) <= memory->size); + // Check if gpa is aligned to 4 bytes. + ASSERT((gpa & 3) == 0); + uint32_t* hva = (uint32_t*)gpa_to_hva(memory, gpa); + return *hva; +} + +/* + * guest_mem_readq() - Read a 64-bit quad-word from guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to read from (must be 8-byte aligned). + * Returns the 64-bit value, corrected for host endianness. + */ +static inline uint64_t guest_mem_readq(guest_memory_t* memory, uint64_t gpa) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint64_t)) <= memory->size); + // Check if gpa is aligned to 8 bytes. + ASSERT((gpa & 7) == 0); + uint64_t* hva = (uint64_t*)gpa_to_hva(memory, gpa); + return *hva; +} + +/* + * ============================================================================ + * Guest Memory Write Functions + * ============================================================================ + */ + +/* + * guest_mem_writeb() - Write one byte to guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to write to. + * @val: The 8-bit value to write. + */ +static inline void guest_mem_writeb(guest_memory_t* memory, uint64_t gpa, uint8_t val) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT(gpa <= memory->size); + uint8_t* hva = gpa_to_hva(memory, gpa); + *hva = val; +} + +/* + * guest_mem_writew() - Write a 16-bit word to guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to write to (must be 2-byte aligned). + * @val: The 16-bit value to write (will be converted to guest endianness). + */ +static inline void guest_mem_writew(guest_memory_t* memory, uint64_t gpa, uint16_t val) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint16_t)) <= memory->size); + // Check if gpa is aligned to 2 bytes. + ASSERT((gpa & 1) == 0); + uint16_t* hva = (uint16_t*)gpa_to_hva(memory, gpa); + *hva = val; +} + +/* + * guest_mem_writel() - Write a 32-bit long-word to guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to write to (must be 4-byte aligned). + * @val: The 32-bit value to write. + */ +static inline void guest_mem_writel(guest_memory_t* memory, uint64_t gpa, uint32_t val) +{ + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint32_t)) <= memory->size); + // Check if gpa is aligned to 4 bytes. + ASSERT((gpa & 3) == 0); + uint32_t* hva = (uint32_t*)gpa_to_hva(memory, gpa); + *hva = val; +} + +/* + * guest_mem_writeq() - Write a 64-bit quad-word to guest memory. + * @memory: The guest memory region. + * @gpa: The Guest Physical Address to write to (must be 8-byte aligned). + * @val: The 64-bit value to write. + */ +static inline void guest_mem_writeq(guest_memory_t* memory, uint64_t gpa, uint64_t val) +{ + ASSERT(nullptr != memory); + ASSERT(nullptr != memory->base); + ASSERT((gpa + sizeof(uint64_t)) <= memory->size); + // Check if gpa is aligned to 8 bytes. + ASSERT((gpa & 7) == 0); + uint64_t* hva = (uint64_t*)gpa_to_hva(memory, gpa); + *hva = val; +} +} // namespace pound::aarch64::memory diff --git a/core/main.cpp b/core/main.cpp index fc2bd02..f4fb66d 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -5,8 +5,8 @@ #include #include "Base/Config.h" +#include "Base/Logging/Log.h" #include "Base/Logging/Backend.h" -#include "JIT/jit.h" #include "gui/gui.h" #include "memory/arena.h" @@ -18,10 +18,6 @@ int main() { - // This is meant to replace malloc() and its related functions. - // TODO(GloriousTaco:memory): Implement std::allocator for this custom allocator which allows it to manage the memory of C++ standard types like std::vector. - memory::arena_t arena = memory::arena_init(1024); - Base::Log::Initialize(); Base::Log::Start(); From 58587bf754e573918f219c9825f897b244021396 Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Sat, 16 Aug 2025 17:37:12 -0400 Subject: [PATCH 09/10] arm64: Renames the aarch64 directory to arm64 The term "aarch64" is the formal name for Armv8-A architecture. However, I found that the establish convention across the wider open source ecosystem is to use the short name "arm64". Signed-off-by: Ronald Caesar --- core/{aarch64 => arm64}/isa.cpp | 8 ++++---- core/{aarch64 => arm64}/isa.h | 4 ++-- core/{aarch64 => arm64}/memory.cpp | 0 core/{aarch64 => arm64}/memory.h | 2 +- core/arm64/mmu.h | 19 +++++++++++++++++++ gui/panels.cpp | 4 ++-- 6 files changed, 28 insertions(+), 9 deletions(-) rename core/{aarch64 => arm64}/isa.cpp (97%) rename core/{aarch64 => arm64}/isa.h (98%) rename core/{aarch64 => arm64}/memory.cpp (100%) rename core/{aarch64 => arm64}/memory.h (99%) create mode 100644 core/arm64/mmu.h diff --git a/core/aarch64/isa.cpp b/core/arm64/isa.cpp similarity index 97% rename from core/aarch64/isa.cpp rename to core/arm64/isa.cpp index 3504709..09c3c6f 100644 --- a/core/aarch64/isa.cpp +++ b/core/arm64/isa.cpp @@ -3,7 +3,7 @@ #include "memory.h" #include "memory/arena.h" -namespace pound::aarch64 +namespace pound::arm64 { void take_synchronous_exception(vcpu_state_t* vcpu, uint8_t exception_class, uint32_t iss, uint64_t faulting_address) { @@ -68,7 +68,7 @@ void take_synchronous_exception(vcpu_state_t* vcpu, uint8_t exception_class, uin * @param memory A pointer to an initialized guest_memory_t struct. * @return true if all tests pass, false otherwise. */ -bool test_guest_ram_access(pound::aarch64::memory::guest_memory_t* memory) +bool test_guest_ram_access(pound::arm64::memory::guest_memory_t* memory) { LOG_INFO(Memory, "--- [ Starting Guest RAM Access Test ] ---"); if (memory == nullptr || memory->base == nullptr || memory->size < 4096) @@ -158,10 +158,10 @@ void cpuTest() pound::memory::arena_t guest_memory_arena = pound::memory::arena_init(GUEST_RAM_SIZE); ASSERT(nullptr != guest_memory_arena.data); - pound::aarch64::memory::guest_memory_t guest_ram = {}; + pound::arm64::memory::guest_memory_t guest_ram = {}; guest_ram.base = static_cast(guest_memory_arena.data); guest_ram.size = guest_memory_arena.capacity; (void)test_guest_ram_access(&guest_ram); } -} // namespace pound::aarch64 +} // namespace pound::armv64 diff --git a/core/aarch64/isa.h b/core/arm64/isa.h similarity index 98% rename from core/aarch64/isa.h rename to core/arm64/isa.h index efd3826..3a21dda 100644 --- a/core/aarch64/isa.h +++ b/core/arm64/isa.h @@ -7,7 +7,7 @@ #include "Base/Logging/Log.h" -namespace pound::aarch64 +namespace pound::arm64 { /* AArch64 R0-R31 */ #define GP_REGISTERS 32 @@ -120,4 +120,4 @@ typedef struct alignas(CACHE_LINE_SIZE) void take_synchronous_exception(vcpu_state_t* vcpu, uint8_t exception_class, uint32_t iss, uint64_t faulting_address); void cpuTest(); -} // namespace pound::aarch64 +} // namespace pound::arm64 diff --git a/core/aarch64/memory.cpp b/core/arm64/memory.cpp similarity index 100% rename from core/aarch64/memory.cpp rename to core/arm64/memory.cpp diff --git a/core/aarch64/memory.h b/core/arm64/memory.h similarity index 99% rename from core/aarch64/memory.h rename to core/arm64/memory.h index b0b31c4..7eca1b0 100644 --- a/core/aarch64/memory.h +++ b/core/arm64/memory.h @@ -2,7 +2,7 @@ #include "Base/Assert.h" -namespace pound::aarch64::memory +namespace pound::arm64::memory { /* diff --git a/core/arm64/mmu.h b/core/arm64/mmu.h new file mode 100644 index 0000000..519a7e1 --- /dev/null +++ b/core/arm64/mmu.h @@ -0,0 +1,19 @@ +#pragma once + +namespace pound::armv8 +/** + * kvm_mmu_gva_to_gpa() - Translate a Guest Virtual Address to a Guest Physical Address. + * @vcpu: The vCPU state, containing MMU configuration (TTBR0_EL1, etc.). + * @gva: The Guest Virtual Address to translate. + * @gpa: A pointer to store the resulting Guest Physical Address. + * + * This function is the entry point for the emulated MMU. + * + * For now, this is a stub that implements identity mapping (GVA -> GPA) + * when the MMU is disabled, which is the correct architectural behavior + * on reset. + * + * Return: 0 on success, or a negative error code on a fault. + */ +int kvm_mmu_gva_to_gpa(vcpu_state_t* vcpu, uint64_t gva, uint64_t* gpa); +} diff --git a/gui/panels.cpp b/gui/panels.cpp index 895f4cd..49f1ac1 100755 --- a/gui/panels.cpp +++ b/gui/panels.cpp @@ -1,7 +1,7 @@ #include "panels.h" #include #include "Base/Assert.h" -#include "aarch64/isa.h" +#include "arm64/isa.h" #include "imgui.h" int8_t gui::panel::render_performance_panel(gui::panel::performance_panel_t* panel, performance_data_t* data, @@ -94,7 +94,7 @@ int8_t gui::panel::render_cpu_panel(bool* show_cpu_result_popup) if (::ImGui::Button("Run CPU Test", ImVec2(120, 0))) { - pound::aarch64::cpuTest(); + pound::arm64::cpuTest(); *show_cpu_result_popup = true; } if (true == *show_cpu_result_popup) From b11f04912d5e2af2382d76dc9146bf1629c3aa77 Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Sat, 16 Aug 2025 18:17:59 -0400 Subject: [PATCH 10/10] arm64: Rename memory.h to guest.h The term "memory" is really ambiguous in the context of an emulator, especially since the MMU is being developed. It can refer to host memory, or guest memory, or both. Signed-off-by: Ronald Caesar --- .gitignore | 1 - core/arm64/{memory.cpp => guest.cpp} | 0 core/arm64/{memory.h => guest.h} | 0 core/arm64/isa.cpp | 2 +- 4 files changed, 1 insertion(+), 2 deletions(-) rename core/arm64/{memory.cpp => guest.cpp} (100%) rename core/arm64/{memory.h => guest.h} (100%) diff --git a/.gitignore b/.gitignore index c8a6111..b9159d8 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,6 @@ x64/ x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ diff --git a/core/arm64/memory.cpp b/core/arm64/guest.cpp similarity index 100% rename from core/arm64/memory.cpp rename to core/arm64/guest.cpp diff --git a/core/arm64/memory.h b/core/arm64/guest.h similarity index 100% rename from core/arm64/memory.h rename to core/arm64/guest.h diff --git a/core/arm64/isa.cpp b/core/arm64/isa.cpp index 09c3c6f..1cb8c4a 100644 --- a/core/arm64/isa.cpp +++ b/core/arm64/isa.cpp @@ -1,6 +1,6 @@ #include "isa.h" #include "Base/Assert.h" -#include "memory.h" +#include "guest.h" #include "memory/arena.h" namespace pound::arm64