target/mips: Check memory permissions with mem_idx

When performing virtual to physical address translation, check the
required privilege level based on the mem_idx rather than the mode in
the hflags. This will allow EVA loads & stores to operate safely only on
user memory from kernel mode.

For the cases where the mmu_idx doesn't need to be overridden
(mips_cpu_get_phys_page_debug() and cpu_mips_translate_address()), we
calculate the required mmu_idx using cpu_mmu_index(). Note that this
only tests the MIPS_HFLAG_KSU bits rather than MIPS_HFLAG_MODE, so we
don't test the debug mode hflag MIPS_HFLAG_DM any longer. This should be
fine as get_physical_address() only compares against MIPS_HFLAG_UM and
MIPS_HFLAG_SM, neither of which should get set by compute_hflags() when
MIPS_HFLAG_DM is set.

Backports commit 9fbf4a58c90183b30bb2c8ad971ccce7e6716a16 from qemu
This commit is contained in:
James Hogan 2018-03-04 00:40:21 -05:00 committed by Lioncash
parent 54b349aee5
commit 8595d11eb4
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7

View file

@ -109,11 +109,11 @@ int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
static int get_physical_address (CPUMIPSState *env, hwaddr *physical, static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
int *prot, target_ulong real_address, int *prot, target_ulong real_address,
int rw, int access_type) int rw, int access_type, int mmu_idx)
{ {
/* User mode can only access useg/xuseg */ /* User mode can only access useg/xuseg */
int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM; int user_mode = mmu_idx == MIPS_HFLAG_UM;
int supervisor_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_SM; int supervisor_mode = mmu_idx == MIPS_HFLAG_SM;
int kernel_mode = !user_mode && !supervisor_mode; int kernel_mode = !user_mode && !supervisor_mode;
#if defined(TARGET_MIPS64) #if defined(TARGET_MIPS64)
int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
@ -403,11 +403,12 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{ {
MIPSCPU *cpu = MIPS_CPU(cs->uc, cs); MIPSCPU *cpu = MIPS_CPU(cs->uc, cs);
CPUMIPSState *env = &cpu->env;
hwaddr phys_addr; hwaddr phys_addr;
int prot; int prot;
if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT,
ACCESS_INT) != 0) { cpu_mmu_index(env, false)) != 0) {
return -1; return -1;
} }
return phys_addr; return phys_addr;
@ -438,7 +439,7 @@ int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
correctly */ correctly */
access_type = ACCESS_INT; access_type = ACCESS_INT;
ret = get_physical_address(env, &physical, &prot, ret = get_physical_address(env, &physical, &prot,
address, rw, access_type); address, rw, access_type, mmu_idx);
switch (ret) { switch (ret) {
case TLBRET_MATCH: case TLBRET_MATCH:
qemu_log_mask(CPU_LOG_MMU, qemu_log_mask(CPU_LOG_MMU,
@ -476,8 +477,8 @@ hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, int r
/* data access */ /* data access */
access_type = ACCESS_INT; access_type = ACCESS_INT;
ret = get_physical_address(env, &physical, &prot, ret = get_physical_address(env, &physical, &prot, address, rw, access_type,
address, rw, access_type); cpu_mmu_index(env, false));
if (ret != TLBRET_MATCH) { if (ret != TLBRET_MATCH) {
raise_mmu_exception(env, address, rw, ret); raise_mmu_exception(env, address, rw, ret);
return -1LL; return -1LL;