pound-emu_pound/core/aarch64/isa.h
Ronald Caesar e7b5349980 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>
2025-08-14 20:07:29 -04:00

197 lines
6.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2025 Pound Emulator Project. All rights reserved.
#pragma once
#include <cstdlib>
#include <cstring>
#include "Base/Logging/Log.h"
namespace pound::aarch64
{
/* AArch64 R0-R31 */
#define GP_REGISTERS 32
/* AArch64 V0-V31 */
#define FP_REGISTERS 32
#define CACHE_LINE_SIZE 64
#define GUEST_RAM_SIZE 10240 // 10KiB
#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.
* @pc: Program Counter.
* @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)
{
unsigned __int128 v[FP_REGISTERS];
uint64_t r[GP_REGISTERS];
uint64_t pc;
uint32_t pstate;
} 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.
*
* 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);
/*
* ============================================================================
* 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
//=========================================================
struct CPU
{
u64 regs[31] = {0}; // X0X30
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..
}
}
};