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/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/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 deleted file mode 100755 index ed4ef51..0000000 --- a/core/aarch64/isa.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "isa.h" -#include "Base/Assert.h" - -void cpuTest() -{ - 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(); -} \ No newline at end of file diff --git a/core/aarch64/isa.h b/core/aarch64/isa.h deleted file mode 100644 index 1e5236d..0000000 --- a/core/aarch64/isa.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2025 Pound Emulator Project. All rights reserved. - -#pragma once - -#include - -#include "Base/Logging/Log.h" - -namespace aarch64 -{ -#define GPR_REGISTERS 32 -#define ZERO_REGISTER_INDEX 31 - -#define FPR_REGISTERS 32 - -typedef struct -{ - uint64_t gpr[GPR_REGISTERS]; - unsigned __int128 fpr[FPR_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); - -} // namespace aarch64 - -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.. - } - } -}; - -void cpuTest(); diff --git a/core/arm64/guest.cpp b/core/arm64/guest.cpp new file mode 100644 index 0000000..77473d1 --- /dev/null +++ b/core/arm64/guest.cpp @@ -0,0 +1,7 @@ +#include "memory.h" +#include "Base/Assert.h" + +namespace pound::aarch64::memory +{ + +} // namespace pound::aarch64::memory diff --git a/core/arm64/guest.h b/core/arm64/guest.h new file mode 100644 index 0000000..7eca1b0 --- /dev/null +++ b/core/arm64/guest.h @@ -0,0 +1,195 @@ +#pragma once + +#include "Base/Assert.h" + +namespace pound::arm64::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/arm64/isa.cpp b/core/arm64/isa.cpp new file mode 100644 index 0000000..1cb8c4a --- /dev/null +++ b/core/arm64/isa.cpp @@ -0,0 +1,167 @@ +#include "isa.h" +#include "Base/Assert.h" +#include "guest.h" +#include "memory/arena.h" + +namespace pound::arm64 +{ +void take_synchronous_exception(vcpu_state_t* vcpu, uint8_t exception_class, uint32_t iss, uint64_t faulting_address) +{ + ASSERT(nullptr != vcpu); + /* An EC holds 6 bits.*/ + ASSERT(0 == (exception_class & 11000000)); + /* An ISS holds 25 bits */ + ASSERT(0 == (iss & 0xFE000000)); + + vcpu->elr_el1 = vcpu->pc; + vcpu->spsr_el1 = vcpu->pstate; + vcpu->esr_el1 = 0; + + /* 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; + + if ((exception_class == EC_DATA_ABORT) || (exception_class == EC_DATA_ABORT_LOWER_EL)) + { + vcpu->far_el1 = faulting_address; + } + + /* The CPU state must be changed to a known safe state for handling */ + vcpu->pstate &= ~0xF0000000; + + /* 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); + + /* 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; + + /* 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! + * + * @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(pound::arm64::memory::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() +{ + vcpu_state_t vcpu_states[CPU_CORES] = {}; + pound::memory::arena_t guest_memory_arena = pound::memory::arena_init(GUEST_RAM_SIZE); + ASSERT(nullptr != guest_memory_arena.data); + + 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::armv64 diff --git a/core/arm64/isa.h b/core/arm64/isa.h new file mode 100644 index 0000000..3a21dda --- /dev/null +++ b/core/arm64/isa.h @@ -0,0 +1,123 @@ +// Copyright 2025 Pound Emulator Project. All rights reserved. + +#pragma once + +#include +#include + +#include "Base/Logging/Log.h" + +namespace pound::arm64 +{ +/* 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 + +/* 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. + * @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. + */ +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; +} vcpu_state_t; + +/* + * take_synchronous_exception() - Emulates the hardware process of taking a synchronous exception to EL1. + * + * @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. + * + * 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. + * + */ +void take_synchronous_exception(vcpu_state_t* vcpu, uint8_t exception_class, uint32_t iss, uint64_t faulting_address); + +void cpuTest(); +} // namespace pound::arm64 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/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(); 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 diff --git a/gui/panels.cpp b/gui/panels.cpp index 3fde731..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))) { - ::cpuTest(); + pound::arm64::cpuTest(); *show_cpu_result_popup = true; } if (true == *show_cpu_result_popup)