master
c 314 lines 10.5 KB
Raw
1 /*
2 * HPPA interrupt helper routines
3 *
4 * Copyright (c) 2017 Richard Henderson
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "qemu/osdep.h"
21 #include "qemu/main-loop.h"
22 #include "qemu/log.h"
23 #include "cpu.h"
24 #include "exec/helper-proto.h"
25 #include "hw/core/cpu.h"
26 #include "hw/hppa/hppa_hardware.h"
27 #include "qemu/plugin.h"
28
29 static void eval_interrupt(HPPACPU *cpu)
30 {
31 CPUState *cs = CPU(cpu);
32 if (cpu->env.cr[CR_EIRR]) {
33 cpu_interrupt(cs, CPU_INTERRUPT_HARD);
34 } else {
35 cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
36 }
37 }
38
39 /* Each CPU has a word mapped into the GSC bus. Anything on the GSC bus
40 * can write to this word to raise an external interrupt on the target CPU.
41 * This includes the system controller (DINO) for regular devices, or
42 * another CPU for SMP interprocessor interrupts.
43 */
44 static uint64_t io_eir_read(void *opaque, hwaddr addr, unsigned size)
45 {
46 HPPACPU *cpu = opaque;
47
48 /* ??? What does a read of this register over the GSC bus do? */
49 return cpu->env.cr[CR_EIRR];
50 }
51
52 static void io_eir_write(void *opaque, hwaddr addr,
53 uint64_t data, unsigned size)
54 {
55 HPPACPU *cpu = opaque;
56 CPUHPPAState *env = &cpu->env;
57 int widthm1 = 31;
58 int le_bit;
59
60 /* The default PSW.W controls the width of EIRR. */
61 if (hppa_is_pa20(env) && env->cr[CR_PSW_DEFAULT] & PDC_PSW_WIDE_BIT) {
62 widthm1 = 63;
63 }
64 le_bit = ~data & widthm1;
65
66 env->cr[CR_EIRR] |= 1ull << le_bit;
67 eval_interrupt(cpu);
68 }
69
70 const MemoryRegionOps hppa_io_eir_ops = {
71 .read = io_eir_read,
72 .write = io_eir_write,
73 .valid.min_access_size = 4,
74 .valid.max_access_size = 4,
75 .impl.min_access_size = 4,
76 .impl.max_access_size = 4,
77 };
78
79 void hppa_cpu_alarm_timer(void *opaque)
80 {
81 /* Raise interrupt 0. */
82 io_eir_write(opaque, 0, 0, 4);
83 }
84
85 void HELPER(write_eirr)(CPUHPPAState *env, target_ulong val)
86 {
87 env->cr[CR_EIRR] &= ~val;
88 bql_lock();
89 eval_interrupt(env_archcpu(env));
90 bql_unlock();
91 }
92
93 void hppa_cpu_do_interrupt(CPUState *cs)
94 {
95 HPPACPU *cpu = HPPA_CPU(cs);
96 CPUHPPAState *env = &cpu->env;
97 int i = cs->exception_index;
98 uint64_t old_psw, old_gva_offset_mask;
99 uint64_t last_pc = cs->cc->get_pc(cs);
100
101 /* As documented in pa2.0 -- interruption handling. */
102 /* step 1 */
103 env->cr[CR_IPSW] = old_psw = cpu_hppa_get_psw(env);
104 old_gva_offset_mask = env->gva_offset_mask;
105
106 /* step 2 -- Note PSW_W is masked out again for pa1.x */
107 cpu_hppa_put_psw(env,
108 (env->cr[CR_PSW_DEFAULT] & PDC_PSW_WIDE_BIT ? PSW_W : 0) |
109 (i == EXCP_HPMC ? PSW_M : 0));
110
111 /* step 3 */
112 /*
113 * IIASQ is the top bits of the virtual address, or zero if translation
114 * is disabled -- with PSW_W == 0, this will reduce to the space.
115 */
116 if (old_psw & PSW_C) {
117 env->cr[CR_IIASQ] =
118 hppa_form_gva_mask(old_gva_offset_mask, env->iasq_f, env->iaoq_f) >> 32;
119 env->cr_back[0] =
120 hppa_form_gva_mask(old_gva_offset_mask, env->iasq_b, env->iaoq_b) >> 32;
121 } else {
122 env->cr[CR_IIASQ] = 0;
123 env->cr_back[0] = 0;
124 }
125 /* IIAOQ is the full offset for wide mode, or 32 bits for narrow mode. */
126 if (old_psw & PSW_W) {
127 env->cr[CR_IIAOQ] = env->iaoq_f;
128 env->cr_back[1] = env->iaoq_b;
129 } else {
130 env->cr[CR_IIAOQ] = (uint32_t)env->iaoq_f;
131 env->cr_back[1] = (uint32_t)env->iaoq_b;
132 }
133
134 if (old_psw & PSW_Q) {
135 /* step 5 */
136 /* ISR and IOR will be set elsewhere. */
137 switch (i) {
138 case EXCP_ILL:
139 case EXCP_BREAK:
140 case EXCP_OVERFLOW:
141 case EXCP_COND:
142 case EXCP_PRIV_REG:
143 case EXCP_PRIV_OPR:
144 /* IIR set via translate.c. */
145 break;
146
147 case EXCP_ASSIST:
148 case EXCP_DTLB_MISS:
149 case EXCP_NA_ITLB_MISS:
150 case EXCP_NA_DTLB_MISS:
151 case EXCP_DMAR:
152 case EXCP_DMPI:
153 case EXCP_UNALIGN:
154 case EXCP_DMP:
155 case EXCP_DMB:
156 case EXCP_TLB_DIRTY:
157 case EXCP_PAGE_REF:
158 case EXCP_ASSIST_EMU:
159 {
160 /* Avoid reading directly from the virtual address, lest we
161 raise another exception from some sort of TLB issue. */
162 /* ??? An alternate fool-proof method would be to store the
163 instruction data into the unwind info. That's probably
164 a bit too much in the way of extra storage required. */
165 vaddr vaddr = env->iaoq_f & -4;
166 hwaddr paddr = vaddr;
167
168 if (old_psw & PSW_C) {
169 int prot, t;
170
171 vaddr = hppa_form_gva_mask(old_gva_offset_mask,
172 env->iasq_f, vaddr);
173 t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX,
174 0, 0, &paddr, &prot);
175 if (t >= 0) {
176 /* We can't re-load the instruction. */
177 env->cr[CR_IIR] = 0;
178 break;
179 }
180 }
181 env->cr[CR_IIR] = ldl_be_phys(cs->as, paddr);
182 if (i == EXCP_ASSIST) {
183 /* stuff insn code into bits of FP exception register #1 */
184 env->fr[0] |= (env->cr[CR_IIR] & 0x03ffffff);
185 }
186 }
187 break;
188
189 default:
190 /* Other exceptions do not set IIR. */
191 break;
192 }
193
194 /* step 6 */
195 env->shadow[0] = env->gr[1];
196 env->shadow[1] = env->gr[8];
197 env->shadow[2] = env->gr[9];
198 env->shadow[3] = env->gr[16];
199 env->shadow[4] = env->gr[17];
200 env->shadow[5] = env->gr[24];
201 env->shadow[6] = env->gr[25];
202 }
203
204 /* step 7 */
205 if (i == EXCP_TOC) {
206 hwaddr pdc_toc_addr = FIRMWARE_START;
207
208 /* for 64-bit include the high bits of PDC */
209 pdc_toc_addr |= ((uint64_t) FIRMWARE_HIGH) << 32;
210 env->iaoq_f = hppa_form_gva(env, 0, pdc_toc_addr);
211
212 /* help SeaBIOS and provide iaoq_b and iasq_back in shadow regs */
213 env->gr[24] = env->cr_back[0];
214 env->gr[25] = env->cr_back[1];
215 } else {
216 env->iaoq_f = hppa_form_gva(env, 0, env->cr[CR_IVA] + 32 * i);
217 }
218 env->iaoq_b = hppa_form_gva(env, 0, env->iaoq_f + 4);
219 env->iasq_f = 0;
220 env->iasq_b = 0;
221
222 switch (i) {
223 case EXCP_HPMC:
224 case EXCP_POWER_FAIL:
225 case EXCP_RC:
226 case EXCP_EXT_INTERRUPT:
227 case EXCP_LPMC:
228 case EXCP_PER_INTERRUPT:
229 case EXCP_TOC:
230 qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
231 break;
232 default:
233 qemu_plugin_vcpu_exception_cb(cs, last_pc);
234 break;
235 }
236
237 if (qemu_loglevel_mask(CPU_LOG_INT)) {
238 static const char * const names[] = {
239 [EXCP_HPMC] = "high priority machine check",
240 [EXCP_POWER_FAIL] = "power fail interrupt",
241 [EXCP_RC] = "recovery counter trap",
242 [EXCP_EXT_INTERRUPT] = "external interrupt",
243 [EXCP_LPMC] = "low priority machine check",
244 [EXCP_ITLB_MISS] = "instruction tlb miss fault",
245 [EXCP_IMP] = "instruction memory protection trap",
246 [EXCP_ILL] = "illegal instruction trap",
247 [EXCP_BREAK] = "break instruction trap",
248 [EXCP_PRIV_OPR] = "privileged operation trap",
249 [EXCP_PRIV_REG] = "privileged register trap",
250 [EXCP_OVERFLOW] = "overflow trap",
251 [EXCP_COND] = "conditional trap",
252 [EXCP_ASSIST] = "assist exception trap",
253 [EXCP_DTLB_MISS] = "data tlb miss fault",
254 [EXCP_NA_ITLB_MISS] = "non-access instruction tlb miss",
255 [EXCP_NA_DTLB_MISS] = "non-access data tlb miss",
256 [EXCP_DMP] = "data memory protection trap",
257 [EXCP_DMB] = "data memory break trap",
258 [EXCP_TLB_DIRTY] = "tlb dirty bit trap",
259 [EXCP_PAGE_REF] = "page reference trap",
260 [EXCP_ASSIST_EMU] = "assist emulation trap",
261 [EXCP_HPT] = "high-privilege transfer trap",
262 [EXCP_LPT] = "low-privilege transfer trap",
263 [EXCP_TB] = "taken branch trap",
264 [EXCP_DMAR] = "data memory access rights trap",
265 [EXCP_DMPI] = "data memory protection id trap",
266 [EXCP_UNALIGN] = "unaligned data reference trap",
267 [EXCP_PER_INTERRUPT] = "performance monitor interrupt",
268 [EXCP_SYSCALL] = "syscall",
269 [EXCP_SYSCALL_LWS] = "syscall-lws",
270 [EXCP_TOC] = "TOC (transfer of control)",
271 };
272
273 FILE *logfile = qemu_log_trylock();
274 if (logfile) {
275 const char *name = NULL;
276
277 if (i >= 0 && i < ARRAY_SIZE(names)) {
278 name = names[i];
279 }
280 if (name) {
281 fprintf(logfile, "INT: cpu %d %s\n", cs->cpu_index, name);
282 } else {
283 fprintf(logfile, "INT: cpu %d unknown %d\n", cs->cpu_index, i);
284 }
285 hppa_cpu_dump_state(cs, logfile, 0);
286 qemu_log_unlock(logfile);
287 }
288 }
289 cs->exception_index = -1;
290 }
291
292 bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
293 {
294 HPPACPU *cpu = HPPA_CPU(cs);
295 CPUHPPAState *env = &cpu->env;
296
297 if (interrupt_request & CPU_INTERRUPT_NMI) {
298 /* Raise TOC (NMI) interrupt */
299 cpu_reset_interrupt(cs, CPU_INTERRUPT_NMI);
300 cs->exception_index = EXCP_TOC;
301 hppa_cpu_do_interrupt(cs);
302 return true;
303 }
304
305 /* If interrupts are requested and enabled, raise them. */
306 if ((interrupt_request & CPU_INTERRUPT_HARD)
307 && (env->psw & PSW_I)
308 && (env->cr[CR_EIRR] & env->cr[CR_EIEM])) {
309 cs->exception_index = EXCP_EXT_INTERRUPT;
310 hppa_cpu_do_interrupt(cs);
311 return true;
312 }
313 return false;
314 }