initial mmu support for sq remaps

This commit is contained in:
Stefanos Kornilios Mitsis Poiitidis 2017-07-09 03:40:08 +02:00 committed by Anthony Pesch
parent e06f450f02
commit 3fade7f1fb
13 changed files with 311 additions and 21 deletions

View File

@ -210,6 +210,7 @@ set(RELIB_SOURCES
src/guest/sh4/sh4_dbg.c
src/guest/sh4/sh4_dmac.c
src/guest/sh4/sh4_intc.c
src/guest/sh4/sh4_mmu.c
src/guest/sh4/sh4_tmu.c
src/guest/debugger.c
src/guest/dreamcast.c

View File

@ -165,6 +165,7 @@ static int sh4_init(struct device *dev) {
sh4->guest->mem = as_translate(sh4->memory_if->space, 0x0);
sh4->guest->space = sh4->memory_if->space;
sh4->guest->invalid_instr = &sh4_invalid_instr;
sh4->guest->load_tlb = &sh4_mmu_load_tlb;
sh4->guest->sq_prefetch = &sh4_ccn_sq_prefetch;
sh4->guest->sleep = &sh4_sleep;
sh4->guest->sr_updated = &sh4_sr_updated;
@ -212,6 +213,10 @@ void sh4_reset(struct sh4 *sh4, uint32_t pc) {
#include "guest/sh4/sh4_regs.inc"
#undef SH4_REG
/* reset tlb */
memset(sh4->utlb_sq_map, 0, sizeof(sh4->utlb_sq_map));
memset(sh4->utlb, 0, sizeof(sh4->utlb));
/* reset interrupts */
sh4_intc_reprioritize(sh4);
@ -351,16 +356,37 @@ AM_BEGIN(struct sh4, sh4_data_map)
AM_RANGE(0x80000000, 0x9fffffff) AM_MIRROR(0x00000000) /* p1 */
AM_RANGE(0xa0000000, 0xbfffffff) AM_MIRROR(0x00000000) /* p2 */
AM_RANGE(0xc0000000, 0xdfffffff) AM_MIRROR(0x00000000) /* p3 */
AM_RANGE(0xe0000000, 0xffffffff) AM_MIRROR(0x00000000) /* p4 */
/* internal cache and sq only accessible through p4 */
/* internal cache is only accessible through p0, not any of the mirrors */
AM_RANGE(0x7c000000, 0x7fffffff) AM_HANDLE("sh4 cache",
(mmio_read_cb)&sh4_ccn_cache_read,
(mmio_write_cb)&sh4_ccn_cache_write,
NULL, NULL)
/* p4 area */
AM_RANGE(0xe0000000, 0xe3ffffff) AM_HANDLE("sh4 sq",
(mmio_read_cb)&sh4_ccn_sq_read,
(mmio_write_cb)&sh4_ccn_sq_write,
NULL, NULL)
AM_RANGE(0xf0000000, 0xf1ffffff) AM_HANDLE("sh4 icache",
(mmio_read_cb)&sh4_ccn_icache_read,
(mmio_write_cb)&sh4_ccn_icache_write,
NULL, NULL)
AM_RANGE(0xf2000000, 0xf3ffffff) AM_HANDLE("sh4 itlb",
(mmio_read_cb)&sh4_mmu_itlb_read,
(mmio_write_cb)&sh4_mmu_itlb_write,
NULL, NULL)
AM_RANGE(0xf4000000, 0xf5ffffff) AM_HANDLE("sh4 ocache",
(mmio_read_cb)&sh4_ccn_ocache_read,
(mmio_write_cb)&sh4_ccn_ocache_write,
NULL, NULL)
AM_RANGE(0xf6000000, 0xf7ffffff) AM_HANDLE("sh4 utlb",
(mmio_read_cb)&sh4_mmu_utlb_read,
(mmio_write_cb)&sh4_mmu_utlb_write,
NULL, NULL)
AM_RANGE(0xfc000000, 0xffffffff) AM_HANDLE("sh4 reg",
(mmio_read_cb)&sh4_reg_read,
(mmio_write_cb)&sh4_reg_write,
NULL, NULL)
AM_END();
/* clang-format on */

View File

@ -36,6 +36,11 @@ struct sh4_dtr {
int size;
};
struct sh4_tlb_entry {
union pteh hi;
union ptel lo;
};
struct sh4 {
struct device;
@ -61,6 +66,10 @@ struct sh4 {
uint64_t requested_interrupts;
/* pending interrupts moved to context for fast jit access */
/* mmu */
uint32_t utlb_sq_map[64];
struct sh4_tlb_entry utlb[64];
/* tmu */
struct timer *tmu_timers[3];
};
@ -71,6 +80,7 @@ DECLARE_COUNTER(sh4_instrs);
AM_DECLARE(sh4_data_map);
/* ccn */
void sh4_ccn_sq_prefetch(void *data, uint32_t addr);
uint32_t sh4_ccn_cache_read(struct sh4 *sh4, uint32_t addr, uint32_t data_mask);
void sh4_ccn_cache_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
@ -78,7 +88,16 @@ void sh4_ccn_cache_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t sh4_ccn_sq_read(struct sh4 *sh4, uint32_t addr, uint32_t data_mask);
void sh4_ccn_sq_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t data_mask);
uint32_t sh4_ccn_icache_read(struct sh4 *sh4, uint32_t addr,
uint32_t data_mask);
void sh4_ccn_icache_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t data_mask);
uint32_t sh4_ccn_ocache_read(struct sh4 *sh4, uint32_t addr,
uint32_t data_mask);
void sh4_ccn_ocache_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t data_mask);
/* dbg */
int sh4_dbg_num_registers(struct device *dev);
void sh4_dbg_step(struct device *dev);
void sh4_dbg_add_breakpoint(struct device *dev, int type, uint32_t addr);
@ -91,10 +110,20 @@ int sh4_dbg_invalid_instr(struct sh4 *sh4);
void sh4_dmac_ddt(struct sh4 *sh, struct sh4_dtr *dtr);
/* intc */
void sh4_intc_update_pending(struct sh4 *sh4);
void sh4_intc_check_pending(void *data);
void sh4_intc_reprioritize(struct sh4 *sh4);
/* mmu */
void sh4_mmu_load_tlb(void *data);
uint32_t sh4_mmu_itlb_read(struct sh4 *sh4, uint32_t addr, uint32_t data_mask);
uint32_t sh4_mmu_utlb_read(struct sh4 *sh4, uint32_t addr, uint32_t data_mask);
void sh4_mmu_itlb_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t data_mask);
void sh4_mmu_utlb_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t data_mask);
struct sh4 *sh4_create(struct dreamcast *dc);
void sh4_destroy(struct sh4 *sh4);
void sh4_debug_menu(struct sh4 *sh4);

View File

@ -1,6 +1,12 @@
#include "guest/sh4/sh4.h"
#include "jit/jit.h"
#if 0
#define LOG_CCN LOG_INFO
#else
#define LOG_CCN(...)
#endif
/* with OIX, bit 25, rather than bit 13, determines which 4kb bank to use */
#define CACHE_OFFSET(addr, OIX) \
((OIX ? ((addr & 0x2000000) >> 13) : ((addr & 0x2000) >> 1)) | (addr & 0xfff))
@ -29,12 +35,27 @@ void sh4_ccn_sq_prefetch(void *data, uint32_t addr) {
DCHECK(addr >= 0xe0000000 && addr <= 0xe3ffffff);
struct sh4 *sh4 = data;
uint32_t dst = addr & 0x03ffffe0;
uint32_t dst = 0x0;
uint32_t sqi = (addr & 0x20) >> 5;
if (sqi) {
dst |= (*sh4->QACR1 & 0x1c) << 24;
if (sh4->MMUCR->AT) {
/* get upper 12 bits from UTLB */
uint32_t vpn = addr >> 20;
dst = sh4->utlb_sq_map[vpn & 0x3f];
/* get lower 20 bits from original address */
dst |= addr & 0xfffe0;
} else {
dst |= (*sh4->QACR0 & 0x1c) << 24;
/* get upper 6 bits from QACR* registers */
if (sqi) {
dst = (*sh4->QACR1 & 0x1c) << 24;
} else {
dst = (*sh4->QACR0 & 0x1c) << 24;
}
/* get lower 26 bits from original address */
dst |= addr & 0x3ffffe0;
}
as_memcpy_to_guest(sh4->memory_if->space, dst, sh4->ctx.sq[sqi], 32);
@ -69,10 +90,43 @@ void sh4_ccn_sq_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
sh4->ctx.sq[sqi][idx] = data;
}
uint32_t sh4_ccn_icache_read(struct sh4 *sh4, uint32_t addr,
uint32_t data_mask) {
LOG_CCN("sh4_ccn_icache_read 0x%08x", addr);
/* return an invalid entry */
return 0;
}
void sh4_ccn_icache_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t data_mask) {
LOG_CCN("sh4_ccn_icache_write 0x%08x", addr);
/* ignore */
}
uint32_t sh4_ccn_ocache_read(struct sh4 *sh4, uint32_t addr,
uint32_t data_mask) {
LOG_CCN("sh4_ccn_ocache_read 0x%08x", addr);
/* return an invalid entry */
return 0;
}
void sh4_ccn_ocache_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t data_mask) {
LOG_CCN("sh4_ccn_ocache_write 0x%08x", addr);
/* ignore */
}
REG_W32(sh4_cb, MMUCR) {
struct sh4 *sh4 = dc->sh4;
if (value) {
LOG_FATAL("MMU not currently supported");
sh4->MMUCR->full = value;
if (sh4->MMUCR->AT) {
LOG_WARNING("MMU not fully supported");
}
}

123
src/guest/sh4/sh4_mmu.c Normal file
View File

@ -0,0 +1,123 @@
#include "guest/sh4/sh4.h"
#if 0
#define LOG_MMU LOG_INFO
#else
#define LOG_MMU(...)
#endif
#define TLB_INDEX(addr) (((addr) >> 8) & 0x3f)
#define PAGE_SIZE(entry) (((entry)->lo.SZ1 << 1) | (entry)->lo.SZ0)
enum {
PAGE_SIZE_1KB,
PAGE_SIZE_4KB,
PAGE_SIZE_64KB,
PAGE_SIZE_1MB,
};
static void sh4_mmu_utlb_sync(struct sh4 *sh4, struct sh4_tlb_entry *entry) {
int n = entry - sh4->utlb;
/* check if entry maps to sq region [0xe0000000, 0xe3ffffff] */
if ((entry->hi.VPN & (0xfc000000 >> 10)) == (0xe0000000 >> 10)) {
/* assume page size is 1MB
FIXME support all page sizes */
uint32_t vpn = entry->hi.VPN >> 10;
uint32_t ppn = entry->lo.PPN << 10;
sh4->utlb_sq_map[vpn & 0x3f] = ppn;
LOG_INFO("sh4_mmu_utlb_sync sq map (%d) 0x%x -> 0x%x", n, vpn, ppn);
} else {
LOG_FATAL("sh4_mmu_utlb_sync memory mapping not supported");
}
}
static void sh4_mmu_translate(struct sh4 *sh4, uint32_t addr) {}
void sh4_mmu_load_tlb(void *data) {
struct sh4 *sh4 = data;
uint32_t n = sh4->MMUCR->URC;
struct sh4_tlb_entry *entry = &sh4->utlb[n];
entry->lo = *sh4->PTEL;
entry->hi = *sh4->PTEH;
sh4_mmu_utlb_sync(sh4, entry);
}
uint32_t sh4_mmu_itlb_read(struct sh4 *sh4, uint32_t addr, uint32_t data_mask) {
if (addr < 0x01000000) {
LOG_MMU("sh4_mmu_itlb_read address array %08x", addr);
} else {
LOG_MMU("sh4_mmu_itlb_read data array %08x", addr);
}
/* return an invalid entry */
return 0;
}
uint32_t sh4_mmu_utlb_read(struct sh4 *sh4, uint32_t addr, uint32_t data_mask) {
if (addr < 0x01000000) {
LOG_MMU("sh4_mmu_utlb_read address array %08x", addr);
struct sh4_tlb_entry *entry = &sh4->utlb[TLB_INDEX(addr)];
uint32_t data = entry->hi.full;
data |= entry->lo.D << 9;
data |= entry->lo.V << 8;
return data;
} else {
if (addr & 0x800000) {
LOG_FATAL("sh4_mmu_utlb_read data array 2 %08x", addr);
} else {
LOG_MMU("sh4_mmu_utlb_read data array 1 %08x", addr);
struct sh4_tlb_entry *entry = &sh4->utlb[TLB_INDEX(addr)];
uint32_t data = entry->lo.full;
return data;
}
}
}
void sh4_mmu_itlb_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t data_mask) {
if (addr < 0x01000000) {
LOG_MMU("sh4_mmu_itlb_write address array %08x %08x", addr, data);
} else {
LOG_MMU("sh4_mmu_itlb_write data array %08x %08x", addr, data);
}
/* ignore */
}
void sh4_mmu_utlb_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
uint32_t data_mask) {
if (addr < 0x01000000) {
if (addr & 0x80) {
LOG_FATAL("sh4_mmu_utlb_write address array (associative) %08x %08x",
addr, data);
} else {
LOG_MMU("sh4_mmu_utlb_write address array %08x %08x", addr, data);
struct sh4_tlb_entry *entry = &sh4->utlb[TLB_INDEX(addr)];
entry->hi.full = data & 0xfffffcff;
entry->lo.D = (data >> 9) & 1;
entry->lo.V = (data >> 8) & 1;
sh4_mmu_utlb_sync(sh4, entry);
}
} else {
if (addr & 0x800000) {
LOG_FATAL("sh4_mmu_utlb_write data array 2 %08x %08x", addr, data);
} else {
LOG_MMU("sh4_mmu_utlb_write data array 1 %08x %08x", addr, data);
struct sh4_tlb_entry *entry = &sh4->utlb[TLB_INDEX(addr)];
entry->lo.full = data;
sh4_mmu_utlb_sync(sh4, entry);
}
}
}

View File

@ -1,9 +1,9 @@
/* ADDR NAME DEFAULT TYPE */
SH4_REG(0xff000000, PTEH, 0x00000000, uint32_t)
SH4_REG(0xff000004, PTEL, 0x00000000, uint32_t)
SH4_REG(0xff000000, PTEH, 0x00000000, union pteh)
SH4_REG(0xff000004, PTEL, 0x00000000, union ptel)
SH4_REG(0xff000008, TTB, 0x00000000, uint32_t)
SH4_REG(0xff00000c, TEA, 0x00000000, uint32_t)
SH4_REG(0xff000010, MMUCR, 0x00000000, uint32_t)
SH4_REG(0xff000010, MMUCR, 0x00000000, union mmucr)
SH4_REG(0xff000014, BASRA, 0x00000000, uint32_t)
SH4_REG(0xff000018, BASRB, 0x00000000, uint32_t)
SH4_REG(0xff00001c, CCR, 0x00000000, union ccr)

View File

@ -3,6 +3,47 @@
#include <stdint.h>
union pteh {
uint32_t full;
struct {
uint32_t ASID : 8;
uint32_t : 2;
uint32_t VPN : 22;
};
};
union ptel {
uint32_t full;
struct {
uint32_t WT : 1;
uint32_t SH : 1;
uint32_t D : 1;
uint32_t C : 1;
uint32_t SZ0 : 1;
uint32_t PR : 2;
uint32_t SZ1 : 1;
uint32_t V : 1;
uint32_t : 1;
uint32_t PPN : 19;
uint32_t : 3;
};
};
union mmucr {
uint32_t full;
struct {
uint32_t AT : 1;
uint32_t : 1;
uint32_t TI : 1;
uint32_t : 5;
uint32_t SV : 1;
uint32_t SQMD : 1;
uint32_t URC : 6;
uint32_t URB : 6;
uint32_t LRUI : 6;
};
};
union ccr {
uint32_t full;
struct {
@ -10,16 +51,16 @@ union ccr {
uint32_t WT : 1;
uint32_t CB : 1;
uint32_t OCI : 1;
uint32_t reserved : 1;
uint32_t : 1;
uint32_t ORA : 1;
uint32_t reserved1 : 1;
uint32_t : 1;
uint32_t OIX : 1;
uint32_t ICE : 1;
uint32_t reserved2 : 2;
uint32_t : 2;
uint32_t ICI : 1;
uint32_t reserved3 : 3;
uint32_t : 3;
uint32_t IIX : 1;
uint32_t reserved4 : 15;
uint32_t : 15;
uint32_t EMODE : 1;
};
};
@ -40,7 +81,7 @@ union chcr {
uint32_t AM : 1;
uint32_t RL : 1;
uint32_t DS : 1;
uint32_t reserved : 4;
uint32_t : 4;
uint32_t DTC : 1;
uint32_t DSA : 3;
uint32_t STC : 1;
@ -54,13 +95,13 @@ union dmaor {
uint32_t DME : 1;
uint32_t NMIF : 1;
uint32_t AE : 1;
uint32_t reserved : 5;
uint32_t : 5;
uint32_t PR0 : 1;
uint32_t PR1 : 1;
uint32_t reserved1 : 4;
uint32_t : 4;
uint32_t DBL : 1;
uint32_t DDT : 1;
uint32_t reserved2 : 16;
uint32_t : 16;
};
};

View File

@ -143,7 +143,8 @@ void input_poll(struct host *base) {
/* retroarch's API provides a binary [0, 1] value for the triggers. map from
this to [0, INT16_MAX] as our host layer expects */
if (desc->id == RETRO_DEVICE_ID_JOYPAD_L2 || desc->id == RETRO_DEVICE_ID_JOYPAD_R2) {
if (desc->id == RETRO_DEVICE_ID_JOYPAD_L2 ||
desc->id == RETRO_DEVICE_ID_JOYPAD_R2) {
value = value ? INT16_MAX : 0;
}

View File

@ -387,6 +387,8 @@ typedef int32_t int128_t[4];
#define SLEEP() guest->sleep(guest->data)
#define LDTLB() guest->load_tlb(guest->data)
/* clang-format on */
#define INSTR(name) \

View File

@ -8,6 +8,7 @@ struct sh4_guest {
/* runtime interface */
void (*invalid_instr)(void *);
void (*load_tlb)(void *);
void (*sq_prefetch)(void *, uint32_t);
void (*sleep)(void *);
void (*sr_updated)(void *, uint32_t);

View File

@ -1379,6 +1379,11 @@ INSTR(SLEEP) {
SLEEP();
}
/* LDTLB */
INSTR(LDTLB) {
LDTLB();
}
/* STC SR,Rn */
INSTR(STCSR) {
I32 v = LOAD_SR_I32();

View File

@ -135,6 +135,7 @@ SH4_INSTR(RTS, "rts", 0000000000001011, 2, SH4_FL
// system control instructions
SH4_INSTR(LDTLB, "ldtlb", 0000000000111000, 1, 0)
SH4_INSTR(CLRMAC, "clrmac", 0000000000101000, 1, 0)
SH4_INSTR(CLRS, "clrs", 0000000001001000, 1, 0)
SH4_INSTR(CLRT, "clrt", 0000000000001000, 1, 0)

View File

@ -441,6 +441,12 @@ static void store_fpscr(struct sh4_guest *guest, struct ir *ir,
struct ir_value *data = ir_alloc_i64(ir, (uint64_t)guest->data); \
ir_call_1(ir, sleep, data); \
}
#define LDTLB() { \
struct ir_value *load_tlb = ir_alloc_i64(ir, (uint64_t)guest->load_tlb); \
struct ir_value *data = ir_alloc_i64(ir, (uint64_t)guest->data); \
ir_call_1(ir, load_tlb, data); \
}
/* clang-format on */