root / target-xtensa / op_helper.c @ b1669e5e
History | View | Annotate | Download (27.5 kB)
1 |
/*
|
---|---|
2 |
* Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions are met:
|
7 |
* * Redistributions of source code must retain the above copyright
|
8 |
* notice, this list of conditions and the following disclaimer.
|
9 |
* * Redistributions in binary form must reproduce the above copyright
|
10 |
* notice, this list of conditions and the following disclaimer in the
|
11 |
* documentation and/or other materials provided with the distribution.
|
12 |
* * Neither the name of the Open Source and Linux Lab nor the
|
13 |
* names of its contributors may be used to endorse or promote products
|
14 |
* derived from this software without specific prior written permission.
|
15 |
*
|
16 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17 |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
19 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
20 |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
21 |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
22 |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
23 |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
25 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26 |
*/
|
27 |
|
28 |
#include "cpu.h" |
29 |
#include "helper.h" |
30 |
#include "qemu/host-utils.h" |
31 |
#include "exec/softmmu_exec.h" |
32 |
|
33 |
static void do_unaligned_access(CPUXtensaState *env, |
34 |
target_ulong addr, int is_write, int is_user, uintptr_t retaddr); |
35 |
|
36 |
#define ALIGNED_ONLY
|
37 |
#define MMUSUFFIX _mmu
|
38 |
|
39 |
#define SHIFT 0 |
40 |
#include "exec/softmmu_template.h" |
41 |
|
42 |
#define SHIFT 1 |
43 |
#include "exec/softmmu_template.h" |
44 |
|
45 |
#define SHIFT 2 |
46 |
#include "exec/softmmu_template.h" |
47 |
|
48 |
#define SHIFT 3 |
49 |
#include "exec/softmmu_template.h" |
50 |
|
51 |
static void do_unaligned_access(CPUXtensaState *env, |
52 |
target_ulong addr, int is_write, int is_user, uintptr_t retaddr) |
53 |
{ |
54 |
if (xtensa_option_enabled(env->config, XTENSA_OPTION_UNALIGNED_EXCEPTION) &&
|
55 |
!xtensa_option_enabled(env->config, XTENSA_OPTION_HW_ALIGNMENT)) { |
56 |
cpu_restore_state(env, retaddr); |
57 |
HELPER(exception_cause_vaddr)(env, |
58 |
env->pc, LOAD_STORE_ALIGNMENT_CAUSE, addr); |
59 |
} |
60 |
} |
61 |
|
62 |
void tlb_fill(CPUXtensaState *env,
|
63 |
target_ulong vaddr, int is_write, int mmu_idx, uintptr_t retaddr) |
64 |
{ |
65 |
uint32_t paddr; |
66 |
uint32_t page_size; |
67 |
unsigned access;
|
68 |
int ret = xtensa_get_physical_addr(env, true, vaddr, is_write, mmu_idx, |
69 |
&paddr, &page_size, &access); |
70 |
|
71 |
qemu_log("%s(%08x, %d, %d) -> %08x, ret = %d\n", __func__,
|
72 |
vaddr, is_write, mmu_idx, paddr, ret); |
73 |
|
74 |
if (ret == 0) { |
75 |
tlb_set_page(env, |
76 |
vaddr & TARGET_PAGE_MASK, |
77 |
paddr & TARGET_PAGE_MASK, |
78 |
access, mmu_idx, page_size); |
79 |
} else {
|
80 |
cpu_restore_state(env, retaddr); |
81 |
HELPER(exception_cause_vaddr)(env, env->pc, ret, vaddr); |
82 |
} |
83 |
} |
84 |
|
85 |
static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) |
86 |
{ |
87 |
uint32_t paddr; |
88 |
uint32_t page_size; |
89 |
unsigned access;
|
90 |
int ret = xtensa_get_physical_addr(env, false, vaddr, 2, 0, |
91 |
&paddr, &page_size, &access); |
92 |
if (ret == 0) { |
93 |
tb_invalidate_phys_addr(paddr); |
94 |
} |
95 |
} |
96 |
|
97 |
void HELPER(exception)(CPUXtensaState *env, uint32_t excp)
|
98 |
{ |
99 |
env->exception_index = excp; |
100 |
if (excp == EXCP_DEBUG) {
|
101 |
env->exception_taken = 0;
|
102 |
} |
103 |
cpu_loop_exit(env); |
104 |
} |
105 |
|
106 |
void HELPER(exception_cause)(CPUXtensaState *env, uint32_t pc, uint32_t cause)
|
107 |
{ |
108 |
uint32_t vector; |
109 |
|
110 |
env->pc = pc; |
111 |
if (env->sregs[PS] & PS_EXCM) {
|
112 |
if (env->config->ndepc) {
|
113 |
env->sregs[DEPC] = pc; |
114 |
} else {
|
115 |
env->sregs[EPC1] = pc; |
116 |
} |
117 |
vector = EXC_DOUBLE; |
118 |
} else {
|
119 |
env->sregs[EPC1] = pc; |
120 |
vector = (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; |
121 |
} |
122 |
|
123 |
env->sregs[EXCCAUSE] = cause; |
124 |
env->sregs[PS] |= PS_EXCM; |
125 |
|
126 |
HELPER(exception)(env, vector); |
127 |
} |
128 |
|
129 |
void HELPER(exception_cause_vaddr)(CPUXtensaState *env,
|
130 |
uint32_t pc, uint32_t cause, uint32_t vaddr) |
131 |
{ |
132 |
env->sregs[EXCVADDR] = vaddr; |
133 |
HELPER(exception_cause)(env, pc, cause); |
134 |
} |
135 |
|
136 |
void debug_exception_env(CPUXtensaState *env, uint32_t cause)
|
137 |
{ |
138 |
if (xtensa_get_cintlevel(env) < env->config->debug_level) {
|
139 |
HELPER(debug_exception)(env, env->pc, cause); |
140 |
} |
141 |
} |
142 |
|
143 |
void HELPER(debug_exception)(CPUXtensaState *env, uint32_t pc, uint32_t cause)
|
144 |
{ |
145 |
unsigned level = env->config->debug_level;
|
146 |
|
147 |
env->pc = pc; |
148 |
env->sregs[DEBUGCAUSE] = cause; |
149 |
env->sregs[EPC1 + level - 1] = pc;
|
150 |
env->sregs[EPS2 + level - 2] = env->sregs[PS];
|
151 |
env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM | |
152 |
(level << PS_INTLEVEL_SHIFT); |
153 |
HELPER(exception)(env, EXC_DEBUG); |
154 |
} |
155 |
|
156 |
uint32_t HELPER(nsa)(uint32_t v) |
157 |
{ |
158 |
if (v & 0x80000000) { |
159 |
v = ~v; |
160 |
} |
161 |
return v ? clz32(v) - 1 : 31; |
162 |
} |
163 |
|
164 |
uint32_t HELPER(nsau)(uint32_t v) |
165 |
{ |
166 |
return v ? clz32(v) : 32; |
167 |
} |
168 |
|
169 |
static void copy_window_from_phys(CPUXtensaState *env, |
170 |
uint32_t window, uint32_t phys, uint32_t n) |
171 |
{ |
172 |
assert(phys < env->config->nareg); |
173 |
if (phys + n <= env->config->nareg) {
|
174 |
memcpy(env->regs + window, env->phys_regs + phys, |
175 |
n * sizeof(uint32_t));
|
176 |
} else {
|
177 |
uint32_t n1 = env->config->nareg - phys; |
178 |
memcpy(env->regs + window, env->phys_regs + phys, |
179 |
n1 * sizeof(uint32_t));
|
180 |
memcpy(env->regs + window + n1, env->phys_regs, |
181 |
(n - n1) * sizeof(uint32_t));
|
182 |
} |
183 |
} |
184 |
|
185 |
static void copy_phys_from_window(CPUXtensaState *env, |
186 |
uint32_t phys, uint32_t window, uint32_t n) |
187 |
{ |
188 |
assert(phys < env->config->nareg); |
189 |
if (phys + n <= env->config->nareg) {
|
190 |
memcpy(env->phys_regs + phys, env->regs + window, |
191 |
n * sizeof(uint32_t));
|
192 |
} else {
|
193 |
uint32_t n1 = env->config->nareg - phys; |
194 |
memcpy(env->phys_regs + phys, env->regs + window, |
195 |
n1 * sizeof(uint32_t));
|
196 |
memcpy(env->phys_regs, env->regs + window + n1, |
197 |
(n - n1) * sizeof(uint32_t));
|
198 |
} |
199 |
} |
200 |
|
201 |
|
202 |
static inline unsigned windowbase_bound(unsigned a, const CPUXtensaState *env) |
203 |
{ |
204 |
return a & (env->config->nareg / 4 - 1); |
205 |
} |
206 |
|
207 |
static inline unsigned windowstart_bit(unsigned a, const CPUXtensaState *env) |
208 |
{ |
209 |
return 1 << windowbase_bound(a, env); |
210 |
} |
211 |
|
212 |
void xtensa_sync_window_from_phys(CPUXtensaState *env)
|
213 |
{ |
214 |
copy_window_from_phys(env, 0, env->sregs[WINDOW_BASE] * 4, 16); |
215 |
} |
216 |
|
217 |
void xtensa_sync_phys_from_window(CPUXtensaState *env)
|
218 |
{ |
219 |
copy_phys_from_window(env, env->sregs[WINDOW_BASE] * 4, 0, 16); |
220 |
} |
221 |
|
222 |
static void rotate_window_abs(CPUXtensaState *env, uint32_t position) |
223 |
{ |
224 |
xtensa_sync_phys_from_window(env); |
225 |
env->sregs[WINDOW_BASE] = windowbase_bound(position, env); |
226 |
xtensa_sync_window_from_phys(env); |
227 |
} |
228 |
|
229 |
static void rotate_window(CPUXtensaState *env, uint32_t delta) |
230 |
{ |
231 |
rotate_window_abs(env, env->sregs[WINDOW_BASE] + delta); |
232 |
} |
233 |
|
234 |
void HELPER(wsr_windowbase)(CPUXtensaState *env, uint32_t v)
|
235 |
{ |
236 |
rotate_window_abs(env, v); |
237 |
} |
238 |
|
239 |
void HELPER(entry)(CPUXtensaState *env, uint32_t pc, uint32_t s, uint32_t imm)
|
240 |
{ |
241 |
int callinc = (env->sregs[PS] & PS_CALLINC) >> PS_CALLINC_SHIFT;
|
242 |
if (s > 3 || ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) != 0) { |
243 |
qemu_log("Illegal entry instruction(pc = %08x), PS = %08x\n",
|
244 |
pc, env->sregs[PS]); |
245 |
HELPER(exception_cause)(env, pc, ILLEGAL_INSTRUCTION_CAUSE); |
246 |
} else {
|
247 |
env->regs[(callinc << 2) | (s & 3)] = env->regs[s] - (imm << 3); |
248 |
rotate_window(env, callinc); |
249 |
env->sregs[WINDOW_START] |= |
250 |
windowstart_bit(env->sregs[WINDOW_BASE], env); |
251 |
} |
252 |
} |
253 |
|
254 |
void HELPER(window_check)(CPUXtensaState *env, uint32_t pc, uint32_t w)
|
255 |
{ |
256 |
uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); |
257 |
uint32_t windowstart = env->sregs[WINDOW_START]; |
258 |
uint32_t m, n; |
259 |
|
260 |
if ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) {
|
261 |
return;
|
262 |
} |
263 |
|
264 |
for (n = 1; ; ++n) { |
265 |
if (n > w) {
|
266 |
return;
|
267 |
} |
268 |
if (windowstart & windowstart_bit(windowbase + n, env)) {
|
269 |
break;
|
270 |
} |
271 |
} |
272 |
|
273 |
m = windowbase_bound(windowbase + n, env); |
274 |
rotate_window(env, n); |
275 |
env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) | |
276 |
(windowbase << PS_OWB_SHIFT) | PS_EXCM; |
277 |
env->sregs[EPC1] = env->pc = pc; |
278 |
|
279 |
if (windowstart & windowstart_bit(m + 1, env)) { |
280 |
HELPER(exception)(env, EXC_WINDOW_OVERFLOW4); |
281 |
} else if (windowstart & windowstart_bit(m + 2, env)) { |
282 |
HELPER(exception)(env, EXC_WINDOW_OVERFLOW8); |
283 |
} else {
|
284 |
HELPER(exception)(env, EXC_WINDOW_OVERFLOW12); |
285 |
} |
286 |
} |
287 |
|
288 |
uint32_t HELPER(retw)(CPUXtensaState *env, uint32_t pc) |
289 |
{ |
290 |
int n = (env->regs[0] >> 30) & 0x3; |
291 |
int m = 0; |
292 |
uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); |
293 |
uint32_t windowstart = env->sregs[WINDOW_START]; |
294 |
uint32_t ret_pc = 0;
|
295 |
|
296 |
if (windowstart & windowstart_bit(windowbase - 1, env)) { |
297 |
m = 1;
|
298 |
} else if (windowstart & windowstart_bit(windowbase - 2, env)) { |
299 |
m = 2;
|
300 |
} else if (windowstart & windowstart_bit(windowbase - 3, env)) { |
301 |
m = 3;
|
302 |
} |
303 |
|
304 |
if (n == 0 || (m != 0 && m != n) || |
305 |
((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) != 0) {
|
306 |
qemu_log("Illegal retw instruction(pc = %08x), "
|
307 |
"PS = %08x, m = %d, n = %d\n",
|
308 |
pc, env->sregs[PS], m, n); |
309 |
HELPER(exception_cause)(env, pc, ILLEGAL_INSTRUCTION_CAUSE); |
310 |
} else {
|
311 |
int owb = windowbase;
|
312 |
|
313 |
ret_pc = (pc & 0xc0000000) | (env->regs[0] & 0x3fffffff); |
314 |
|
315 |
rotate_window(env, -n); |
316 |
if (windowstart & windowstart_bit(env->sregs[WINDOW_BASE], env)) {
|
317 |
env->sregs[WINDOW_START] &= ~windowstart_bit(owb, env); |
318 |
} else {
|
319 |
/* window underflow */
|
320 |
env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) | |
321 |
(windowbase << PS_OWB_SHIFT) | PS_EXCM; |
322 |
env->sregs[EPC1] = env->pc = pc; |
323 |
|
324 |
if (n == 1) { |
325 |
HELPER(exception)(env, EXC_WINDOW_UNDERFLOW4); |
326 |
} else if (n == 2) { |
327 |
HELPER(exception)(env, EXC_WINDOW_UNDERFLOW8); |
328 |
} else if (n == 3) { |
329 |
HELPER(exception)(env, EXC_WINDOW_UNDERFLOW12); |
330 |
} |
331 |
} |
332 |
} |
333 |
return ret_pc;
|
334 |
} |
335 |
|
336 |
void HELPER(rotw)(CPUXtensaState *env, uint32_t imm4)
|
337 |
{ |
338 |
rotate_window(env, imm4); |
339 |
} |
340 |
|
341 |
void HELPER(restore_owb)(CPUXtensaState *env)
|
342 |
{ |
343 |
rotate_window_abs(env, (env->sregs[PS] & PS_OWB) >> PS_OWB_SHIFT); |
344 |
} |
345 |
|
346 |
void HELPER(movsp)(CPUXtensaState *env, uint32_t pc)
|
347 |
{ |
348 |
if ((env->sregs[WINDOW_START] &
|
349 |
(windowstart_bit(env->sregs[WINDOW_BASE] - 3, env) |
|
350 |
windowstart_bit(env->sregs[WINDOW_BASE] - 2, env) |
|
351 |
windowstart_bit(env->sregs[WINDOW_BASE] - 1, env))) == 0) { |
352 |
HELPER(exception_cause)(env, pc, ALLOCA_CAUSE); |
353 |
} |
354 |
} |
355 |
|
356 |
void HELPER(wsr_lbeg)(CPUXtensaState *env, uint32_t v)
|
357 |
{ |
358 |
if (env->sregs[LBEG] != v) {
|
359 |
tb_invalidate_virtual_addr(env, env->sregs[LEND] - 1);
|
360 |
env->sregs[LBEG] = v; |
361 |
} |
362 |
} |
363 |
|
364 |
void HELPER(wsr_lend)(CPUXtensaState *env, uint32_t v)
|
365 |
{ |
366 |
if (env->sregs[LEND] != v) {
|
367 |
tb_invalidate_virtual_addr(env, env->sregs[LEND] - 1);
|
368 |
env->sregs[LEND] = v; |
369 |
tb_invalidate_virtual_addr(env, env->sregs[LEND] - 1);
|
370 |
} |
371 |
} |
372 |
|
373 |
void HELPER(dump_state)(CPUXtensaState *env)
|
374 |
{ |
375 |
XtensaCPU *cpu = xtensa_env_get_cpu(env); |
376 |
|
377 |
cpu_dump_state(CPU(cpu), stderr, fprintf, 0);
|
378 |
} |
379 |
|
380 |
void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel)
|
381 |
{ |
382 |
CPUState *cpu; |
383 |
|
384 |
env->pc = pc; |
385 |
env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | |
386 |
(intlevel << PS_INTLEVEL_SHIFT); |
387 |
check_interrupts(env); |
388 |
if (env->pending_irq_level) {
|
389 |
cpu_loop_exit(env); |
390 |
return;
|
391 |
} |
392 |
|
393 |
cpu = CPU(xtensa_env_get_cpu(env)); |
394 |
env->halt_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
395 |
cpu->halted = 1;
|
396 |
if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
|
397 |
xtensa_rearm_ccompare_timer(env); |
398 |
} |
399 |
HELPER(exception)(env, EXCP_HLT); |
400 |
} |
401 |
|
402 |
void HELPER(timer_irq)(CPUXtensaState *env, uint32_t id, uint32_t active)
|
403 |
{ |
404 |
xtensa_timer_irq(env, id, active); |
405 |
} |
406 |
|
407 |
void HELPER(advance_ccount)(CPUXtensaState *env, uint32_t d)
|
408 |
{ |
409 |
xtensa_advance_ccount(env, d); |
410 |
} |
411 |
|
412 |
void HELPER(check_interrupts)(CPUXtensaState *env)
|
413 |
{ |
414 |
check_interrupts(env); |
415 |
} |
416 |
|
417 |
/*!
|
418 |
* Check vaddr accessibility/cache attributes and raise an exception if
|
419 |
* specified by the ATOMCTL SR.
|
420 |
*
|
421 |
* Note: local memory exclusion is not implemented
|
422 |
*/
|
423 |
void HELPER(check_atomctl)(CPUXtensaState *env, uint32_t pc, uint32_t vaddr)
|
424 |
{ |
425 |
uint32_t paddr, page_size, access; |
426 |
uint32_t atomctl = env->sregs[ATOMCTL]; |
427 |
int rc = xtensa_get_physical_addr(env, true, vaddr, 1, |
428 |
xtensa_get_cring(env), &paddr, &page_size, &access); |
429 |
|
430 |
/*
|
431 |
* s32c1i never causes LOAD_PROHIBITED_CAUSE exceptions,
|
432 |
* see opcode description in the ISA
|
433 |
*/
|
434 |
if (rc == 0 && |
435 |
(access & (PAGE_READ | PAGE_WRITE)) != (PAGE_READ | PAGE_WRITE)) { |
436 |
rc = STORE_PROHIBITED_CAUSE; |
437 |
} |
438 |
|
439 |
if (rc) {
|
440 |
HELPER(exception_cause_vaddr)(env, pc, rc, vaddr); |
441 |
} |
442 |
|
443 |
/*
|
444 |
* When data cache is not configured use ATOMCTL bypass field.
|
445 |
* See ISA, 4.3.12.4 The Atomic Operation Control Register (ATOMCTL)
|
446 |
* under the Conditional Store Option.
|
447 |
*/
|
448 |
if (!xtensa_option_enabled(env->config, XTENSA_OPTION_DCACHE)) {
|
449 |
access = PAGE_CACHE_BYPASS; |
450 |
} |
451 |
|
452 |
switch (access & PAGE_CACHE_MASK) {
|
453 |
case PAGE_CACHE_WB:
|
454 |
atomctl >>= 2;
|
455 |
/* fall through */
|
456 |
case PAGE_CACHE_WT:
|
457 |
atomctl >>= 2;
|
458 |
/* fall through */
|
459 |
case PAGE_CACHE_BYPASS:
|
460 |
if ((atomctl & 0x3) == 0) { |
461 |
HELPER(exception_cause_vaddr)(env, pc, |
462 |
LOAD_STORE_ERROR_CAUSE, vaddr); |
463 |
} |
464 |
break;
|
465 |
|
466 |
case PAGE_CACHE_ISOLATE:
|
467 |
HELPER(exception_cause_vaddr)(env, pc, |
468 |
LOAD_STORE_ERROR_CAUSE, vaddr); |
469 |
break;
|
470 |
|
471 |
default:
|
472 |
break;
|
473 |
} |
474 |
} |
475 |
|
476 |
void HELPER(wsr_rasid)(CPUXtensaState *env, uint32_t v)
|
477 |
{ |
478 |
v = (v & 0xffffff00) | 0x1; |
479 |
if (v != env->sregs[RASID]) {
|
480 |
env->sregs[RASID] = v; |
481 |
tlb_flush(env, 1);
|
482 |
} |
483 |
} |
484 |
|
485 |
static uint32_t get_page_size(const CPUXtensaState *env, bool dtlb, uint32_t way) |
486 |
{ |
487 |
uint32_t tlbcfg = env->sregs[dtlb ? DTLBCFG : ITLBCFG]; |
488 |
|
489 |
switch (way) {
|
490 |
case 4: |
491 |
return (tlbcfg >> 16) & 0x3; |
492 |
|
493 |
case 5: |
494 |
return (tlbcfg >> 20) & 0x1; |
495 |
|
496 |
case 6: |
497 |
return (tlbcfg >> 24) & 0x1; |
498 |
|
499 |
default:
|
500 |
return 0; |
501 |
} |
502 |
} |
503 |
|
504 |
/*!
|
505 |
* Get bit mask for the virtual address bits translated by the TLB way
|
506 |
*/
|
507 |
uint32_t xtensa_tlb_get_addr_mask(const CPUXtensaState *env, bool dtlb, uint32_t way) |
508 |
{ |
509 |
if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
|
510 |
bool varway56 = dtlb ?
|
511 |
env->config->dtlb.varway56 : |
512 |
env->config->itlb.varway56; |
513 |
|
514 |
switch (way) {
|
515 |
case 4: |
516 |
return 0xfff00000 << get_page_size(env, dtlb, way) * 2; |
517 |
|
518 |
case 5: |
519 |
if (varway56) {
|
520 |
return 0xf8000000 << get_page_size(env, dtlb, way); |
521 |
} else {
|
522 |
return 0xf8000000; |
523 |
} |
524 |
|
525 |
case 6: |
526 |
if (varway56) {
|
527 |
return 0xf0000000 << (1 - get_page_size(env, dtlb, way)); |
528 |
} else {
|
529 |
return 0xf0000000; |
530 |
} |
531 |
|
532 |
default:
|
533 |
return 0xfffff000; |
534 |
} |
535 |
} else {
|
536 |
return REGION_PAGE_MASK;
|
537 |
} |
538 |
} |
539 |
|
540 |
/*!
|
541 |
* Get bit mask for the 'VPN without index' field.
|
542 |
* See ISA, 4.6.5.6, data format for RxTLB0
|
543 |
*/
|
544 |
static uint32_t get_vpn_mask(const CPUXtensaState *env, bool dtlb, uint32_t way) |
545 |
{ |
546 |
if (way < 4) { |
547 |
bool is32 = (dtlb ?
|
548 |
env->config->dtlb.nrefillentries : |
549 |
env->config->itlb.nrefillentries) == 32;
|
550 |
return is32 ? 0xffff8000 : 0xffffc000; |
551 |
} else if (way == 4) { |
552 |
return xtensa_tlb_get_addr_mask(env, dtlb, way) << 2; |
553 |
} else if (way <= 6) { |
554 |
uint32_t mask = xtensa_tlb_get_addr_mask(env, dtlb, way); |
555 |
bool varway56 = dtlb ?
|
556 |
env->config->dtlb.varway56 : |
557 |
env->config->itlb.varway56; |
558 |
|
559 |
if (varway56) {
|
560 |
return mask << (way == 5 ? 2 : 3); |
561 |
} else {
|
562 |
return mask << 1; |
563 |
} |
564 |
} else {
|
565 |
return 0xfffff000; |
566 |
} |
567 |
} |
568 |
|
569 |
/*!
|
570 |
* Split virtual address into VPN (with index) and entry index
|
571 |
* for the given TLB way
|
572 |
*/
|
573 |
void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, bool dtlb, |
574 |
uint32_t *vpn, uint32_t wi, uint32_t *ei) |
575 |
{ |
576 |
bool varway56 = dtlb ?
|
577 |
env->config->dtlb.varway56 : |
578 |
env->config->itlb.varway56; |
579 |
|
580 |
if (!dtlb) {
|
581 |
wi &= 7;
|
582 |
} |
583 |
|
584 |
if (wi < 4) { |
585 |
bool is32 = (dtlb ?
|
586 |
env->config->dtlb.nrefillentries : |
587 |
env->config->itlb.nrefillentries) == 32;
|
588 |
*ei = (v >> 12) & (is32 ? 0x7 : 0x3); |
589 |
} else {
|
590 |
switch (wi) {
|
591 |
case 4: |
592 |
{ |
593 |
uint32_t eibase = 20 + get_page_size(env, dtlb, wi) * 2; |
594 |
*ei = (v >> eibase) & 0x3;
|
595 |
} |
596 |
break;
|
597 |
|
598 |
case 5: |
599 |
if (varway56) {
|
600 |
uint32_t eibase = 27 + get_page_size(env, dtlb, wi);
|
601 |
*ei = (v >> eibase) & 0x3;
|
602 |
} else {
|
603 |
*ei = (v >> 27) & 0x1; |
604 |
} |
605 |
break;
|
606 |
|
607 |
case 6: |
608 |
if (varway56) {
|
609 |
uint32_t eibase = 29 - get_page_size(env, dtlb, wi);
|
610 |
*ei = (v >> eibase) & 0x7;
|
611 |
} else {
|
612 |
*ei = (v >> 28) & 0x1; |
613 |
} |
614 |
break;
|
615 |
|
616 |
default:
|
617 |
*ei = 0;
|
618 |
break;
|
619 |
} |
620 |
} |
621 |
*vpn = v & xtensa_tlb_get_addr_mask(env, dtlb, wi); |
622 |
} |
623 |
|
624 |
/*!
|
625 |
* Split TLB address into TLB way, entry index and VPN (with index).
|
626 |
* See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format
|
627 |
*/
|
628 |
static void split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb, |
629 |
uint32_t *vpn, uint32_t *wi, uint32_t *ei) |
630 |
{ |
631 |
if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
|
632 |
*wi = v & (dtlb ? 0xf : 0x7); |
633 |
split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei); |
634 |
} else {
|
635 |
*vpn = v & REGION_PAGE_MASK; |
636 |
*wi = 0;
|
637 |
*ei = (v >> 29) & 0x7; |
638 |
} |
639 |
} |
640 |
|
641 |
static xtensa_tlb_entry *get_tlb_entry(CPUXtensaState *env,
|
642 |
uint32_t v, bool dtlb, uint32_t *pwi)
|
643 |
{ |
644 |
uint32_t vpn; |
645 |
uint32_t wi; |
646 |
uint32_t ei; |
647 |
|
648 |
split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); |
649 |
if (pwi) {
|
650 |
*pwi = wi; |
651 |
} |
652 |
return xtensa_tlb_get_entry(env, dtlb, wi, ei);
|
653 |
} |
654 |
|
655 |
uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) |
656 |
{ |
657 |
if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
|
658 |
uint32_t wi; |
659 |
const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi);
|
660 |
return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid;
|
661 |
} else {
|
662 |
return v & REGION_PAGE_MASK;
|
663 |
} |
664 |
} |
665 |
|
666 |
uint32_t HELPER(rtlb1)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) |
667 |
{ |
668 |
const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, NULL); |
669 |
return entry->paddr | entry->attr;
|
670 |
} |
671 |
|
672 |
void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
|
673 |
{ |
674 |
if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
|
675 |
uint32_t wi; |
676 |
xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); |
677 |
if (entry->variable && entry->asid) {
|
678 |
tlb_flush_page(env, entry->vaddr); |
679 |
entry->asid = 0;
|
680 |
} |
681 |
} |
682 |
} |
683 |
|
684 |
uint32_t HELPER(ptlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) |
685 |
{ |
686 |
if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
|
687 |
uint32_t wi; |
688 |
uint32_t ei; |
689 |
uint8_t ring; |
690 |
int res = xtensa_tlb_lookup(env, v, dtlb, &wi, &ei, &ring);
|
691 |
|
692 |
switch (res) {
|
693 |
case 0: |
694 |
if (ring >= xtensa_get_ring(env)) {
|
695 |
return (v & 0xfffff000) | wi | (dtlb ? 0x10 : 0x8); |
696 |
} |
697 |
break;
|
698 |
|
699 |
case INST_TLB_MULTI_HIT_CAUSE:
|
700 |
case LOAD_STORE_TLB_MULTI_HIT_CAUSE:
|
701 |
HELPER(exception_cause_vaddr)(env, env->pc, res, v); |
702 |
break;
|
703 |
} |
704 |
return 0; |
705 |
} else {
|
706 |
return (v & REGION_PAGE_MASK) | 0x1; |
707 |
} |
708 |
} |
709 |
|
710 |
void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env, |
711 |
xtensa_tlb_entry *entry, bool dtlb,
|
712 |
unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte) |
713 |
{ |
714 |
entry->vaddr = vpn; |
715 |
entry->paddr = pte & xtensa_tlb_get_addr_mask(env, dtlb, wi); |
716 |
entry->asid = (env->sregs[RASID] >> ((pte >> 1) & 0x18)) & 0xff; |
717 |
entry->attr = pte & 0xf;
|
718 |
} |
719 |
|
720 |
void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb, |
721 |
unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte) |
722 |
{ |
723 |
xtensa_tlb_entry *entry = xtensa_tlb_get_entry(env, dtlb, wi, ei); |
724 |
|
725 |
if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
|
726 |
if (entry->variable) {
|
727 |
if (entry->asid) {
|
728 |
tlb_flush_page(env, entry->vaddr); |
729 |
} |
730 |
xtensa_tlb_set_entry_mmu(env, entry, dtlb, wi, ei, vpn, pte); |
731 |
tlb_flush_page(env, entry->vaddr); |
732 |
} else {
|
733 |
qemu_log("%s %d, %d, %d trying to set immutable entry\n",
|
734 |
__func__, dtlb, wi, ei); |
735 |
} |
736 |
} else {
|
737 |
tlb_flush_page(env, entry->vaddr); |
738 |
if (xtensa_option_enabled(env->config,
|
739 |
XTENSA_OPTION_REGION_TRANSLATION)) { |
740 |
entry->paddr = pte & REGION_PAGE_MASK; |
741 |
} |
742 |
entry->attr = pte & 0xf;
|
743 |
} |
744 |
} |
745 |
|
746 |
void HELPER(wtlb)(CPUXtensaState *env, uint32_t p, uint32_t v, uint32_t dtlb)
|
747 |
{ |
748 |
uint32_t vpn; |
749 |
uint32_t wi; |
750 |
uint32_t ei; |
751 |
split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); |
752 |
xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p); |
753 |
} |
754 |
|
755 |
|
756 |
void HELPER(wsr_ibreakenable)(CPUXtensaState *env, uint32_t v)
|
757 |
{ |
758 |
uint32_t change = v ^ env->sregs[IBREAKENABLE]; |
759 |
unsigned i;
|
760 |
|
761 |
for (i = 0; i < env->config->nibreak; ++i) { |
762 |
if (change & (1 << i)) { |
763 |
tb_invalidate_virtual_addr(env, env->sregs[IBREAKA + i]); |
764 |
} |
765 |
} |
766 |
env->sregs[IBREAKENABLE] = v & ((1 << env->config->nibreak) - 1); |
767 |
} |
768 |
|
769 |
void HELPER(wsr_ibreaka)(CPUXtensaState *env, uint32_t i, uint32_t v)
|
770 |
{ |
771 |
if (env->sregs[IBREAKENABLE] & (1 << i) && env->sregs[IBREAKA + i] != v) { |
772 |
tb_invalidate_virtual_addr(env, env->sregs[IBREAKA + i]); |
773 |
tb_invalidate_virtual_addr(env, v); |
774 |
} |
775 |
env->sregs[IBREAKA + i] = v; |
776 |
} |
777 |
|
778 |
static void set_dbreak(CPUXtensaState *env, unsigned i, uint32_t dbreaka, |
779 |
uint32_t dbreakc) |
780 |
{ |
781 |
int flags = BP_CPU | BP_STOP_BEFORE_ACCESS;
|
782 |
uint32_t mask = dbreakc | ~DBREAKC_MASK; |
783 |
|
784 |
if (env->cpu_watchpoint[i]) {
|
785 |
cpu_watchpoint_remove_by_ref(env, env->cpu_watchpoint[i]); |
786 |
} |
787 |
if (dbreakc & DBREAKC_SB) {
|
788 |
flags |= BP_MEM_WRITE; |
789 |
} |
790 |
if (dbreakc & DBREAKC_LB) {
|
791 |
flags |= BP_MEM_READ; |
792 |
} |
793 |
/* contiguous mask after inversion is one less than some power of 2 */
|
794 |
if ((~mask + 1) & ~mask) { |
795 |
qemu_log("DBREAKC mask is not contiguous: 0x%08x\n", dbreakc);
|
796 |
/* cut mask after the first zero bit */
|
797 |
mask = 0xffffffff << (32 - clo32(mask)); |
798 |
} |
799 |
if (cpu_watchpoint_insert(env, dbreaka & mask, ~mask + 1, |
800 |
flags, &env->cpu_watchpoint[i])) { |
801 |
env->cpu_watchpoint[i] = NULL;
|
802 |
qemu_log("Failed to set data breakpoint at 0x%08x/%d\n",
|
803 |
dbreaka & mask, ~mask + 1);
|
804 |
} |
805 |
} |
806 |
|
807 |
void HELPER(wsr_dbreaka)(CPUXtensaState *env, uint32_t i, uint32_t v)
|
808 |
{ |
809 |
uint32_t dbreakc = env->sregs[DBREAKC + i]; |
810 |
|
811 |
if ((dbreakc & DBREAKC_SB_LB) &&
|
812 |
env->sregs[DBREAKA + i] != v) { |
813 |
set_dbreak(env, i, v, dbreakc); |
814 |
} |
815 |
env->sregs[DBREAKA + i] = v; |
816 |
} |
817 |
|
818 |
void HELPER(wsr_dbreakc)(CPUXtensaState *env, uint32_t i, uint32_t v)
|
819 |
{ |
820 |
if ((env->sregs[DBREAKC + i] ^ v) & (DBREAKC_SB_LB | DBREAKC_MASK)) {
|
821 |
if (v & DBREAKC_SB_LB) {
|
822 |
set_dbreak(env, i, env->sregs[DBREAKA + i], v); |
823 |
} else {
|
824 |
if (env->cpu_watchpoint[i]) {
|
825 |
cpu_watchpoint_remove_by_ref(env, env->cpu_watchpoint[i]); |
826 |
env->cpu_watchpoint[i] = NULL;
|
827 |
} |
828 |
} |
829 |
} |
830 |
env->sregs[DBREAKC + i] = v; |
831 |
} |
832 |
|
833 |
void HELPER(wur_fcr)(CPUXtensaState *env, uint32_t v)
|
834 |
{ |
835 |
static const int rounding_mode[] = { |
836 |
float_round_nearest_even, |
837 |
float_round_to_zero, |
838 |
float_round_up, |
839 |
float_round_down, |
840 |
}; |
841 |
|
842 |
env->uregs[FCR] = v & 0xfffff07f;
|
843 |
set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status);
|
844 |
} |
845 |
|
846 |
float32 HELPER(abs_s)(float32 v) |
847 |
{ |
848 |
return float32_abs(v);
|
849 |
} |
850 |
|
851 |
float32 HELPER(neg_s)(float32 v) |
852 |
{ |
853 |
return float32_chs(v);
|
854 |
} |
855 |
|
856 |
float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b) |
857 |
{ |
858 |
return float32_add(a, b, &env->fp_status);
|
859 |
} |
860 |
|
861 |
float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b) |
862 |
{ |
863 |
return float32_sub(a, b, &env->fp_status);
|
864 |
} |
865 |
|
866 |
float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b) |
867 |
{ |
868 |
return float32_mul(a, b, &env->fp_status);
|
869 |
} |
870 |
|
871 |
float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) |
872 |
{ |
873 |
return float32_muladd(b, c, a, 0, |
874 |
&env->fp_status); |
875 |
} |
876 |
|
877 |
float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) |
878 |
{ |
879 |
return float32_muladd(b, c, a, float_muladd_negate_product,
|
880 |
&env->fp_status); |
881 |
} |
882 |
|
883 |
uint32_t HELPER(ftoi)(float32 v, uint32_t rounding_mode, uint32_t scale) |
884 |
{ |
885 |
float_status fp_status = {0};
|
886 |
|
887 |
set_float_rounding_mode(rounding_mode, &fp_status); |
888 |
return float32_to_int32(
|
889 |
float32_scalbn(v, scale, &fp_status), &fp_status); |
890 |
} |
891 |
|
892 |
uint32_t HELPER(ftoui)(float32 v, uint32_t rounding_mode, uint32_t scale) |
893 |
{ |
894 |
float_status fp_status = {0};
|
895 |
float32 res; |
896 |
|
897 |
set_float_rounding_mode(rounding_mode, &fp_status); |
898 |
|
899 |
res = float32_scalbn(v, scale, &fp_status); |
900 |
|
901 |
if (float32_is_neg(v) && !float32_is_any_nan(v)) {
|
902 |
return float32_to_int32(res, &fp_status);
|
903 |
} else {
|
904 |
return float32_to_uint32(res, &fp_status);
|
905 |
} |
906 |
} |
907 |
|
908 |
float32 HELPER(itof)(CPUXtensaState *env, uint32_t v, uint32_t scale) |
909 |
{ |
910 |
return float32_scalbn(int32_to_float32(v, &env->fp_status),
|
911 |
(int32_t)scale, &env->fp_status); |
912 |
} |
913 |
|
914 |
float32 HELPER(uitof)(CPUXtensaState *env, uint32_t v, uint32_t scale) |
915 |
{ |
916 |
return float32_scalbn(uint32_to_float32(v, &env->fp_status),
|
917 |
(int32_t)scale, &env->fp_status); |
918 |
} |
919 |
|
920 |
static inline void set_br(CPUXtensaState *env, bool v, uint32_t br) |
921 |
{ |
922 |
if (v) {
|
923 |
env->sregs[BR] |= br; |
924 |
} else {
|
925 |
env->sregs[BR] &= ~br; |
926 |
} |
927 |
} |
928 |
|
929 |
void HELPER(un_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b)
|
930 |
{ |
931 |
set_br(env, float32_unordered_quiet(a, b, &env->fp_status), br); |
932 |
} |
933 |
|
934 |
void HELPER(oeq_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b)
|
935 |
{ |
936 |
set_br(env, float32_eq_quiet(a, b, &env->fp_status), br); |
937 |
} |
938 |
|
939 |
void HELPER(ueq_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b)
|
940 |
{ |
941 |
int v = float32_compare_quiet(a, b, &env->fp_status);
|
942 |
set_br(env, v == float_relation_equal || v == float_relation_unordered, br); |
943 |
} |
944 |
|
945 |
void HELPER(olt_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b)
|
946 |
{ |
947 |
set_br(env, float32_lt_quiet(a, b, &env->fp_status), br); |
948 |
} |
949 |
|
950 |
void HELPER(ult_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b)
|
951 |
{ |
952 |
int v = float32_compare_quiet(a, b, &env->fp_status);
|
953 |
set_br(env, v == float_relation_less || v == float_relation_unordered, br); |
954 |
} |
955 |
|
956 |
void HELPER(ole_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b)
|
957 |
{ |
958 |
set_br(env, float32_le_quiet(a, b, &env->fp_status), br); |
959 |
} |
960 |
|
961 |
void HELPER(ule_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b)
|
962 |
{ |
963 |
int v = float32_compare_quiet(a, b, &env->fp_status);
|
964 |
set_br(env, v != float_relation_greater, br); |
965 |
} |