Commit 4ef37e69 authored by James Hogan's avatar James Hogan Committed by Paolo Bonzini
Browse files

target-mips: get_physical_address: Add KVM awareness



MIPS KVM trap & emulate mode (which is currently the only supported
mode) has to add an extra kseg0/kseg1 at 0x40000000 and an extra
kseg2/kseg3 at 0x60000000. Take this into account in
get_physical_address() so that debug memory access works.

This is done by translating the address to a standard kseg0 or kseg2
address before doing the normal address translation. The real virtual
address is still used for TLB lookups.

Signed-off-by: default avatarJames Hogan <james.hogan@imgtec.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 22010ce7
Loading
Loading
Loading
Loading
+26 −7
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <signal.h>

#include "cpu.h"
#include "sysemu/kvm.h"

enum {
    TLBRET_DIRTY = -4,
@@ -100,7 +101,7 @@ int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
}

static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
                                int *prot, target_ulong address,
                                int *prot, target_ulong real_address,
                                int rw, int access_type)
{
    /* User mode can only access useg/xuseg */
@@ -113,6 +114,8 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
    int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
#endif
    int ret = TLBRET_MATCH;
    /* effective address (modified for KVM T&E kernel segments) */
    target_ulong address = real_address;

#if 0
    qemu_log("user mode %d h %08x\n", user_mode, env->hflags);
@@ -124,19 +127,35 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
#define KSEG2_BASE      0xC0000000UL
#define KSEG3_BASE      0xE0000000UL

#define KVM_KSEG0_BASE  0x40000000UL
#define KVM_KSEG2_BASE  0x60000000UL

    if (kvm_enabled()) {
        /* KVM T&E adds guest kernel segments in useg */
        if (real_address >= KVM_KSEG0_BASE) {
            if (real_address < KVM_KSEG2_BASE) {
                /* kseg0 */
                address += KSEG0_BASE - KVM_KSEG0_BASE;
            } else if (real_address <= USEG_LIMIT) {
                /* kseg2/3 */
                address += KSEG2_BASE - KVM_KSEG2_BASE;
            }
        }
    }

    if (address <= USEG_LIMIT) {
        /* useg */
        if (env->CP0_Status & (1 << CP0St_ERL)) {
            *physical = address & 0xFFFFFFFF;
            *prot = PAGE_READ | PAGE_WRITE;
        } else {
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type);
        }
#if defined(TARGET_MIPS64)
    } else if (address < 0x4000000000000000ULL) {
        /* xuseg */
        if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) {
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
        }
@@ -144,7 +163,7 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
        /* xsseg */
        if ((supervisor_mode || kernel_mode) &&
            SX && address <= (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) {
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
        }
@@ -161,7 +180,7 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
        /* xkseg */
        if (kernel_mode && KX &&
            address <= (0xFFFFFFFF7FFFFFFFULL & env->SEGMask)) {
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
        }
@@ -185,7 +204,7 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
    } else if (address < (int32_t)KSEG3_BASE) {
        /* sseg (kseg2) */
        if (supervisor_mode || kernel_mode) {
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
        }
@@ -193,7 +212,7 @@ static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
        /* kseg3 */
        /* XXX: debug segment is not emulated */
        if (kernel_mode) {
            ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
        }