summary refs log tree commit diff stats
path: root/target/hppa
diff options
context:
space:
mode:
Diffstat (limited to 'target/hppa')
-rw-r--r--target/hppa/cpu.c14
-rw-r--r--target/hppa/cpu.h14
-rw-r--r--target/hppa/gdbstub.c20
-rw-r--r--target/hppa/helper.c34
-rw-r--r--target/hppa/helper.h1
-rw-r--r--target/hppa/insns.decode6
-rw-r--r--target/hppa/mem_helper.c70
-rw-r--r--target/hppa/op_helper.c7
-rw-r--r--target/hppa/trace-events18
-rw-r--r--target/hppa/translate.c152
10 files changed, 257 insertions, 79 deletions
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
index 00bf444620..e64f48581e 100644
--- a/target/hppa/cpu.c
+++ b/target/hppa/cpu.c
@@ -20,6 +20,7 @@
 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
+#include "qemu/qemu-print.h"
 #include "cpu.h"
 #include "qemu-common.h"
 #include "exec/exec-all.h"
@@ -113,22 +114,17 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp)
 static void hppa_cpu_list_entry(gpointer data, gpointer user_data)
 {
     ObjectClass *oc = data;
-    CPUListState *s = user_data;
 
-    (*s->cpu_fprintf)(s->file, "  %s\n", object_class_get_name(oc));
+    qemu_printf("  %s\n", object_class_get_name(oc));
 }
 
-void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+void hppa_cpu_list(void)
 {
-    CPUListState s = {
-        .file = f,
-        .cpu_fprintf = cpu_fprintf,
-    };
     GSList *list;
 
     list = object_class_get_list_sorted(TYPE_HPPA_CPU, false);
-    (*cpu_fprintf)(f, "Available CPUs:\n");
-    g_slist_foreach(list, hppa_cpu_list_entry, &s);
+    qemu_printf("Available CPUs:\n");
+    g_slist_foreach(list, hppa_cpu_list_entry, NULL);
     g_slist_free(list);
 }
 
diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h
index 861bbb1f16..923346adb6 100644
--- a/target/hppa/cpu.h
+++ b/target/hppa/cpu.h
@@ -143,6 +143,10 @@
 #endif
 
 #define CR_RC            0
+#define CR_PID1          8
+#define CR_PID2          9
+#define CR_PID3          12
+#define CR_PID4          13
 #define CR_SCRCCR        10
 #define CR_SAR           11
 #define CR_IVA           14
@@ -268,7 +272,7 @@ void hppa_translate_init(void);
 
 #define CPU_RESOLVING_TYPE TYPE_HPPA_CPU
 
-void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+void hppa_cpu_list(void);
 
 static inline target_ulong hppa_form_gva_psw(target_ureg psw, uint64_t spc,
                                              target_ureg off)
@@ -341,6 +345,12 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env);
 void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg);
 void cpu_hppa_loaded_fr0(CPUHPPAState *env);
 
+#ifdef CONFIG_USER_ONLY
+static inline void cpu_hppa_change_prot_id(CPUHPPAState *env) { }
+#else
+void cpu_hppa_change_prot_id(CPUHPPAState *env);
+#endif
+
 #define cpu_signal_handler cpu_hppa_signal_handler
 
 int cpu_hppa_signal_handler(int host_signum, void *pinfo, void *puc);
@@ -349,7 +359,7 @@ int hppa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
 int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
 void hppa_cpu_do_interrupt(CPUState *cpu);
 bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req);
-void hppa_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function, int);
+void hppa_cpu_dump_state(CPUState *cs, FILE *f, int);
 #ifdef CONFIG_USER_ONLY
 int hppa_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
                               int rw, int midx);
diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c
index 3157a690f2..983bf92aaf 100644
--- a/target/hppa/gdbstub.c
+++ b/target/hppa/gdbstub.c
@@ -93,19 +93,19 @@ int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
         val = env->cr[CR_RC];
         break;
     case 52:
-        val = env->cr[8];
+        val = env->cr[CR_PID1];
         break;
     case 53:
-        val = env->cr[9];
+        val = env->cr[CR_PID2];
         break;
     case 54:
         val = env->cr[CR_SCRCCR];
         break;
     case 55:
-        val = env->cr[12];
+        val = env->cr[CR_PID3];
         break;
     case 56:
-        val = env->cr[13];
+        val = env->cr[CR_PID4];
         break;
     case 57:
         val = env->cr[24];
@@ -224,19 +224,23 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
         env->cr[CR_RC] = val;
         break;
     case 52:
-        env->cr[8] = val;
+        env->cr[CR_PID1] = val;
+        cpu_hppa_change_prot_id(env);
         break;
     case 53:
-        env->cr[9] = val;
+        env->cr[CR_PID2] = val;
+        cpu_hppa_change_prot_id(env);
         break;
     case 54:
         env->cr[CR_SCRCCR] = val;
         break;
     case 55:
-        env->cr[12] = val;
+        env->cr[CR_PID3] = val;
+        cpu_hppa_change_prot_id(env);
         break;
     case 56:
-        env->cr[13] = val;
+        env->cr[CR_PID4] = val;
+        cpu_hppa_change_prot_id(env);
         break;
     case 57:
         env->cr[24] = val;
diff --git a/target/hppa/helper.c b/target/hppa/helper.c
index 6539061e52..11c61b3ca2 100644
--- a/target/hppa/helper.c
+++ b/target/hppa/helper.c
@@ -21,7 +21,9 @@
 
 #include "cpu.h"
 #include "fpu/softfloat.h"
+#include "exec/exec-all.h"
 #include "exec/helper-proto.h"
+#include "qemu/qemu-print.h"
 
 target_ureg cpu_hppa_get_psw(CPUHPPAState *env)
 {
@@ -49,6 +51,7 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env)
 
 void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw)
 {
+    target_ureg old_psw = env->psw;
     target_ureg cb = 0;
 
     env->psw = psw & ~(PSW_N | PSW_V | PSW_CB);
@@ -64,10 +67,17 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw)
     cb |= ((psw >>  9) & 1) <<  8;
     cb |= ((psw >>  8) & 1) <<  4;
     env->psw_cb = cb;
+
+    /* If PSW_P changes, it affects how we translate addresses.  */
+    if ((psw ^ old_psw) & PSW_P) {
+#ifndef CONFIG_USER_ONLY
+        CPUState *src = CPU(hppa_env_get_cpu(env));
+        tlb_flush_by_mmuidx(src, 0xf);
+#endif
+    }
 }
 
-void hppa_cpu_dump_state(CPUState *cs, FILE *f,
-                         fprintf_function cpu_fprintf, int flags)
+void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags)
 {
     HPPACPU *cpu = HPPA_CPU(cs);
     CPUHPPAState *env = &cpu->env;
@@ -76,9 +86,9 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f,
     char psw_c[20];
     int i;
 
-    cpu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx "\n",
-                hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f),
-                hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b));
+    qemu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx "\n",
+                 hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f),
+                 hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b));
 
     psw_c[0]  = (psw & PSW_W ? 'W' : '-');
     psw_c[1]  = (psw & PSW_E ? 'E' : '-');
@@ -101,20 +111,20 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f,
     psw_c[18] = '\0';
     psw_cb = ((env->psw_cb >> 4) & 0x01111111) | (env->psw_cb_msb << 28);
 
-    cpu_fprintf(f, "PSW  " TREG_FMT_lx " CB   " TREG_FMT_lx " %s\n",
-                psw, psw_cb, psw_c);
+    qemu_fprintf(f, "PSW  " TREG_FMT_lx " CB   " TREG_FMT_lx " %s\n",
+                 psw, psw_cb, psw_c);
 
     for (i = 0; i < 32; i++) {
-        cpu_fprintf(f, "GR%02d " TREG_FMT_lx "%c", i, env->gr[i],
-                    (i & 3) == 3 ? '\n' : ' ');
+        qemu_fprintf(f, "GR%02d " TREG_FMT_lx "%c", i, env->gr[i],
+                     (i & 3) == 3 ? '\n' : ' ');
     }
 #ifndef CONFIG_USER_ONLY
     for (i = 0; i < 8; i++) {
-        cpu_fprintf(f, "SR%02d %08x%c", i, (uint32_t)(env->sr[i] >> 32),
-                    (i & 3) == 3 ? '\n' : ' ');
+        qemu_fprintf(f, "SR%02d %08x%c", i, (uint32_t)(env->sr[i] >> 32),
+                     (i & 3) == 3 ? '\n' : ' ');
     }
 #endif
-     cpu_fprintf(f, "\n");
+     qemu_fprintf(f, "\n");
 
     /* ??? FR */
 }
diff --git a/target/hppa/helper.h b/target/hppa/helper.h
index bfe0dd1db1..38d834ef6b 100644
--- a/target/hppa/helper.h
+++ b/target/hppa/helper.h
@@ -92,4 +92,5 @@ DEF_HELPER_FLAGS_3(itlbp, TCG_CALL_NO_RWG, void, env, tl, tr)
 DEF_HELPER_FLAGS_2(ptlb, TCG_CALL_NO_RWG, void, env, tl)
 DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env)
 DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tr, env, tl)
+DEF_HELPER_FLAGS_1(change_prot_id, TCG_CALL_NO_RWG, void, env)
 #endif
diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode
index 55ff39dd05..f0dd71dd08 100644
--- a/target/hppa/insns.decode
+++ b/target/hppa/insns.decode
@@ -133,6 +133,9 @@ ixtlbx          000001 b:5 r:5 sp:2 0100000 addr:1 0 00000      data=1
 ixtlbx          000001 b:5 r:5 ... 000000 addr:1 0 00000        \
                 sp=%assemble_sr3x data=0
 
+# pcxl and pcxl2 Fast TLB Insert instructions
+ixtlbxf         000001 00000 r:5 00 0 data:1 01000 addr:1 0 00000
+
 pxtlbx          000001 b:5 x:5 sp:2 0100100 local:1 m:1 -----   data=1
 pxtlbx          000001 b:5 x:5 ... 000100 local:1 m:1 -----     \
                 sp=%assemble_sr3x data=0
@@ -525,3 +528,6 @@ fmpy_d          001110 ..... ..... 010 ..... ... .....  @f0e_d_3
 fdiv_d          001110 ..... ..... 011 ..... ... .....  @f0e_d_3
 
 xmpyu           001110 ..... ..... 010 .0111 .00 t:5    r1=%ra64 r2=%rb64
+
+# diag
+diag            000101 ----- ----- ---- ---- ---- ----
diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c
index aecf3075f6..77fb544838 100644
--- a/target/hppa/mem_helper.c
+++ b/target/hppa/mem_helper.c
@@ -22,6 +22,7 @@
 #include "exec/exec-all.h"
 #include "exec/helper-proto.h"
 #include "qom/cpu.h"
+#include "trace.h"
 
 #ifdef CONFIG_USER_ONLY
 int hppa_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
@@ -43,9 +44,12 @@ static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr)
     for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
         hppa_tlb_entry *ent = &env->tlb[i];
         if (ent->va_b <= addr && addr <= ent->va_e) {
+            trace_hppa_tlb_find_entry(env, ent + i, ent->entry_valid,
+                                      ent->va_b, ent->va_e, ent->pa);
             return ent;
         }
     }
+    trace_hppa_tlb_find_entry_not_found(env, addr);
     return NULL;
 }
 
@@ -55,6 +59,8 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, hppa_tlb_entry *ent)
     unsigned i, n = 1 << (2 * ent->page_size);
     uint64_t addr = ent->va_b;
 
+    trace_hppa_tlb_flush_ent(env, ent, ent->va_b, ent->va_e, ent->pa);
+
     for (i = 0; i < n; ++i, addr += TARGET_PAGE_SIZE) {
         /* Do not flush MMU_PHYS_IDX.  */
         tlb_flush_page_by_mmuidx(cs, addr, 0xf);
@@ -96,9 +102,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
     if (ent == NULL || !ent->entry_valid) {
         phys = 0;
         prot = 0;
-        /* ??? Unconditionally report data tlb miss,
-           even if this is an instruction fetch.  */
-        ret = EXCP_DTLB_MISS;
+        ret = (type == PAGE_EXEC) ? EXCP_ITLB_MISS : EXCP_DTLB_MISS;
         goto egress;
     }
 
@@ -127,7 +131,20 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
         break;
     }
 
-    /* ??? Check PSW_P and ent->access_prot.  This can remove PAGE_WRITE.  */
+    /* access_id == 0 means public page and no check is performed */
+    if ((env->psw & PSW_P) && ent->access_id) {
+        /* If bits [31:1] match, and bit 0 is set, suppress write.  */
+        int match = ent->access_id * 2 + 1;
+
+        if (match == env->cr[CR_PID1] || match == env->cr[CR_PID2] ||
+            match == env->cr[CR_PID3] || match == env->cr[CR_PID4]) {
+            prot &= PAGE_READ | PAGE_EXEC;
+            if (type == PAGE_WRITE) {
+                ret = EXCP_DMPI;
+                goto egress;
+            }
+        }
+    }
 
     /* No guest access type indicates a non-architectural access from
        within QEMU.  Bypass checks for access, D, B and T bits.  */
@@ -137,8 +154,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
 
     if (unlikely(!(prot & type))) {
         /* The access isn't allowed -- Inst/Data Memory Protection Fault.  */
-        ret = (type & PAGE_EXEC ? EXCP_IMP :
-               prot & PAGE_READ ? EXCP_DMP : EXCP_DMAR);
+        ret = (type & PAGE_EXEC) ? EXCP_IMP : EXCP_DMAR;
         goto egress;
     }
 
@@ -171,6 +187,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
  egress:
     *pphys = phys;
     *pprot = prot;
+    trace_hppa_tlb_get_physical_address(env, ret, prot, addr, phys);
     return ret;
 }
 
@@ -200,6 +217,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size,
               MMUAccessType type, int mmu_idx, uintptr_t retaddr)
 {
     HPPACPU *cpu = HPPA_CPU(cs);
+    CPUHPPAState *env = &cpu->env;
     int prot, excp, a_prot;
     hwaddr phys;
 
@@ -215,9 +233,10 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size,
         break;
     }
 
-    excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx,
+    excp = hppa_get_physical_address(env, addr, mmu_idx,
                                      a_prot, &phys, &prot);
     if (unlikely(excp >= 0)) {
+        trace_hppa_tlb_fill_excp(env, addr, size, type, mmu_idx);
         /* Failure.  Raise the indicated exception.  */
         cs->exception_index = excp;
         if (cpu->env.psw & PSW_Q) {
@@ -228,6 +247,8 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size,
         cpu_loop_exit_restore(cs, retaddr);
     }
 
+    trace_hppa_tlb_fill_success(env, addr & TARGET_PAGE_MASK,
+                                phys & TARGET_PAGE_MASK, size, type, mmu_idx);
     /* Success!  Store the translation into the QEMU TLB.  */
     tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
                  prot, mmu_idx, TARGET_PAGE_SIZE);
@@ -242,11 +263,13 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
     /* Zap any old entries covering ADDR; notice empty entries on the way.  */
     for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
         hppa_tlb_entry *ent = &env->tlb[i];
-        if (!ent->entry_valid) {
-            empty = ent;
-        } else if (ent->va_b <= addr && addr <= ent->va_e) {
-            hppa_flush_tlb_ent(env, ent);
-            empty = ent;
+        if (ent->va_b <= addr && addr <= ent->va_e) {
+            if (ent->entry_valid) {
+                hppa_flush_tlb_ent(env, ent);
+            }
+            if (!empty) {
+                empty = ent;
+            }
         }
     }
 
@@ -259,6 +282,7 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
     empty->va_b = addr & TARGET_PAGE_MASK;
     empty->va_e = empty->va_b + TARGET_PAGE_SIZE - 1;
     empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS;
+    trace_hppa_tlb_itlba(env, empty, empty->va_b, empty->va_e, empty->pa);
 }
 
 /* Insert (Insn/Data) TLB Protection.  Note this is PA 1.1 only.  */
@@ -266,7 +290,7 @@ void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
 {
     hppa_tlb_entry *ent = hppa_find_tlb(env, addr);
 
-    if (unlikely(ent == NULL || ent->entry_valid)) {
+    if (unlikely(ent == NULL)) {
         qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n");
         return;
     }
@@ -280,6 +304,8 @@ void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
     ent->d = extract32(reg, 28, 1);
     ent->t = extract32(reg, 29, 1);
     ent->entry_valid = 1;
+    trace_hppa_tlb_itlbp(env, ent, ent->access_id, ent->u, ent->ar_pl2,
+                         ent->ar_pl1, ent->ar_type, ent->b, ent->d, ent->t);
 }
 
 /* Purge (Insn/Data) TLB.  This is explicitly page-based, and is
@@ -299,6 +325,7 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr)
 {
     CPUState *src = CPU(hppa_env_get_cpu(env));
     CPUState *cpu;
+    trace_hppa_tlb_ptlb(env);
     run_on_cpu_data data = RUN_ON_CPU_TARGET_PTR(addr);
 
     CPU_FOREACH(cpu) {
@@ -314,11 +341,24 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr)
 void HELPER(ptlbe)(CPUHPPAState *env)
 {
     CPUState *src = CPU(hppa_env_get_cpu(env));
-
+    trace_hppa_tlb_ptlbe(env);
     memset(env->tlb, 0, sizeof(env->tlb));
     tlb_flush_by_mmuidx(src, 0xf);
 }
 
+void cpu_hppa_change_prot_id(CPUHPPAState *env)
+{
+    if (env->psw & PSW_P) {
+        CPUState *src = CPU(hppa_env_get_cpu(env));
+        tlb_flush_by_mmuidx(src, 0xf);
+    }
+}
+
+void HELPER(change_prot_id)(CPUHPPAState *env)
+{
+    cpu_hppa_change_prot_id(env);
+}
+
 target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr)
 {
     hwaddr phys;
@@ -335,8 +375,10 @@ target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr)
         if (excp == EXCP_DTLB_MISS) {
             excp = EXCP_NA_DTLB_MISS;
         }
+        trace_hppa_tlb_lpa_failed(env, addr);
         hppa_dynamic_excp(env, excp, GETPC());
     }
+    trace_hppa_tlb_lpa_success(env, addr, phys);
     return phys;
 }
 
diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c
index 268caaaa20..a55a5dfc02 100644
--- a/target/hppa/op_helper.c
+++ b/target/hppa/op_helper.c
@@ -25,6 +25,7 @@
 #include "sysemu/sysemu.h"
 #include "qemu/timer.h"
 #include "fpu/softfloat.h"
+#include "trace.h"
 
 void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp)
 {
@@ -165,6 +166,7 @@ target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr,
     int prot, excp;
     hwaddr phys;
 
+    trace_hppa_tlb_probe(addr, level, want);
     /* Fail if the requested privilege level is higher than current.  */
     if (level < (env->iaoq_f & 3)) {
         return 0;
@@ -676,11 +678,6 @@ target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm)
 
 void HELPER(rfi)(CPUHPPAState *env)
 {
-    /* ??? On second reading this condition simply seems
-       to be undefined rather than a diagnosed trap.  */
-    if (env->psw & (PSW_I | PSW_R | PSW_Q)) {
-        helper_excp(env, EXCP_ILL);
-    }
     env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32;
     env->iasq_b = (uint64_t)env->cr_back[0] << 32;
     env->iaoq_f = env->cr[CR_IIAOQ];
diff --git a/target/hppa/trace-events b/target/hppa/trace-events
new file mode 100644
index 0000000000..0731ce7ce1
--- /dev/null
+++ b/target/hppa/trace-events
@@ -0,0 +1,18 @@
+# See docs/devel/tracing.txt for syntax documentation.
+
+# mem_helper.c
+disable hppa_tlb_flush_ent(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx"
+disable hppa_tlb_find_entry(void *env, void *ent, int valid, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p valid=%d va_b=0x%lx va_e=0x%lx pa=0x%lx"
+disable hppa_tlb_find_entry_not_found(void *env, uint64_t addr) "env=%p addr=%08lx"
+disable hppa_tlb_get_physical_address(void *env, int ret, int prot, uint64_t addr, uint64_t phys) "env=%p ret=%d prot=%d addr=0x%lx phys=0x%lx"
+disable hppa_tlb_fill_excp(void *env, uint64_t addr, int size, int type, int mmu_idx) "env=%p addr=0x%lx size=%d type=%d mmu_idx=%d"
+disable hppa_tlb_fill_success(void *env, uint64_t addr, uint64_t phys, int size, int type, int mmu_idx) "env=%p addr=0x%lx phys=0x%lx size=%d type=%d mmu_idx=%d"
+disable hppa_tlb_itlba(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx"
+disable hppa_tlb_itlbp(void *env, void *ent, int access_id, int u, int pl2, int pl1, int type, int b, int d, int t) "env=%p ent=%p access_id=%x u=%d pl2=%d pl1=%d type=%d b=%d d=%d t=%d"
+disable hppa_tlb_ptlb(void *env) "env=%p"
+disable hppa_tlb_ptlbe(void *env) "env=%p"
+disable hppa_tlb_lpa_success(void *env, uint64_t addr, uint64_t phys) "env=%p addr=0x%lx phys=0x%lx"
+disable hppa_tlb_lpa_failed(void *env, uint64_t addr) "env=%p addr=0x%lx"
+
+# op_helper.c
+disable hppa_tlb_probe(uint64_t addr, int level, int want) "addr=0x%lx level=%d want=%d"
diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index dc5636fe94..e1febdfea1 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -347,6 +347,7 @@ static int expand_shl11(int val)
 /* Similarly, but we want to return to the main loop immediately
    to recognize unmasked interrupts.  */
 #define DISAS_IAQ_N_STALE_EXIT      DISAS_TARGET_2
+#define DISAS_EXIT                  DISAS_TARGET_3
 
 /* global register indexes */
 static TCGv_reg cpu_gr[32];
@@ -816,12 +817,10 @@ static bool gen_illegal(DisasContext *ctx)
 
 static bool use_goto_tb(DisasContext *ctx, target_ureg dest)
 {
-    /* Suppress goto_tb in the case of single-steping and IO.  */
-    if ((tb_cflags(ctx->base.tb) & CF_LAST_IO)
-        || ctx->base.singlestep_enabled) {
-        return false;
-    }
-    return true;
+    /* Suppress goto_tb for page crossing, IO, or single-steping.  */
+    return !(((ctx->base.pc_first ^ dest) & TARGET_PAGE_MASK)
+             || (tb_cflags(ctx->base.tb) & CF_LAST_IO)
+             || ctx->base.singlestep_enabled);
 }
 
 /* If the next insn is to be nullified, and it's on the same page,
@@ -2258,6 +2257,16 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a)
                        offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ]));
         break;
 
+    case CR_PID1:
+    case CR_PID2:
+    case CR_PID3:
+    case CR_PID4:
+        tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl]));
+#ifndef CONFIG_USER_ONLY
+        gen_helper_change_prot_id(cpu_env);
+#endif
+        break;
+
     default:
         tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl]));
         break;
@@ -2474,9 +2483,8 @@ static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a)
         gen_helper_itlbp(cpu_env, addr, reg);
     }
 
-    /* Exit TB for ITLB change if mmu is enabled.  This *should* not be
-       the case, since the OS TLB fill handler runs with mmu disabled.  */
-    if (!a->data && (ctx->tb_flags & PSW_C)) {
+    /* Exit TB for TLB change if mmu is enabled.  */
+    if (ctx->tb_flags & PSW_C) {
         ctx->base.is_jmp = DISAS_IAQ_N_STALE;
     }
     return nullify_end(ctx);
@@ -2503,7 +2511,61 @@ static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a)
     }
 
     /* Exit TB for TLB change if mmu is enabled.  */
-    if (!a->data && (ctx->tb_flags & PSW_C)) {
+    if (ctx->tb_flags & PSW_C) {
+        ctx->base.is_jmp = DISAS_IAQ_N_STALE;
+    }
+    return nullify_end(ctx);
+#endif
+}
+
+/*
+ * Implement the pcxl and pcxl2 Fast TLB Insert instructions.
+ * See
+ *     https://parisc.wiki.kernel.org/images-parisc/a/a9/Pcxl2_ers.pdf
+ *     page 13-9 (195/206)
+ */
+static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a)
+{
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
+    TCGv_tl addr, atl, stl;
+    TCGv_reg reg;
+
+    nullify_over(ctx);
+
+    /*
+     * FIXME:
+     *  if (not (pcxl or pcxl2))
+     *    return gen_illegal(ctx);
+     *
+     * Note for future: these are 32-bit systems; no hppa64.
+     */
+
+    atl = tcg_temp_new_tl();
+    stl = tcg_temp_new_tl();
+    addr = tcg_temp_new_tl();
+
+    tcg_gen_ld32u_i64(stl, cpu_env,
+                      a->data ? offsetof(CPUHPPAState, cr[CR_ISR])
+                      : offsetof(CPUHPPAState, cr[CR_IIASQ]));
+    tcg_gen_ld32u_i64(atl, cpu_env,
+                      a->data ? offsetof(CPUHPPAState, cr[CR_IOR])
+                      : offsetof(CPUHPPAState, cr[CR_IIAOQ]));
+    tcg_gen_shli_i64(stl, stl, 32);
+    tcg_gen_or_tl(addr, atl, stl);
+    tcg_temp_free_tl(atl);
+    tcg_temp_free_tl(stl);
+
+    reg = load_gpr(ctx, a->r);
+    if (a->addr) {
+        gen_helper_itlba(cpu_env, addr, reg);
+    } else {
+        gen_helper_itlbp(cpu_env, addr, reg);
+    }
+    tcg_temp_free_tl(addr);
+
+    /* Exit TB for TLB change if mmu is enabled.  */
+    if (ctx->tb_flags & PSW_C) {
         ctx->base.is_jmp = DISAS_IAQ_N_STALE;
     }
     return nullify_end(ctx);
@@ -3033,7 +3095,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1,
     DisasCond cond;
 
     in2 = load_gpr(ctx, r);
-    dest = dest_gpr(ctx, r);
+    dest = tcg_temp_new();
     sv = NULL;
     cb_msb = NULL;
 
@@ -3049,6 +3111,8 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1,
     }
 
     cond = do_cond(c * 2 + f, dest, cb_msb, sv);
+    save_gpr(ctx, r, dest);
+    tcg_temp_free(dest);
     return do_cbranch(ctx, disp, n, &cond);
 }
 
@@ -3446,6 +3510,8 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
 {
     target_ureg dest = iaoq_dest(ctx, a->disp);
 
+    nullify_over(ctx);
+
     /* Make sure the caller hasn't done something weird with the queue.
      * ??? This is not quite the same as the PSW[B] bit, which would be
      * expensive to track.  Real hardware will trap for
@@ -3483,7 +3549,16 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
     }
 #endif
 
-    return do_dbranch(ctx, dest, a->l, a->n);
+    if (a->l) {
+        TCGv_reg tmp = dest_gpr(ctx, a->l);
+        if (ctx->privilege < 3) {
+            tcg_gen_andi_reg(tmp, tmp, -4);
+        }
+        tcg_gen_ori_reg(tmp, tmp, ctx->privilege);
+        save_gpr(ctx, a->l, tmp);
+    }
+
+    return do_dbranch(ctx, dest, 0, a->n);
 }
 
 static bool trans_blr(DisasContext *ctx, arg_blr *a)
@@ -4048,6 +4123,13 @@ static bool trans_fmpyfadd_d(DisasContext *ctx, arg_fmpyfadd_d *a)
     return nullify_end(ctx);
 }
 
+static bool trans_diag(DisasContext *ctx, arg_diag *a)
+{
+    qemu_log_mask(LOG_UNIMP, "DIAG opcode ignored\n");
+    cond_free(&ctx->null_cond);
+    return true;
+}
+
 static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
@@ -4191,19 +4273,31 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     ctx->iaoq_b = ctx->iaoq_n;
     ctx->base.pc_next += 4;
 
-    if (ret == DISAS_NORETURN || ret == DISAS_IAQ_N_UPDATED) {
-        return;
-    }
-    if (ctx->iaoq_f == -1) {
-        tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b);
-        copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
+    switch (ret) {
+    case DISAS_NORETURN:
+    case DISAS_IAQ_N_UPDATED:
+        break;
+
+    case DISAS_NEXT:
+    case DISAS_IAQ_N_STALE:
+    case DISAS_IAQ_N_STALE_EXIT:
+        if (ctx->iaoq_f == -1) {
+            tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b);
+            copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
 #ifndef CONFIG_USER_ONLY
-        tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b);
+            tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b);
 #endif
-        nullify_save(ctx);
-        ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
-    } else if (ctx->iaoq_b == -1) {
-        tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var);
+            nullify_save(ctx);
+            ctx->base.is_jmp = (ret == DISAS_IAQ_N_STALE_EXIT
+                                ? DISAS_EXIT
+                                : DISAS_IAQ_N_UPDATED);
+        } else if (ctx->iaoq_b == -1) {
+            tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var);
+        }
+        break;
+
+    default:
+        g_assert_not_reached();
     }
 }
 
@@ -4225,11 +4319,12 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
     case DISAS_IAQ_N_UPDATED:
         if (ctx->base.singlestep_enabled) {
             gen_excp_1(EXCP_DEBUG);
-        } else if (is_jmp == DISAS_IAQ_N_STALE_EXIT) {
-            tcg_gen_exit_tb(NULL, 0);
-        } else {
+        } else if (is_jmp != DISAS_IAQ_N_STALE_EXIT) {
             tcg_gen_lookup_and_goto_ptr();
         }
+        /* FALLTHRU */
+    case DISAS_EXIT:
+        tcg_gen_exit_tb(NULL, 0);
         break;
     default:
         g_assert_not_reached();
@@ -4271,11 +4366,10 @@ static const TranslatorOps hppa_tr_ops = {
     .disas_log          = hppa_tr_disas_log,
 };
 
-void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
-
+void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
 {
     DisasContext ctx;
-    translator_loop(&hppa_tr_ops, &ctx.base, cs, tb);
+    translator_loop(&hppa_tr_ops, &ctx.base, cs, tb, max_insns);
 }
 
 void restore_state_to_opc(CPUHPPAState *env, TranslationBlock *tb,