From 354b7b65bec340150bf640fb4cf439901c978de3 Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Thu, 30 Jan 2020 00:45:39 +0100 Subject: [PATCH] ppcmmu: handle unaligned reads and writes. --- cpu/ppc/ppcmmu.cpp | 123 ++++++++++++++++++++++++++------------------- memreadwrite.h | 16 ++++++ 2 files changed, 88 insertions(+), 51 deletions(-) diff --git a/cpu/ppc/ppcmmu.cpp b/cpu/ppc/ppcmmu.cpp index 92a452a..0f6868e 100644 --- a/cpu/ppc/ppcmmu.cpp +++ b/cpu/ppc/ppcmmu.cpp @@ -13,7 +13,6 @@ - implement BAT access check - add proper error and exception handling - clarify what to do in the case of unaligned memory accesses - - remove dependency on MPC106 (use generic memory controller interface instead) */ #include @@ -98,56 +97,6 @@ void ppc_set_cur_instruction(const uint8_t* ptr) ppc_cur_instruction = READ_DWORD_BE_A(ptr); } -static inline void ppc_memstore_16bit(unsigned char* ptr, uint16_t value, uint32_t offset) { - if (ppc_state.ppc_msr & 1) { /* little-endian byte ordering */ - ptr[offset] = value & 0xFF; - ptr[offset + 1] = (value >> 8) & 0xFF; - } - else { /* big-endian byte ordering */ - ptr[offset] = (value >> 8) & 0xFF; - ptr[offset + 1] = value & 0xFF; - } -} - -static inline void ppc_memstore_32bit(unsigned char* ptr, uint32_t value, uint32_t offset) { - if (ppc_state.ppc_msr & 1) { /* little-endian byte ordering */ - ptr[offset] = value & 0xFF; - ptr[offset + 1] = (value >> 8) & 0xFF; - ptr[offset + 2] = (value >> 16) & 0xFF; - ptr[offset + 3] = (value >> 24) & 0xFF; - } - else { /* big-endian byte ordering */ - ptr[offset] = (value >> 24) & 0xFF; - ptr[offset + 1] = (value >> 16) & 0xFF; - ptr[offset + 2] = (value >> 8) & 0xFF; - ptr[offset + 3] = value & 0xFF; - } -} - -static inline void ppc_memstore_64bit(unsigned char* ptr, uint64_t value, uint32_t offset) { - if (ppc_state.ppc_msr & 1) { /* little-endian byte ordering */ - ptr[offset] = value & 0xFF; - ptr[offset + 1] = (value >> 8) & 0xFF; - ptr[offset + 2] = (value >> 16) & 0xFF; - ptr[offset + 3] = (value >> 24) & 0xFF; - ptr[offset + 4] = (value >> 32) & 0xFF; - ptr[offset + 5] = (value >> 40) & 0xFF; - ptr[offset + 6] = (value >> 48) & 0xFF; - ptr[offset + 7] = (value >> 56) & 0xFF; - } - else { /* big-endian byte ordering */ - ptr[offset] = (value >> 56) & 0xFF; - ptr[offset + 1] = (value >> 48) & 0xFF; - ptr[offset + 2] = (value >> 40) & 0xFF; - ptr[offset + 3] = (value >> 32) & 0xFF; - ptr[offset + 4] = (value >> 24) & 0xFF; - ptr[offset + 5] = (value >> 16) & 0xFF; - ptr[offset + 6] = (value >> 8) & 0xFF; - ptr[offset + 7] = value & 0xFF; - } -} - - void ibat_update(uint32_t bat_reg) { int upper_reg_num; @@ -401,6 +350,27 @@ static uint32_t ppc_mmu_addr_translate(uint32_t la, int is_write) return pa; } +static void mem_write_unaligned(uint32_t addr, uint32_t value, uint32_t size) +{ + printf("WARNING! Attempt to write unaligned %d bytes to 0x%08X\n", size, addr); + + if (((addr & 0xFFF) + size) > 0x1000) { + printf("SOS! Cross-page unaligned write, addr=%08X, size=%d\n", addr, size); + exit(-1); //FIXME! + } else { + /* data address translation if enabled */ + if (ppc_state.ppc_msr & 0x10) { + addr = ppc_mmu_addr_translate(addr, 0); + } + + if (size == 2) { + WRITE_PHYS_MEM(last_write_area, addr, WRITE_WORD_BE_U, value, 2); + } else { + WRITE_PHYS_MEM(last_write_area, addr, WRITE_DWORD_BE_U, value, 4); + } + } +} + void mem_write_byte(uint32_t addr, uint8_t value) { /* data address translation if enabled */ @@ -415,6 +385,10 @@ void mem_write_byte(uint32_t addr, uint8_t value) void mem_write_word(uint32_t addr, uint16_t value) { + if (addr & 1) { + mem_write_unaligned(addr, value, 2); + } + /* data address translation if enabled */ if (ppc_state.ppc_msr & 0x10) { addr = ppc_mmu_addr_translate(addr, 1); @@ -425,6 +399,10 @@ void mem_write_word(uint32_t addr, uint16_t value) void mem_write_dword(uint32_t addr, uint32_t value) { + if (addr & 3) { + mem_write_unaligned(addr, value, 4); + } + /* data address translation if enabled */ if (ppc_state.ppc_msr & 0x10) { addr = ppc_mmu_addr_translate(addr, 1); @@ -435,6 +413,11 @@ void mem_write_dword(uint32_t addr, uint32_t value) void mem_write_qword(uint32_t addr, uint64_t value) { + if (addr & 7) { + printf("SOS! Attempt to write unaligned QWORD to 0x%08X\n", addr); + exit(-1); //FIXME! + } + /* data address translation if enabled */ if (ppc_state.ppc_msr & 0x10) { addr = ppc_mmu_addr_translate(addr, 1); @@ -443,6 +426,31 @@ void mem_write_qword(uint32_t addr, uint64_t value) WRITE_PHYS_MEM(last_write_area, addr, WRITE_QWORD_BE_A, value, 8); } +static uint32_t mem_grab_unaligned(uint32_t addr, uint32_t size) +{ + uint32_t ret = 0; + + printf("WARNING! Attempt to read unaligned %d bytes from 0x%08X\n", size, addr); + + if (((addr & 0xFFF) + size) > 0x1000) { + printf("SOS! Cross-page unaligned read, addr=%08X, size=%d\n", addr, size); + exit(-1); //FIXME! + } else { + /* data address translation if enabled */ + if (ppc_state.ppc_msr & 0x10) { + addr = ppc_mmu_addr_translate(addr, 0); + } + + if (size == 2) { + READ_PHYS_MEM(last_read_area, addr, READ_WORD_BE_U, 2, 0xFFFFU); + } else { + READ_PHYS_MEM(last_read_area, addr, READ_DWORD_BE_U, 4, 0xFFFFFFFFUL); + } + } + + return ret; +} + /** Grab a value from memory into a register */ uint8_t mem_grab_byte(uint32_t addr) { @@ -461,6 +469,10 @@ uint16_t mem_grab_word(uint32_t addr) { uint16_t ret; + if (addr & 1) { + return mem_grab_unaligned(addr, 2); + } + /* data address translation if enabled */ if (ppc_state.ppc_msr & 0x10) { addr = ppc_mmu_addr_translate(addr, 0); @@ -474,6 +486,10 @@ uint32_t mem_grab_dword(uint32_t addr) { uint32_t ret; + if (addr & 3) { + return mem_grab_unaligned(addr, 4); + } + /* data address translation if enabled */ if (ppc_state.ppc_msr & 0x10) { addr = ppc_mmu_addr_translate(addr, 0); @@ -487,6 +503,11 @@ uint64_t mem_grab_qword(uint32_t addr) { uint64_t ret; + if (addr & 7) { + printf("SOS! Attempt to read unaligned QWORD at 0x%08X\n", addr); + exit(-1); //FIXME! + } + /* data address translation if enabled */ if (ppc_state.ppc_msr & 0x10) { addr = ppc_mmu_addr_translate(addr, 0); diff --git a/memreadwrite.h b/memreadwrite.h index b90e227..0593d93 100644 --- a/memreadwrite.h +++ b/memreadwrite.h @@ -62,4 +62,20 @@ /* write an aligned big-endian QWORD (64bit) */ #define WRITE_QWORD_BE_A(addr,val) (*((uint64_t *)((addr))) = BYTESWAP_64(val)) +/* write an unaligned big-endian WORD (16bit) */ +#define WRITE_WORD_BE_U(addr, val) \ +do { \ + (addr)[0] = ((val) >> 8) & 0xFF; \ + (addr)[1] = (val) & 0xFF; \ +} while(0) + +/* write an unaligned big-endian DWORD (32bit) */ +#define WRITE_DWORD_BE_U(addr, val) \ +do { \ + (addr)[0] = ((val) >> 24) & 0xFF; \ + (addr)[1] = ((val) >> 16) & 0xFF; \ + (addr)[2] = ((val) >> 8) & 0xFF; \ + (addr)[3] = (val) & 0xFF; \ +} while(0) + #endif /* MEM_READ_WRITE_H */