target/m68k: add MC68040 MMU

Only add MC68040 MMU page table processing and related
registers (Special Status Word, Translation Control Register,
User Root Pointer and Supervisor Root Pointer).

Transparent Translation Registers, DFC/SFC and pflush/ptest
will be added later.

Backports commit 88b2fef6c3c3b45ac0dc2196ace7248a09c8e41d from qemu
This commit is contained in:
Laurent Vivier 2018-03-06 10:58:22 -05:00 committed by Lioncash
parent 0aecb15f3b
commit ee10680bf9
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
4 changed files with 419 additions and 16 deletions

View file

@ -263,9 +263,9 @@ static void m68k_cpu_class_init(struct uc_struct *uc, ObjectClass *c, void *data
cc->do_interrupt = m68k_cpu_do_interrupt; cc->do_interrupt = m68k_cpu_do_interrupt;
cc->cpu_exec_interrupt = m68k_cpu_exec_interrupt; cc->cpu_exec_interrupt = m68k_cpu_exec_interrupt;
cc->set_pc = m68k_cpu_set_pc; cc->set_pc = m68k_cpu_set_pc;
#ifdef CONFIG_USER_ONLY
cc->handle_mmu_fault = m68k_cpu_handle_mmu_fault; cc->handle_mmu_fault = m68k_cpu_handle_mmu_fault;
#else #if defined(CONFIG_SOFTMMU)
cc->do_unassigned_access = m68k_cpu_unassigned_access;
cc->get_phys_page_debug = m68k_cpu_get_phys_page_debug; cc->get_phys_page_debug = m68k_cpu_get_phys_page_debug;
#endif #endif
cc->tcg_initialize = m68k_tcg_init; cc->tcg_initialize = m68k_tcg_init;

View file

@ -118,6 +118,12 @@ typedef struct CPUM68KState {
/* MMU status. */ /* MMU status. */
struct { struct {
uint32_t ar; uint32_t ar;
uint32_t ssw;
/* 68040 */
uint16_t tcr;
uint32_t urp;
uint32_t srp;
bool fault;
} mmu; } mmu;
/* Control registers. */ /* Control registers. */
@ -223,6 +229,92 @@ typedef enum {
#define M68K_USP 1 #define M68K_USP 1
#define M68K_ISP 2 #define M68K_ISP 2
/* bits for 68040 special status word */
#define M68K_CP_040 0x8000
#define M68K_CU_040 0x4000
#define M68K_CT_040 0x2000
#define M68K_CM_040 0x1000
#define M68K_MA_040 0x0800
#define M68K_ATC_040 0x0400
#define M68K_LK_040 0x0200
#define M68K_RW_040 0x0100
#define M68K_SIZ_040 0x0060
#define M68K_TT_040 0x0018
#define M68K_TM_040 0x0007
#define M68K_TM_040_DATA 0x0001
#define M68K_TM_040_CODE 0x0002
#define M68K_TM_040_SUPER 0x0004
/* bits for 68040 write back status word */
#define M68K_WBV_040 0x80
#define M68K_WBSIZ_040 0x60
#define M68K_WBBYT_040 0x20
#define M68K_WBWRD_040 0x40
#define M68K_WBLNG_040 0x00
#define M68K_WBTT_040 0x18
#define M68K_WBTM_040 0x07
/* bus access size codes */
#define M68K_BA_SIZE_MASK 0x60
#define M68K_BA_SIZE_BYTE 0x20
#define M68K_BA_SIZE_WORD 0x40
#define M68K_BA_SIZE_LONG 0x00
#define M68K_BA_SIZE_LINE 0x60
/* bus access transfer type codes */
#define M68K_BA_TT_MOVE16 0x08
/* bits for 68040 MMU status register (mmusr) */
#define M68K_MMU_B_040 0x0800
#define M68K_MMU_G_040 0x0400
#define M68K_MMU_U1_040 0x0200
#define M68K_MMU_U0_040 0x0100
#define M68K_MMU_S_040 0x0080
#define M68K_MMU_CM_040 0x0060
#define M68K_MMU_M_040 0x0010
#define M68K_MMU_WP_040 0x0004
#define M68K_MMU_T_040 0x0002
#define M68K_MMU_R_040 0x0001
#define M68K_MMU_SR_MASK_040 (M68K_MMU_G_040 | M68K_MMU_U1_040 | \
M68K_MMU_U0_040 | M68K_MMU_S_040 | \
M68K_MMU_CM_040 | M68K_MMU_M_040 | \
M68K_MMU_WP_040)
/* bits for 68040 MMU Translation Control Register */
#define M68K_TCR_ENABLED 0x8000
#define M68K_TCR_PAGE_8K 0x4000
/* bits for 68040 MMU Table Descriptor / Page Descriptor / TTR */
#define M68K_DESC_WRITEPROT 0x00000004
#define M68K_DESC_USED 0x00000008
#define M68K_DESC_MODIFIED 0x00000010
#define M68K_DESC_CACHEMODE 0x00000060
#define M68K_DESC_CM_WRTHRU 0x00000000
#define M68K_DESC_CM_COPYBK 0x00000020
#define M68K_DESC_CM_SERIAL 0x00000040
#define M68K_DESC_CM_NCACHE 0x00000060
#define M68K_DESC_SUPERONLY 0x00000080
#define M68K_DESC_USERATTR 0x00000300
#define M68K_DESC_USERATTR_SHIFT 8
#define M68K_DESC_GLOBAL 0x00000400
#define M68K_DESC_URESERVED 0x00000800
#define M68K_4K_PAGE_MASK (~0xff)
#define M68K_POINTER_BASE(entry) (entry & ~0x1ff)
#define M68K_ROOT_INDEX(addr) ((address >> 23) & 0x1fc)
#define M68K_POINTER_INDEX(addr) ((address >> 16) & 0x1fc)
#define M68K_4K_PAGE_BASE(entry) (next & M68K_4K_PAGE_MASK)
#define M68K_4K_PAGE_INDEX(addr) ((address >> 10) & 0xfc)
#define M68K_8K_PAGE_MASK (~0x7f)
#define M68K_8K_PAGE_BASE(entry) (next & M68K_8K_PAGE_MASK)
#define M68K_8K_PAGE_INDEX(addr) ((address >> 11) & 0x7c)
#define M68K_UDT_VALID(entry) (entry & 2)
#define M68K_PDT_VALID(entry) (entry & 3)
#define M68K_PDT_INDIRECT(entry) ((entry & 3) == 2)
#define M68K_INDIRECT_POINTER(addr) (addr & ~3)
/* m68k Control Registers */ /* m68k Control Registers */
/* ColdFire */ /* ColdFire */
@ -384,16 +476,23 @@ void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf);
void register_m68k_insns (CPUM68KState *env); void register_m68k_insns (CPUM68KState *env);
#ifdef CONFIG_USER_ONLY
/* Coldfire Linux uses 8k pages /* Coldfire Linux uses 8k pages
* and m68k linux uses 4k pages * and m68k linux uses 4k pages
* use the smaller one * use the smallest one
*/ */
#define TARGET_PAGE_BITS 12 #define TARGET_PAGE_BITS 12
#else
/* Smallest TLB entry size is 1k. */ enum {
#define TARGET_PAGE_BITS 10 /* 1 bit to define user level / supervisor access */
#endif ACCESS_SUPER = 0x01,
/* 1 bit to indicate direction */
ACCESS_STORE = 0x02,
/* 1 bit to indicate debug access */
ACCESS_DEBUG = 0x04,
/* Type of instruction that generated the access */
ACCESS_CODE = 0x10, /* Code fetch access */
ACCESS_DATA = 0x20, /* Data load/store access */
};
#define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_PHYS_ADDR_SPACE_BITS 32
#define TARGET_VIRT_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32
@ -414,6 +513,7 @@ static inline CPUM68KState *cpu_init(struct uc_struct *uc, const char *cpu_model
/* MMU modes definitions */ /* MMU modes definitions */
#define MMU_MODE0_SUFFIX _kernel #define MMU_MODE0_SUFFIX _kernel
#define MMU_MODE1_SUFFIX _user #define MMU_MODE1_SUFFIX _user
#define MMU_KERNEL_IDX 0
#define MMU_USER_IDX 1 #define MMU_USER_IDX 1
static inline int cpu_mmu_index (CPUM68KState *env, bool ifetch) static inline int cpu_mmu_index (CPUM68KState *env, bool ifetch)
{ {
@ -422,6 +522,9 @@ static inline int cpu_mmu_index (CPUM68KState *env, bool ifetch)
int m68k_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, int m68k_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw,
int mmu_idx); int mmu_idx);
void m68k_cpu_unassigned_access(CPUState *cs, hwaddr addr,
bool is_write, bool is_exec, int is_asi,
unsigned size);
#include "exec/cpu-all.h" #include "exec/cpu-all.h"

View file

@ -87,6 +87,15 @@ void HELPER(m68k_movec_to)(CPUM68KState *env, uint32_t reg, uint32_t val)
m68k_switch_sp(env); m68k_switch_sp(env);
return; return;
/* MC680[34]0 */ /* MC680[34]0 */
case M68K_CR_TC:
env->mmu.tcr = val;
return;
case M68K_CR_SRP:
env->mmu.srp = val;
return;
case M68K_CR_URP:
env->mmu.urp = val;
return;
case M68K_CR_USP: case M68K_CR_USP:
env->sp[M68K_USP] = val; env->sp[M68K_USP] = val;
return; return;
@ -113,12 +122,19 @@ uint32_t HELPER(m68k_movec_from)(CPUM68KState *env, uint32_t reg)
case M68K_CR_CACR: case M68K_CR_CACR:
return env->cacr; return env->cacr;
/* MC680[34]0 */ /* MC680[34]0 */
case M68K_CR_TC:
return env->mmu.tcr;
case M68K_CR_SRP:
return env->mmu.srp;
case M68K_CR_USP: case M68K_CR_USP:
return env->sp[M68K_USP]; return env->sp[M68K_USP];
case M68K_CR_MSP: case M68K_CR_MSP:
return env->sp[M68K_SSP]; return env->sp[M68K_SSP];
case M68K_CR_ISP: case M68K_CR_ISP:
return env->sp[M68K_ISP]; return env->sp[M68K_ISP];
/* MC68040/MC68LC040 */
case M68K_CR_URP:
return env->mmu.urp;
} }
cpu_abort(CPU(cpu), "Unimplemented control register read 0x%x\n", cpu_abort(CPU(cpu), "Unimplemented control register read 0x%x\n",
reg); reg);
@ -195,25 +211,217 @@ int m68k_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw,
#else #else
/* MMU */ /* MMU: 68040 only */
static int get_physical_address(CPUM68KState *env, hwaddr *physical,
int *prot, target_ulong address,
int access_type, target_ulong *page_size)
{
M68kCPU *cpu = m68k_env_get_cpu(env);
CPUState *cs = CPU(cpu);
uint32_t entry;
uint32_t next;
target_ulong page_mask;
bool debug = access_type & ACCESS_DEBUG;
int page_bits;
/* Page Table Root Pointer */
*prot = PAGE_READ | PAGE_WRITE;
if (access_type & ACCESS_CODE) {
*prot |= PAGE_EXEC;
}
if (access_type & ACCESS_SUPER) {
next = env->mmu.srp;
} else {
next = env->mmu.urp;
}
/* Root Index */
entry = M68K_POINTER_BASE(next) | M68K_ROOT_INDEX(address);
next = ldl_phys(cs->as, entry);
if (!M68K_UDT_VALID(next)) {
return -1;
}
if (!(next & M68K_DESC_USED) && !debug) {
stl_phys(cs->as, entry, next | M68K_DESC_USED);
}
if (next & M68K_DESC_WRITEPROT) {
*prot &= ~PAGE_WRITE;
if (access_type & ACCESS_STORE) {
return -1;
}
}
/* Pointer Index */
entry = M68K_POINTER_BASE(next) | M68K_POINTER_INDEX(address);
next = ldl_phys(cs->as, entry);
if (!M68K_UDT_VALID(next)) {
return -1;
}
if (!(next & M68K_DESC_USED) && !debug) {
stl_phys(cs->as, entry, next | M68K_DESC_USED);
}
if (next & M68K_DESC_WRITEPROT) {
*prot &= ~PAGE_WRITE;
if (access_type & ACCESS_STORE) {
return -1;
}
}
/* Page Index */
if (env->mmu.tcr & M68K_TCR_PAGE_8K) {
entry = M68K_8K_PAGE_BASE(next) | M68K_8K_PAGE_INDEX(address);
} else {
entry = M68K_4K_PAGE_BASE(next) | M68K_4K_PAGE_INDEX(address);
}
next = ldl_phys(cs->as, entry);
if (!M68K_PDT_VALID(next)) {
return -1;
}
if (M68K_PDT_INDIRECT(next)) {
next = ldl_phys(cs->as, M68K_INDIRECT_POINTER(next));
}
if (access_type & ACCESS_STORE) {
if (next & M68K_DESC_WRITEPROT) {
if (!(next & M68K_DESC_USED) && !debug) {
stl_phys(cs->as, entry, next | M68K_DESC_USED);
}
} else if ((next & (M68K_DESC_MODIFIED | M68K_DESC_USED)) !=
(M68K_DESC_MODIFIED | M68K_DESC_USED) && !debug) {
stl_phys(cs->as, entry,
next | (M68K_DESC_MODIFIED | M68K_DESC_USED));
}
} else {
if (!(next & M68K_DESC_USED) && !debug) {
stl_phys(cs->as, entry, next | M68K_DESC_USED);
}
}
if (env->mmu.tcr & M68K_TCR_PAGE_8K) {
page_bits = 13;
} else {
page_bits = 12;
}
*page_size = 1 << page_bits;
page_mask = ~(*page_size - 1);
*physical = next & page_mask;
if (next & M68K_DESC_WRITEPROT) {
*prot &= ~PAGE_WRITE;
if (access_type & ACCESS_STORE) {
return -1;
}
}
if (next & M68K_DESC_SUPERONLY) {
if ((access_type & ACCESS_SUPER) == 0) {
return -1;
}
}
return 0;
}
/* TODO: This will need fixing once the MMU is implemented. */
hwaddr m68k_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) hwaddr m68k_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{ {
M68kCPU *cpu = M68K_CPU(cs->uc, cs);
CPUM68KState *env = &cpu->env;
hwaddr phys_addr;
int prot;
int access_type;
target_ulong page_size;
if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) {
/* MMU disabled */
return addr; return addr;
} }
access_type = ACCESS_DATA | ACCESS_DEBUG;
if (env->sr & SR_S) {
access_type |= ACCESS_SUPER;
}
if (get_physical_address(env, &phys_addr, &prot,
addr, access_type, &page_size) != 0) {
return -1;
}
return phys_addr;
}
int m68k_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, int m68k_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw,
int mmu_idx) int mmu_idx)
{ {
M68kCPU *cpu = M68K_CPU(cs->uc, cs);
CPUM68KState *env = &cpu->env;
hwaddr physical;
int prot; int prot;
int access_type;
int ret;
target_ulong page_size;
address &= TARGET_PAGE_MASK; if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) {
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; /* MMU disabled */
tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); tlb_set_page(cs, address & TARGET_PAGE_MASK,
address & TARGET_PAGE_MASK,
PAGE_READ | PAGE_WRITE | PAGE_EXEC,
mmu_idx, TARGET_PAGE_SIZE);
return 0; return 0;
} }
if (rw == 2) {
access_type = ACCESS_CODE;
rw = 0;
} else {
access_type = ACCESS_DATA;
if (rw) {
access_type |= ACCESS_STORE;
}
}
if (mmu_idx != MMU_USER_IDX) {
access_type |= ACCESS_SUPER;
}
ret = get_physical_address(&cpu->env, &physical, &prot,
address, access_type, &page_size);
if (ret == 0) {
address &= TARGET_PAGE_MASK;
physical += address & (page_size - 1);
tlb_set_page(cs, address, physical,
prot, mmu_idx, TARGET_PAGE_SIZE);
return 0;
}
/* page fault */
env->mmu.ssw = M68K_ATC_040;
switch (size) {
case 1:
env->mmu.ssw |= M68K_BA_SIZE_BYTE;
break;
case 2:
env->mmu.ssw |= M68K_BA_SIZE_WORD;
break;
case 4:
env->mmu.ssw |= M68K_BA_SIZE_LONG;
break;
}
if (access_type & ACCESS_SUPER) {
env->mmu.ssw |= M68K_TM_040_SUPER;
}
if (access_type & ACCESS_CODE) {
env->mmu.ssw |= M68K_TM_040_CODE;
} else {
env->mmu.ssw |= M68K_TM_040_DATA;
}
if (!(access_type & ACCESS_STORE)) {
env->mmu.ssw |= M68K_RW_040;
}
env->mmu.ar = address;
cs->exception_index = EXCP_ACCESS;
return 1;
}
/* Notify CPU of a pending interrupt. Prioritization and vectoring should /* Notify CPU of a pending interrupt. Prioritization and vectoring should
be handled by the interrupt controller. Real hardware only requests be handled by the interrupt controller. Real hardware only requests
the vector when the interrupt is acknowledged by the CPU. For the vector when the interrupt is acknowledged by the CPU. For

View file

@ -369,7 +369,49 @@ static void m68k_interrupt_all(CPUM68KState *env, int is_hw)
sp = env->aregs[7]; sp = env->aregs[7];
sp &= ~1; sp &= ~1;
if (cs->exception_index == EXCP_ADDRESS) { if (cs->exception_index == EXCP_ACCESS) {
if (env->mmu.fault) {
cpu_abort(cs, "DOUBLE MMU FAULT\n");
}
env->mmu.fault = true;
sp -= 4;
cpu_stl_kernel(env, sp, 0); /* push data 3 */
sp -= 4;
cpu_stl_kernel(env, sp, 0); /* push data 2 */
sp -= 4;
cpu_stl_kernel(env, sp, 0); /* push data 1 */
sp -= 4;
cpu_stl_kernel(env, sp, 0); /* write back 1 / push data 0 */
sp -= 4;
cpu_stl_kernel(env, sp, 0); /* write back 1 address */
sp -= 4;
cpu_stl_kernel(env, sp, 0); /* write back 2 data */
sp -= 4;
cpu_stl_kernel(env, sp, 0); /* write back 2 address */
sp -= 4;
cpu_stl_kernel(env, sp, 0); /* write back 3 data */
sp -= 4;
cpu_stl_kernel(env, sp, env->mmu.ar); /* write back 3 address */
sp -= 4;
cpu_stl_kernel(env, sp, env->mmu.ar); /* fault address */
sp -= 2;
cpu_stw_kernel(env, sp, 0); /* write back 1 status */
sp -= 2;
cpu_stw_kernel(env, sp, 0); /* write back 2 status */
sp -= 2;
cpu_stw_kernel(env, sp, 0); /* write back 3 status */
sp -= 2;
cpu_stw_kernel(env, sp, env->mmu.ssw); /* special status word */
sp -= 4;
cpu_stl_kernel(env, sp, env->mmu.ar); /* effective address */
do_stack_frame(env, &sp, 7, oldsr, 0, retaddr);
env->mmu.fault = false;
if (qemu_loglevel_mask(CPU_LOG_INT)) {
qemu_log(" "
"ssw: %08x ea: %08x\n",
env->mmu.ssw, env->mmu.ar);
}
} else if (cs->exception_index == EXCP_ADDRESS) {
do_stack_frame(env, &sp, 2, oldsr, 0, retaddr); do_stack_frame(env, &sp, 2, oldsr, 0, retaddr);
} else if (cs->exception_index == EXCP_ILLEGAL || } else if (cs->exception_index == EXCP_ILLEGAL ||
cs->exception_index == EXCP_DIV0 || cs->exception_index == EXCP_DIV0 ||
@ -417,6 +459,56 @@ static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
{ {
do_interrupt_all(env, 1); do_interrupt_all(env, 1);
} }
void m68k_cpu_unassigned_access(CPUState *cs, hwaddr addr, bool is_write,
bool is_exec, int is_asi, unsigned size)
{
M68kCPU *cpu = M68K_CPU(cs->uc, cs);
CPUM68KState *env = &cpu->env;
#ifdef DEBUG_UNASSIGNED
qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n",
addr, is_write, is_exec);
#endif
if (env == NULL) {
/* when called from gdb, env is NULL */
return;
}
if (m68k_feature(env, M68K_FEATURE_M68040)) {
env->mmu.ssw |= M68K_ATC_040;
/* FIXME: manage MMU table access error */
env->mmu.ssw &= ~M68K_TM_040;
if (env->sr & SR_S) { /* SUPERVISOR */
env->mmu.ssw |= M68K_TM_040_SUPER;
}
if (is_exec) { /* instruction or data */
env->mmu.ssw |= M68K_TM_040_CODE;
} else {
env->mmu.ssw |= M68K_TM_040_DATA;
}
env->mmu.ssw &= ~M68K_BA_SIZE_MASK;
switch (size) {
case 1:
env->mmu.ssw |= M68K_BA_SIZE_BYTE;
break;
case 2:
env->mmu.ssw |= M68K_BA_SIZE_WORD;
break;
case 4:
env->mmu.ssw |= M68K_BA_SIZE_LONG;
break;
}
if (!is_write) {
env->mmu.ssw |= M68K_RW_040;
}
env->mmu.ar = addr;
cs->exception_index = EXCP_ACCESS;
cpu_loop_exit(cs);
}
}
#endif #endif
bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request) bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request)