xref: /openbmc/qemu/target/hppa/int_helper.c (revision c53e401e)
11a19da0dSRichard Henderson /*
21a19da0dSRichard Henderson  *  HPPA interrupt helper routines
31a19da0dSRichard Henderson  *
41a19da0dSRichard Henderson  *  Copyright (c) 2017 Richard Henderson
51a19da0dSRichard Henderson  *
61a19da0dSRichard Henderson  * This library is free software; you can redistribute it and/or
71a19da0dSRichard Henderson  * modify it under the terms of the GNU Lesser General Public
81a19da0dSRichard Henderson  * License as published by the Free Software Foundation; either
9d6ea4236SChetan Pant  * version 2.1 of the License, or (at your option) any later version.
101a19da0dSRichard Henderson  *
111a19da0dSRichard Henderson  * This library is distributed in the hope that it will be useful,
121a19da0dSRichard Henderson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
131a19da0dSRichard Henderson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
141a19da0dSRichard Henderson  * Lesser General Public License for more details.
151a19da0dSRichard Henderson  *
161a19da0dSRichard Henderson  * You should have received a copy of the GNU Lesser General Public
171a19da0dSRichard Henderson  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
181a19da0dSRichard Henderson  */
191a19da0dSRichard Henderson 
201a19da0dSRichard Henderson #include "qemu/osdep.h"
211a19da0dSRichard Henderson #include "qemu/main-loop.h"
220d114d0dSPhilippe Mathieu-Daudé #include "qemu/log.h"
231a19da0dSRichard Henderson #include "cpu.h"
241a19da0dSRichard Henderson #include "exec/helper-proto.h"
252e5b09fdSMarkus Armbruster #include "hw/core/cpu.h"
264a4554c6SHelge Deller #include "hw/hppa/hppa_hardware.h"
271a19da0dSRichard Henderson 
284f5f2548SRichard Henderson static void eval_interrupt(HPPACPU *cpu)
294f5f2548SRichard Henderson {
304f5f2548SRichard Henderson     CPUState *cs = CPU(cpu);
314f5f2548SRichard Henderson     if (cpu->env.cr[CR_EIRR] & cpu->env.cr[CR_EIEM]) {
324f5f2548SRichard Henderson         cpu_interrupt(cs, CPU_INTERRUPT_HARD);
334f5f2548SRichard Henderson     } else {
344f5f2548SRichard Henderson         cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
354f5f2548SRichard Henderson     }
364f5f2548SRichard Henderson }
374f5f2548SRichard Henderson 
384f5f2548SRichard Henderson /* Each CPU has a word mapped into the GSC bus.  Anything on the GSC bus
394f5f2548SRichard Henderson  * can write to this word to raise an external interrupt on the target CPU.
408b81968cSMichael Tokarev  * This includes the system controller (DINO) for regular devices, or
414f5f2548SRichard Henderson  * another CPU for SMP interprocessor interrupts.
424f5f2548SRichard Henderson  */
434f5f2548SRichard Henderson static uint64_t io_eir_read(void *opaque, hwaddr addr, unsigned size)
444f5f2548SRichard Henderson {
454f5f2548SRichard Henderson     HPPACPU *cpu = opaque;
464f5f2548SRichard Henderson 
474f5f2548SRichard Henderson     /* ??? What does a read of this register over the GSC bus do?  */
484f5f2548SRichard Henderson     return cpu->env.cr[CR_EIRR];
494f5f2548SRichard Henderson }
504f5f2548SRichard Henderson 
514f5f2548SRichard Henderson static void io_eir_write(void *opaque, hwaddr addr,
524f5f2548SRichard Henderson                          uint64_t data, unsigned size)
534f5f2548SRichard Henderson {
544f5f2548SRichard Henderson     HPPACPU *cpu = opaque;
55*c53e401eSRichard Henderson     int le_bit = ~data & 31;
564f5f2548SRichard Henderson 
57*c53e401eSRichard Henderson     cpu->env.cr[CR_EIRR] |= (target_ulong)1 << le_bit;
584f5f2548SRichard Henderson     eval_interrupt(cpu);
594f5f2548SRichard Henderson }
604f5f2548SRichard Henderson 
614f5f2548SRichard Henderson const MemoryRegionOps hppa_io_eir_ops = {
624f5f2548SRichard Henderson     .read = io_eir_read,
634f5f2548SRichard Henderson     .write = io_eir_write,
644f5f2548SRichard Henderson     .valid.min_access_size = 4,
654f5f2548SRichard Henderson     .valid.max_access_size = 4,
664f5f2548SRichard Henderson     .impl.min_access_size = 4,
674f5f2548SRichard Henderson     .impl.max_access_size = 4,
684f5f2548SRichard Henderson };
694f5f2548SRichard Henderson 
7049c29d6cSRichard Henderson void hppa_cpu_alarm_timer(void *opaque)
7149c29d6cSRichard Henderson {
7249c29d6cSRichard Henderson     /* Raise interrupt 0.  */
7349c29d6cSRichard Henderson     io_eir_write(opaque, 0, 0, 4);
7449c29d6cSRichard Henderson }
7549c29d6cSRichard Henderson 
76*c53e401eSRichard Henderson void HELPER(write_eirr)(CPUHPPAState *env, target_ulong val)
774f5f2548SRichard Henderson {
784f5f2548SRichard Henderson     env->cr[CR_EIRR] &= ~val;
794f5f2548SRichard Henderson     qemu_mutex_lock_iothread();
8025f32708SRichard Henderson     eval_interrupt(env_archcpu(env));
814f5f2548SRichard Henderson     qemu_mutex_unlock_iothread();
824f5f2548SRichard Henderson }
834f5f2548SRichard Henderson 
84*c53e401eSRichard Henderson void HELPER(write_eiem)(CPUHPPAState *env, target_ulong val)
854f5f2548SRichard Henderson {
864f5f2548SRichard Henderson     env->cr[CR_EIEM] = val;
874f5f2548SRichard Henderson     qemu_mutex_lock_iothread();
8825f32708SRichard Henderson     eval_interrupt(env_archcpu(env));
894f5f2548SRichard Henderson     qemu_mutex_unlock_iothread();
904f5f2548SRichard Henderson }
911a19da0dSRichard Henderson 
921a19da0dSRichard Henderson void hppa_cpu_do_interrupt(CPUState *cs)
931a19da0dSRichard Henderson {
941a19da0dSRichard Henderson     HPPACPU *cpu = HPPA_CPU(cs);
951a19da0dSRichard Henderson     CPUHPPAState *env = &cpu->env;
961a19da0dSRichard Henderson     int i = cs->exception_index;
97*c53e401eSRichard Henderson     target_ulong iaoq_f = env->iaoq_f;
98*c53e401eSRichard Henderson     target_ulong iaoq_b = env->iaoq_b;
99c301f34eSRichard Henderson     uint64_t iasq_f = env->iasq_f;
100c301f34eSRichard Henderson     uint64_t iasq_b = env->iasq_b;
101*c53e401eSRichard Henderson     target_ulong old_psw;
1021a19da0dSRichard Henderson 
1031a19da0dSRichard Henderson     /* As documented in pa2.0 -- interruption handling.  */
1041a19da0dSRichard Henderson     /* step 1 */
1051a19da0dSRichard Henderson     env->cr[CR_IPSW] = old_psw = cpu_hppa_get_psw(env);
1061a19da0dSRichard Henderson 
1071a19da0dSRichard Henderson     /* step 2 -- note PSW_W == 0 for !HPPA64.  */
1081a19da0dSRichard Henderson     cpu_hppa_put_psw(env, PSW_W | (i == EXCP_HPMC ? PSW_M : 0));
1091a19da0dSRichard Henderson 
1101a19da0dSRichard Henderson     /* step 3 */
111c301f34eSRichard Henderson     env->cr[CR_IIASQ] = iasq_f >> 32;
112c301f34eSRichard Henderson     env->cr_back[0] = iasq_b >> 32;
1131a19da0dSRichard Henderson     env->cr[CR_IIAOQ] = iaoq_f;
1141a19da0dSRichard Henderson     env->cr_back[1] = iaoq_b;
1151a19da0dSRichard Henderson 
1161a19da0dSRichard Henderson     if (old_psw & PSW_Q) {
1171a19da0dSRichard Henderson         /* step 5 */
1181a19da0dSRichard Henderson         /* ISR and IOR will be set elsewhere.  */
1191a19da0dSRichard Henderson         switch (i) {
1201a19da0dSRichard Henderson         case EXCP_ILL:
1211a19da0dSRichard Henderson         case EXCP_BREAK:
1221a19da0dSRichard Henderson         case EXCP_PRIV_REG:
1231a19da0dSRichard Henderson         case EXCP_PRIV_OPR:
1241a19da0dSRichard Henderson             /* IIR set via translate.c.  */
1251a19da0dSRichard Henderson             break;
1261a19da0dSRichard Henderson 
1271a19da0dSRichard Henderson         case EXCP_OVERFLOW:
1281a19da0dSRichard Henderson         case EXCP_COND:
1291a19da0dSRichard Henderson         case EXCP_ASSIST:
1301a19da0dSRichard Henderson         case EXCP_DTLB_MISS:
1311a19da0dSRichard Henderson         case EXCP_NA_ITLB_MISS:
1321a19da0dSRichard Henderson         case EXCP_NA_DTLB_MISS:
1331a19da0dSRichard Henderson         case EXCP_DMAR:
1341a19da0dSRichard Henderson         case EXCP_DMPI:
1351a19da0dSRichard Henderson         case EXCP_UNALIGN:
1361a19da0dSRichard Henderson         case EXCP_DMP:
1371a19da0dSRichard Henderson         case EXCP_DMB:
1381a19da0dSRichard Henderson         case EXCP_TLB_DIRTY:
1391a19da0dSRichard Henderson         case EXCP_PAGE_REF:
1401a19da0dSRichard Henderson         case EXCP_ASSIST_EMU:
1411a19da0dSRichard Henderson             {
1421a19da0dSRichard Henderson                 /* Avoid reading directly from the virtual address, lest we
1431a19da0dSRichard Henderson                    raise another exception from some sort of TLB issue.  */
144650cdb2aSRichard Henderson                 /* ??? An alternate fool-proof method would be to store the
145650cdb2aSRichard Henderson                    instruction data into the unwind info.  That's probably
146650cdb2aSRichard Henderson                    a bit too much in the way of extra storage required.  */
1471a19da0dSRichard Henderson                 vaddr vaddr;
1481a19da0dSRichard Henderson                 hwaddr paddr;
1491a19da0dSRichard Henderson 
1501a19da0dSRichard Henderson                 paddr = vaddr = iaoq_f & -4;
151c301f34eSRichard Henderson                 if (old_psw & PSW_C) {
152650cdb2aSRichard Henderson                     int prot, t;
153650cdb2aSRichard Henderson 
154650cdb2aSRichard Henderson                     vaddr = hppa_form_gva_psw(old_psw, iasq_f, vaddr);
155650cdb2aSRichard Henderson                     t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX,
156fa824d99SHelge Deller                                                   0, &paddr, &prot, NULL);
157650cdb2aSRichard Henderson                     if (t >= 0) {
158650cdb2aSRichard Henderson                         /* We can't re-load the instruction.  */
159650cdb2aSRichard Henderson                         env->cr[CR_IIR] = 0;
160650cdb2aSRichard Henderson                         break;
161650cdb2aSRichard Henderson                     }
162c301f34eSRichard Henderson                 }
1631a19da0dSRichard Henderson                 env->cr[CR_IIR] = ldl_phys(cs->as, paddr);
1641a19da0dSRichard Henderson             }
1651a19da0dSRichard Henderson             break;
1661a19da0dSRichard Henderson 
1671a19da0dSRichard Henderson         default:
1681a19da0dSRichard Henderson             /* Other exceptions do not set IIR.  */
1691a19da0dSRichard Henderson             break;
1701a19da0dSRichard Henderson         }
1711a19da0dSRichard Henderson 
1721a19da0dSRichard Henderson         /* step 6 */
1731a19da0dSRichard Henderson         env->shadow[0] = env->gr[1];
1741a19da0dSRichard Henderson         env->shadow[1] = env->gr[8];
1751a19da0dSRichard Henderson         env->shadow[2] = env->gr[9];
1761a19da0dSRichard Henderson         env->shadow[3] = env->gr[16];
1771a19da0dSRichard Henderson         env->shadow[4] = env->gr[17];
1781a19da0dSRichard Henderson         env->shadow[5] = env->gr[24];
1791a19da0dSRichard Henderson         env->shadow[6] = env->gr[25];
1801a19da0dSRichard Henderson     }
1811a19da0dSRichard Henderson 
1821a19da0dSRichard Henderson     /* step 7 */
1834a4554c6SHelge Deller     if (i == EXCP_TOC) {
1844a4554c6SHelge Deller         env->iaoq_f = FIRMWARE_START;
1854a4554c6SHelge Deller         /* help SeaBIOS and provide iaoq_b and iasq_back in shadow regs */
1864a4554c6SHelge Deller         env->gr[24] = env->cr_back[0];
1874a4554c6SHelge Deller         env->gr[25] = env->cr_back[1];
1884a4554c6SHelge Deller     } else {
1891a19da0dSRichard Henderson         env->iaoq_f = env->cr[CR_IVA] + 32 * i;
1904a4554c6SHelge Deller     }
1911a19da0dSRichard Henderson     env->iaoq_b = env->iaoq_f + 4;
192c301f34eSRichard Henderson     env->iasq_f = 0;
193c301f34eSRichard Henderson     env->iasq_b = 0;
1941a19da0dSRichard Henderson 
1951a19da0dSRichard Henderson     if (qemu_loglevel_mask(CPU_LOG_INT)) {
1961a19da0dSRichard Henderson         static const char * const names[] = {
1971a19da0dSRichard Henderson             [EXCP_HPMC]          = "high priority machine check",
1981a19da0dSRichard Henderson             [EXCP_POWER_FAIL]    = "power fail interrupt",
1991a19da0dSRichard Henderson             [EXCP_RC]            = "recovery counter trap",
2001a19da0dSRichard Henderson             [EXCP_EXT_INTERRUPT] = "external interrupt",
2011a19da0dSRichard Henderson             [EXCP_LPMC]          = "low priority machine check",
2021a19da0dSRichard Henderson             [EXCP_ITLB_MISS]     = "instruction tlb miss fault",
2031a19da0dSRichard Henderson             [EXCP_IMP]           = "instruction memory protection trap",
2041a19da0dSRichard Henderson             [EXCP_ILL]           = "illegal instruction trap",
2051a19da0dSRichard Henderson             [EXCP_BREAK]         = "break instruction trap",
2061a19da0dSRichard Henderson             [EXCP_PRIV_OPR]      = "privileged operation trap",
2071a19da0dSRichard Henderson             [EXCP_PRIV_REG]      = "privileged register trap",
2081a19da0dSRichard Henderson             [EXCP_OVERFLOW]      = "overflow trap",
2091a19da0dSRichard Henderson             [EXCP_COND]          = "conditional trap",
2101a19da0dSRichard Henderson             [EXCP_ASSIST]        = "assist exception trap",
2111a19da0dSRichard Henderson             [EXCP_DTLB_MISS]     = "data tlb miss fault",
2121a19da0dSRichard Henderson             [EXCP_NA_ITLB_MISS]  = "non-access instruction tlb miss",
2131a19da0dSRichard Henderson             [EXCP_NA_DTLB_MISS]  = "non-access data tlb miss",
2141a19da0dSRichard Henderson             [EXCP_DMP]           = "data memory protection trap",
2151a19da0dSRichard Henderson             [EXCP_DMB]           = "data memory break trap",
2161a19da0dSRichard Henderson             [EXCP_TLB_DIRTY]     = "tlb dirty bit trap",
2171a19da0dSRichard Henderson             [EXCP_PAGE_REF]      = "page reference trap",
2181a19da0dSRichard Henderson             [EXCP_ASSIST_EMU]    = "assist emulation trap",
2191a19da0dSRichard Henderson             [EXCP_HPT]           = "high-privilege transfer trap",
2201a19da0dSRichard Henderson             [EXCP_LPT]           = "low-privilege transfer trap",
2211a19da0dSRichard Henderson             [EXCP_TB]            = "taken branch trap",
2221a19da0dSRichard Henderson             [EXCP_DMAR]          = "data memory access rights trap",
2231a19da0dSRichard Henderson             [EXCP_DMPI]          = "data memory protection id trap",
2241a19da0dSRichard Henderson             [EXCP_UNALIGN]       = "unaligned data reference trap",
2251a19da0dSRichard Henderson             [EXCP_PER_INTERRUPT] = "performance monitor interrupt",
2261a19da0dSRichard Henderson             [EXCP_SYSCALL]       = "syscall",
2271a19da0dSRichard Henderson             [EXCP_SYSCALL_LWS]   = "syscall-lws",
2284a4554c6SHelge Deller             [EXCP_TOC]           = "TOC (transfer of control)",
2291a19da0dSRichard Henderson         };
2301a19da0dSRichard Henderson         static int count;
2311a19da0dSRichard Henderson         const char *name = NULL;
2321a19da0dSRichard Henderson         char unknown[16];
2331a19da0dSRichard Henderson 
2341a19da0dSRichard Henderson         if (i >= 0 && i < ARRAY_SIZE(names)) {
2351a19da0dSRichard Henderson             name = names[i];
2361a19da0dSRichard Henderson         }
2371a19da0dSRichard Henderson         if (!name) {
2381a19da0dSRichard Henderson             snprintf(unknown, sizeof(unknown), "unknown %d", i);
2391a19da0dSRichard Henderson             name = unknown;
2401a19da0dSRichard Henderson         }
2411a19da0dSRichard Henderson         qemu_log("INT %6d: %s @ " TARGET_FMT_lx "," TARGET_FMT_lx
242*c53e401eSRichard Henderson                  " -> " TARGET_FMT_lx " " TARGET_FMT_lx "\n",
2431a19da0dSRichard Henderson                  ++count, name,
244c301f34eSRichard Henderson                  hppa_form_gva(env, iasq_f, iaoq_f),
245c301f34eSRichard Henderson                  hppa_form_gva(env, iasq_b, iaoq_b),
2461a19da0dSRichard Henderson                  env->iaoq_f,
247c301f34eSRichard Henderson                  hppa_form_gva(env, (uint64_t)env->cr[CR_ISR] << 32,
248c301f34eSRichard Henderson                                env->cr[CR_IOR]));
2491a19da0dSRichard Henderson     }
2501a19da0dSRichard Henderson     cs->exception_index = -1;
2511a19da0dSRichard Henderson }
2521a19da0dSRichard Henderson 
2531a19da0dSRichard Henderson bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
2541a19da0dSRichard Henderson {
2551a19da0dSRichard Henderson     HPPACPU *cpu = HPPA_CPU(cs);
2561a19da0dSRichard Henderson     CPUHPPAState *env = &cpu->env;
2571a19da0dSRichard Henderson 
2584a4554c6SHelge Deller     if (interrupt_request & CPU_INTERRUPT_NMI) {
2594a4554c6SHelge Deller         /* Raise TOC (NMI) interrupt */
2604a4554c6SHelge Deller         cpu_reset_interrupt(cs, CPU_INTERRUPT_NMI);
2614a4554c6SHelge Deller         cs->exception_index = EXCP_TOC;
2624a4554c6SHelge Deller         hppa_cpu_do_interrupt(cs);
2634a4554c6SHelge Deller         return true;
2644a4554c6SHelge Deller     }
2654a4554c6SHelge Deller 
2661a19da0dSRichard Henderson     /* If interrupts are requested and enabled, raise them.  */
2671a19da0dSRichard Henderson     if ((env->psw & PSW_I) && (interrupt_request & CPU_INTERRUPT_HARD)) {
2681a19da0dSRichard Henderson         cs->exception_index = EXCP_EXT_INTERRUPT;
2691a19da0dSRichard Henderson         hppa_cpu_do_interrupt(cs);
2701a19da0dSRichard Henderson         return true;
2711a19da0dSRichard Henderson     }
2721a19da0dSRichard Henderson     return false;
2731a19da0dSRichard Henderson }
274