mirror of
https://github.com/pound-emu/pound.git
synced 2025-12-11 07:36:57 +00:00
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 <github43132@proton.me>
This commit is contained in:
parent
c550b00dde
commit
e7b5349980
5 changed files with 286 additions and 24 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<uint8_t*>(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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue