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