]> Git Repo - qemu.git/blobdiff - target-mips/helper.c
target-mips: Remove unused gen_load_ACX, gen_store_ACX and cpu_ACX
[qemu.git] / target-mips / helper.c
index 8102f03c264ff5854a6342f770c06fbfc8bd6684..fe16820885773b8bbe02dc190c1c4047c2245306 100644 (file)
@@ -24,7 +24,7 @@
 #include <signal.h>
 
 #include "cpu.h"
-#include "exec-all.h"
+#include "sysemu/kvm.h"
 
 enum {
     TLBRET_DIRTY = -4,
@@ -37,7 +37,7 @@ enum {
 #if !defined(CONFIG_USER_ONLY)
 
 /* no MMU emulation */
-int no_mmu_map_address (CPUState *env, target_phys_addr_t *physical, int *prot,
+int no_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
                         target_ulong address, int rw, int access_type)
 {
     *physical = address;
@@ -46,7 +46,7 @@ int no_mmu_map_address (CPUState *env, target_phys_addr_t *physical, int *prot,
 }
 
 /* fixed mapping MMU emulation */
-int fixed_mmu_map_address (CPUState *env, target_phys_addr_t *physical, int *prot,
+int fixed_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
                            target_ulong address, int rw, int access_type)
 {
     if (address <= (int32_t)0x7FFFFFFFUL) {
@@ -64,7 +64,7 @@ int fixed_mmu_map_address (CPUState *env, target_phys_addr_t *physical, int *pro
 }
 
 /* MIPS32/MIPS64 R4000-style MMU emulation */
-int r4k_map_address (CPUState *env, target_phys_addr_t *physical, int *prot,
+int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
                      target_ulong address, int rw, int access_type)
 {
     uint8_t ASID = env->CP0_EntryHi & 0xFF;
@@ -100,8 +100,8 @@ int r4k_map_address (CPUState *env, target_phys_addr_t *physical, int *prot,
     return TLBRET_NOMATCH;
 }
 
-static int get_physical_address (CPUState *env, target_phys_addr_t *physical,
-                                int *prot, target_ulong address,
+static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
+                                int *prot, target_ulong real_address,
                                 int rw, int access_type)
 {
     /* User mode can only access useg/xuseg */
@@ -114,24 +114,48 @@ static int get_physical_address (CPUState *env, target_phys_addr_t *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);
 #endif
 
-    if (address <= (int32_t)0x7FFFFFFFUL) {
+#define USEG_LIMIT      0x7FFFFFFFUL
+#define KSEG0_BASE      0x80000000UL
+#define KSEG1_BASE      0xA0000000UL
+#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;
         }
@@ -139,7 +163,7 @@ static int get_physical_address (CPUState *env, target_phys_addr_t *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;
         }
@@ -156,31 +180,31 @@ static int get_physical_address (CPUState *env, target_phys_addr_t *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;
         }
 #endif
-    } else if (address < (int32_t)0xA0000000UL) {
+    } else if (address < (int32_t)KSEG1_BASE) {
         /* kseg0 */
         if (kernel_mode) {
-            *physical = address - (int32_t)0x80000000UL;
+            *physical = address - (int32_t)KSEG0_BASE;
             *prot = PAGE_READ | PAGE_WRITE;
         } else {
             ret = TLBRET_BADADDR;
         }
-    } else if (address < (int32_t)0xC0000000UL) {
+    } else if (address < (int32_t)KSEG2_BASE) {
         /* kseg1 */
         if (kernel_mode) {
-            *physical = address - (int32_t)0xA0000000UL;
+            *physical = address - (int32_t)KSEG1_BASE;
             *prot = PAGE_READ | PAGE_WRITE;
         } else {
             ret = TLBRET_BADADDR;
         }
-    } else if (address < (int32_t)0xE0000000UL) {
+    } 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;
         }
@@ -188,13 +212,13 @@ static int get_physical_address (CPUState *env, target_phys_addr_t *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;
         }
     }
 #if 0
-    qemu_log(TARGET_FMT_lx " %d %d => " TARGET_FMT_lx " %d (%d)\n",
+    qemu_log(TARGET_FMT_lx " %d %d => %" HWADDR_PRIx " %d (%d)\n",
             address, rw, access_type, *physical, *prot, ret);
 #endif
 
@@ -202,9 +226,10 @@ static int get_physical_address (CPUState *env, target_phys_addr_t *physical,
 }
 #endif
 
-static void raise_mmu_exception(CPUState *env, target_ulong address,
+static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
                                 int rw, int tlb_error)
 {
+    CPUState *cs = CPU(mips_env_get_cpu(env));
     int exception = 0, error_code = 0;
 
     switch (tlb_error) {
@@ -250,56 +275,60 @@ static void raise_mmu_exception(CPUState *env, target_ulong address,
                         ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) |
                         ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9);
 #endif
-    env->exception_index = exception;
+    cs->exception_index = exception;
     env->error_code = error_code;
 }
 
 #if !defined(CONFIG_USER_ONLY)
-target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 {
-    target_phys_addr_t phys_addr;
+    MIPSCPU *cpu = MIPS_CPU(cs);
+    hwaddr phys_addr;
     int prot;
 
-    if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
+    if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0,
+                             ACCESS_INT) != 0) {
         return -1;
+    }
     return phys_addr;
 }
 #endif
 
-int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
-                               int mmu_idx, int is_softmmu)
+int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
+                              int mmu_idx)
 {
+    MIPSCPU *cpu = MIPS_CPU(cs);
+    CPUMIPSState *env = &cpu->env;
 #if !defined(CONFIG_USER_ONLY)
-    target_phys_addr_t physical;
+    hwaddr physical;
     int prot;
-#endif
     int access_type;
+#endif
     int ret = 0;
 
 #if 0
-    log_cpu_state(env, 0);
+    log_cpu_state(cs, 0);
 #endif
-    qemu_log("%s pc " TARGET_FMT_lx " ad " TARGET_FMT_lx " rw %d mmu_idx %d smmu %d\n",
-              __func__, env->active_tc.PC, address, rw, mmu_idx, is_softmmu);
+    qemu_log("%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " rw %d mmu_idx %d\n",
+              __func__, env->active_tc.PC, address, rw, mmu_idx);
 
     rw &= 1;
 
     /* data access */
+#if !defined(CONFIG_USER_ONLY)
     /* XXX: put correct access by using cpu_restore_state()
        correctly */
     access_type = ACCESS_INT;
-#if defined(CONFIG_USER_ONLY)
-    ret = TLBRET_NOMATCH;
-#else
     ret = get_physical_address(env, &physical, &prot,
                                address, rw, access_type);
-    qemu_log("%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_plx " prot %d\n",
-              __func__, address, ret, physical, prot);
+    qemu_log("%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx
+             " prot %d\n",
+             __func__, address, ret, physical, prot);
     if (ret == TLBRET_MATCH) {
-       tlb_set_page(env, address & TARGET_PAGE_MASK,
-                    physical & TARGET_PAGE_MASK, prot | PAGE_EXEC,
-                    mmu_idx, TARGET_PAGE_SIZE);
-       ret = 0;
+        tlb_set_page(cs, address & TARGET_PAGE_MASK,
+                     physical & TARGET_PAGE_MASK, prot | PAGE_EXEC,
+                     mmu_idx, TARGET_PAGE_SIZE);
+        ret = 0;
     } else if (ret < 0)
 #endif
     {
@@ -311,9 +340,9 @@ int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
 }
 
 #if !defined(CONFIG_USER_ONLY)
-target_phys_addr_t cpu_mips_translate_address(CPUState *env, target_ulong address, int rw)
+hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, int rw)
 {
-    target_phys_addr_t physical;
+    hwaddr physical;
     int prot;
     int access_type;
     int ret = 0;
@@ -369,8 +398,7 @@ static const char * const excp_names[EXCP_LAST + 1] = {
     [EXCP_CACHE] = "cache error",
 };
 
-#if !defined(CONFIG_USER_ONLY)
-static target_ulong exception_resume_pc (CPUState *env)
+target_ulong exception_resume_pc (CPUMIPSState *env)
 {
     target_ulong bad_pc;
     target_ulong isa_mode;
@@ -385,29 +413,46 @@ static target_ulong exception_resume_pc (CPUState *env)
 
     return bad_pc;
 }
+
+#if !defined(CONFIG_USER_ONLY)
+static void set_hflags_for_handler (CPUMIPSState *env)
+{
+    /* Exception handlers are entered in 32-bit mode.  */
+    env->hflags &= ~(MIPS_HFLAG_M16);
+    /* ...except that microMIPS lets you choose.  */
+    if (env->insn_flags & ASE_MICROMIPS) {
+        env->hflags |= (!!(env->CP0_Config3
+                           & (1 << CP0C3_ISA_ON_EXC))
+                        << MIPS_HFLAG_M16_SHIFT);
+    }
+}
 #endif
 
-void do_interrupt (CPUState *env)
+void mips_cpu_do_interrupt(CPUState *cs)
 {
 #if !defined(CONFIG_USER_ONLY)
+    MIPSCPU *cpu = MIPS_CPU(cs);
+    CPUMIPSState *env = &cpu->env;
     target_ulong offset;
     int cause = -1;
     const char *name;
 
-    if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
-        if (env->exception_index < 0 || env->exception_index > EXCP_LAST)
+    if (qemu_log_enabled() && cs->exception_index != EXCP_EXT_INTERRUPT) {
+        if (cs->exception_index < 0 || cs->exception_index > EXCP_LAST) {
             name = "unknown";
-        else
-            name = excp_names[env->exception_index];
+        } else {
+            name = excp_names[cs->exception_index];
+        }
 
         qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " %s exception\n",
                  __func__, env->active_tc.PC, env->CP0_EPC, name);
     }
-    if (env->exception_index == EXCP_EXT_INTERRUPT &&
-        (env->hflags & MIPS_HFLAG_DM))
-        env->exception_index = EXCP_DINT;
+    if (cs->exception_index == EXCP_EXT_INTERRUPT &&
+        (env->hflags & MIPS_HFLAG_DM)) {
+        cs->exception_index = EXCP_DINT;
+    }
     offset = 0x180;
-    switch (env->exception_index) {
+    switch (cs->exception_index) {
     case EXCP_DSS:
         env->CP0_Debug |= 1 << CP0DB_DSS;
         /* Debug single step cannot be raised inside a delay slot and
@@ -438,13 +483,12 @@ void do_interrupt (CPUState *env)
         env->hflags &= ~(MIPS_HFLAG_KSU);
         /* EJTAG probe trap enable is not implemented... */
         if (!(env->CP0_Status & (1 << CP0St_EXL)))
-            env->CP0_Cause &= ~(1 << CP0Ca_BD);
+            env->CP0_Cause &= ~(1U << CP0Ca_BD);
         env->active_tc.PC = (int32_t)0xBFC00480;
-        /* Exception handlers are entered in 32-bit mode.  */
-        env->hflags &= ~(MIPS_HFLAG_M16);
+        set_hflags_for_handler(env);
         break;
     case EXCP_RESET:
-        cpu_reset(env);
+        cpu_reset(CPU(cpu));
         break;
     case EXCP_SRESET:
         env->CP0_Status |= (1 << CP0St_SR);
@@ -459,15 +503,41 @@ void do_interrupt (CPUState *env)
         env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
         env->hflags &= ~(MIPS_HFLAG_KSU);
         if (!(env->CP0_Status & (1 << CP0St_EXL)))
-            env->CP0_Cause &= ~(1 << CP0Ca_BD);
+            env->CP0_Cause &= ~(1U << CP0Ca_BD);
         env->active_tc.PC = (int32_t)0xBFC00000;
-        /* Exception handlers are entered in 32-bit mode.  */
-        env->hflags &= ~(MIPS_HFLAG_M16);
+        set_hflags_for_handler(env);
         break;
     case EXCP_EXT_INTERRUPT:
         cause = 0;
         if (env->CP0_Cause & (1 << CP0Ca_IV))
             offset = 0x200;
+
+        if (env->CP0_Config3 & ((1 << CP0C3_VInt) | (1 << CP0C3_VEIC))) {
+            /* Vectored Interrupts.  */
+            unsigned int spacing;
+            unsigned int vector;
+            unsigned int pending = (env->CP0_Cause & CP0Ca_IP_mask) >> 8;
+
+            pending &= env->CP0_Status >> 8;
+            /* Compute the Vector Spacing.  */
+            spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & ((1 << 6) - 1);
+            spacing <<= 5;
+
+            if (env->CP0_Config3 & (1 << CP0C3_VInt)) {
+                /* For VInt mode, the MIPS computes the vector internally.  */
+                for (vector = 7; vector > 0; vector--) {
+                    if (pending & (1 << vector)) {
+                        /* Found it.  */
+                        break;
+                    }
+                }
+            } else {
+                /* For VEIC mode, the external interrupt controller feeds the
+                   vector through the CP0Cause IP lines.  */
+                vector = pending;
+            }
+            offset = 0x200 + vector * spacing;
+        }
         goto set_EPC;
     case EXCP_LTLBL:
         cause = 1;
@@ -481,7 +551,8 @@ void do_interrupt (CPUState *env)
             int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
             int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
 
-            if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX))
+            if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) &&
+                (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F))))
                 offset = 0x080;
             else
 #endif
@@ -497,7 +568,8 @@ void do_interrupt (CPUState *env)
             int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
             int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
 
-            if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX))
+            if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) &&
+                (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F))))
                 offset = 0x080;
             else
 #endif
@@ -555,6 +627,9 @@ void do_interrupt (CPUState *env)
     case EXCP_THREAD:
         cause = 25;
         goto set_EPC;
+    case EXCP_DSPDIS:
+        cause = 26;
+        goto set_EPC;
     case EXCP_CACHE:
         cause = 30;
         if (env->CP0_Status & (1 << CP0St_BEV)) {
@@ -566,9 +641,9 @@ void do_interrupt (CPUState *env)
         if (!(env->CP0_Status & (1 << CP0St_EXL))) {
             env->CP0_EPC = exception_resume_pc(env);
             if (env->hflags & MIPS_HFLAG_BMASK) {
-                env->CP0_Cause |= (1 << CP0Ca_BD);
+                env->CP0_Cause |= (1U << CP0Ca_BD);
             } else {
-                env->CP0_Cause &= ~(1 << CP0Ca_BD);
+                env->CP0_Cause &= ~(1U << CP0Ca_BD);
             }
             env->CP0_Status |= (1 << CP0St_EXL);
             env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
@@ -581,16 +656,15 @@ void do_interrupt (CPUState *env)
             env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff);
         }
         env->active_tc.PC += offset;
-        /* Exception handlers are entered in 32-bit mode.  */
-        env->hflags &= ~(MIPS_HFLAG_M16);
+        set_hflags_for_handler(env);
         env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC);
         break;
     default:
-        qemu_log("Invalid MIPS exception %d. Exiting\n", env->exception_index);
-        printf("Invalid MIPS exception %d. Exiting\n", env->exception_index);
+        qemu_log("Invalid MIPS exception %d. Exiting\n", cs->exception_index);
+        printf("Invalid MIPS exception %d. Exiting\n", cs->exception_index);
         exit(1);
     }
-    if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
+    if (qemu_log_enabled() && cs->exception_index != EXCP_EXT_INTERRUPT) {
         qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n"
                 "    S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n",
                 __func__, env->active_tc.PC, env->CP0_EPC, cause,
@@ -598,12 +672,31 @@ void do_interrupt (CPUState *env)
                 env->CP0_DEPC);
     }
 #endif
-    env->exception_index = EXCP_NONE;
+    cs->exception_index = EXCP_NONE;
+}
+
+bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+    if (interrupt_request & CPU_INTERRUPT_HARD) {
+        MIPSCPU *cpu = MIPS_CPU(cs);
+        CPUMIPSState *env = &cpu->env;
+
+        if (cpu_mips_hw_interrupts_pending(env)) {
+            /* Raise it */
+            cs->exception_index = EXCP_EXT_INTERRUPT;
+            env->error_code = 0;
+            mips_cpu_do_interrupt(cs);
+            return true;
+        }
+    }
+    return false;
 }
 
 #if !defined(CONFIG_USER_ONLY)
-void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra)
+void r4k_invalidate_tlb (CPUMIPSState *env, int idx, int use_extra)
 {
+    MIPSCPU *cpu = mips_env_get_cpu(env);
+    CPUState *cs;
     r4k_tlb_t *tlb;
     target_ulong addr;
     target_ulong end;
@@ -629,6 +722,7 @@ void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra)
     /* 1k pages are not supported. */
     mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
     if (tlb->V0) {
+        cs = CPU(cpu);
         addr = tlb->VPN & ~mask;
 #if defined(TARGET_MIPS64)
         if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
@@ -637,11 +731,12 @@ void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra)
 #endif
         end = addr | (mask >> 1);
         while (addr < end) {
-            tlb_flush_page (env, addr);
+            tlb_flush_page(cs, addr);
             addr += TARGET_PAGE_SIZE;
         }
     }
     if (tlb->V1) {
+        cs = CPU(cpu);
         addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1);
 #if defined(TARGET_MIPS64)
         if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
@@ -650,7 +745,7 @@ void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra)
 #endif
         end = addr | mask;
         while (addr - 1 < end) {
-            tlb_flush_page (env, addr);
+            tlb_flush_page(cs, addr);
             addr += TARGET_PAGE_SIZE;
         }
     }
This page took 0.042783 seconds and 4 git commands to generate.