From e7b534998021f2d63dfecd457d2fee48c3651eb7 Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Thu, 14 Aug 2025 19:56:21 -0400 Subject: [PATCH] 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)