1 |
/* |
2 |
* Copyright (C) 2004-2005 Anders Gavare. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. 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 |
* 3. The name of the author may not be used to endorse or promote products |
13 |
* derived from this software without specific prior written permission. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
* |
27 |
* |
28 |
* $Id: bintrans.c,v 1.165 2005/04/27 16:37:34 debug Exp $ |
29 |
* |
30 |
* Dynamic binary translation. |
31 |
* |
32 |
* |
33 |
* -------------------------------------------------------------------------- |
34 |
* |
35 |
* NOTE: This code needs a lot of updating; much of it is MIPS-specific. |
36 |
* |
37 |
* -------------------------------------------------------------------------- |
38 |
* |
39 |
* |
40 |
* This should be documented/commented better. Some of the main concepts are: |
41 |
* |
42 |
* o) Keep a translation cache of a certain number of blocks. |
43 |
* |
44 |
* o) Only translate simple instructions. (For example, the 'tlbwr' |
45 |
* instruction is not actually translated, converted into a call |
46 |
* to C code.) |
47 |
* |
48 |
* o) Translate code in physical ram, not virtual. This will keep |
49 |
* things translated over process switches, and TLB updates. |
50 |
* |
51 |
* o) When the translation cache is "full", then throw away everything |
52 |
* translated so far and restart from scratch. The cache is of a |
53 |
* fixed size, say 24 MB. (This is inspired by a comment in the Qemu |
54 |
* technical documentation: "A 16 MByte cache holds the most recently |
55 |
* used translations. For simplicity, it is completely flushed when |
56 |
* it is full.") |
57 |
* |
58 |
* o) Do not translate over MIPS page boundaries (4 KB). |
59 |
* (TODO: Perhaps it would be possible if we're running in kernel |
60 |
* space? But that would require special checks at the end of |
61 |
* each page.) |
62 |
* |
63 |
* o) If memory is overwritten, any translated block for that page |
64 |
* must be invalidated. (It is removed from the cache so that it |
65 |
* cannot be found on lookups.) |
66 |
* |
67 |
* o) Only run a certain number of instructions, before returning to |
68 |
* the main loop. (This is needed in order to allow devices to |
69 |
* cause interrupts, and so on.) |
70 |
* |
71 |
* o) Check for exceptions inside the block, for those instructions |
72 |
* that require that. Update the program counter by the number |
73 |
* of successfully executed instructions only. |
74 |
* |
75 |
* o) There is no "intermediate representation"; everything is translated |
76 |
* directly from MIPS machine code to target machine code. |
77 |
* |
78 |
* o) Theoretical support for multiple target architectures (Alpha, |
79 |
* i386, sparc, mips :-), ...), but only Alpha and i386 implemented |
80 |
* so far. |
81 |
* |
82 |
* o) Load/stores: TODO: Comment. |
83 |
* |
84 |
* Testing: Running regression tests with and without the binary translator |
85 |
* enabled should obviously result in the exact same results, or something is |
86 |
* wrong. |
87 |
* |
88 |
* The general idea is something like this: |
89 |
* |
90 |
* Check for the current PC (actually: its physical form) in the |
91 |
* translation cache. If it is found, then run the translated code chunk, |
92 |
* otherwise try to translate and then run it. |
93 |
* |
94 |
* A few checks are made though, to make sure that the environment is "safe" |
95 |
* enough; starting inside a delay slot or "nullified" slot is considered |
96 |
* non-safe. |
97 |
*/ |
98 |
|
99 |
#include <errno.h> |
100 |
#include <stdio.h> |
101 |
#include <stdlib.h> |
102 |
#include <string.h> |
103 |
#include <sys/types.h> |
104 |
#include <sys/mman.h> |
105 |
|
106 |
#include "bintrans.h" |
107 |
#include "cop0.h" |
108 |
#include "cpu.h" |
109 |
#include "cpu_mips.h" |
110 |
#include "machine.h" |
111 |
#include "memory.h" |
112 |
#include "mips_cpu_types.h" |
113 |
#include "misc.h" |
114 |
#include "opcodes_mips.h" |
115 |
|
116 |
|
117 |
#ifndef BINTRANS |
118 |
|
119 |
/* |
120 |
* No bintrans, then let's supply dummy functions: |
121 |
*/ |
122 |
|
123 |
int bintrans_pc_is_in_cache(struct cpu *cpu, uint64_t pc) { return 0; } |
124 |
void bintrans_invalidate(struct cpu *cpu, uint64_t paddr) { } |
125 |
int bintrans_attempt_translate(struct cpu *cpu, uint64_t paddr) { return 0; } |
126 |
void bintrans_restart(struct cpu *cpu) { } |
127 |
void bintrans_init_cpu(struct cpu *cpu) { } |
128 |
void bintrans_init(struct machine *machine, struct memory *mem) |
129 |
{ |
130 |
fatal("\n*** NOT starting bintrans, as gxemul " |
131 |
"was compiled without such support!\n\n"); |
132 |
} |
133 |
|
134 |
#else |
135 |
|
136 |
|
137 |
/* Function declaration, should be the same as in bintrans_*.c: */ |
138 |
|
139 |
static void bintrans_host_cacheinvalidate(unsigned char *p, size_t len); |
140 |
static void bintrans_write_chunkreturn(unsigned char **addrp); |
141 |
static void bintrans_write_chunkreturn_fail(unsigned char **addrp); |
142 |
static void bintrans_write_pc_inc(unsigned char **addrp); |
143 |
static void bintrans_write_quickjump(struct memory *mem, |
144 |
unsigned char *quickjump_code, uint32_t chunkoffset); |
145 |
static int bintrans_write_instruction__addiu_etc(unsigned char **addrp, int rt, |
146 |
int rs, int imm, int instruction_type); |
147 |
static int bintrans_write_instruction__addu_etc(unsigned char **addrp, int rd, |
148 |
int rs, int rt, int sa, int instruction_type); |
149 |
static int bintrans_write_instruction__branch(unsigned char **addrp, |
150 |
int instruction_type, int regimm_type, int rt, int rs, int imm); |
151 |
static int bintrans_write_instruction__jr(unsigned char **addrp, int rs, |
152 |
int rd, int special); |
153 |
static int bintrans_write_instruction__jal(unsigned char **addrp, int imm, |
154 |
int link); |
155 |
static int bintrans_write_instruction__delayedbranch(struct memory *mem, |
156 |
unsigned char **addrp, uint32_t *potential_chunk_p, uint32_t *chunks, |
157 |
int only_care_about_chunk_p, int p, int forward); |
158 |
static int bintrans_write_instruction__loadstore(struct memory *mem, |
159 |
unsigned char **addrp, int rt, int imm, int rs, int instruction_type, |
160 |
int bigendian); |
161 |
static int bintrans_write_instruction__lui(unsigned char **addrp, int rt, |
162 |
int imm); |
163 |
static int bintrans_write_instruction__mfmthilo(unsigned char **addrp, int rd, |
164 |
int from_flag, int hi_flag); |
165 |
static int bintrans_write_instruction__mfc_mtc(struct memory *mem, |
166 |
unsigned char **addrp, int coproc_nr, int flag64bit, int rt, int rd, |
167 |
int mtcflag); |
168 |
static int bintrans_write_instruction__tlb_rfe_etc(unsigned char **addrp, |
169 |
int itype); |
170 |
|
171 |
#define CALL_TLBWI 0 |
172 |
#define CALL_TLBWR 1 |
173 |
#define CALL_TLBP 2 |
174 |
#define CALL_TLBR 3 |
175 |
#define CALL_RFE 4 |
176 |
#define CALL_ERET 5 |
177 |
#define CALL_BREAK 6 |
178 |
#define CALL_SYSCALL 7 |
179 |
|
180 |
|
181 |
static void bintrans_register_potential_quick_jump(struct memory *mem, |
182 |
unsigned char *a, int p) |
183 |
{ |
184 |
/* printf("%02i: a=%016llx p=%i\n", mem->quick_jumps_index, a, p); */ |
185 |
mem->quick_jump_host_address[mem->quick_jumps_index] = a; |
186 |
mem->quick_jump_page_offset[mem->quick_jumps_index] = p; |
187 |
mem->quick_jumps_index ++; |
188 |
if (mem->quick_jumps_index > mem->n_quick_jumps) |
189 |
mem->n_quick_jumps = mem->quick_jumps_index; |
190 |
if (mem->quick_jumps_index >= MAX_QUICK_JUMPS) |
191 |
mem->quick_jumps_index = 0; |
192 |
} |
193 |
|
194 |
|
195 |
/* Include host architecture specific bintrans code: */ |
196 |
|
197 |
#ifdef ALPHA |
198 |
#define BACKEND_NAME "Alpha" |
199 |
#include "bintrans_alpha.c" |
200 |
#else |
201 |
#ifdef I386 |
202 |
#define BACKEND_NAME "i386" |
203 |
#include "bintrans_i386.c" |
204 |
#else |
205 |
#ifdef MIPS |
206 |
#define BACKEND_NAME "MIPS" |
207 |
#include "bintrans_mips.c" |
208 |
#else |
209 |
#ifdef SPARCV9 |
210 |
#define BACKEND_NAME "UltraSPARC" |
211 |
#include "bintrans_sparcv9.c" |
212 |
#else |
213 |
#error Unsupported host architecture for bintrans. |
214 |
#endif /* SPARCV9 */ |
215 |
#endif /* MIPS */ |
216 |
#endif /* I386 */ |
217 |
#endif /* ALPHA */ |
218 |
|
219 |
|
220 |
/* |
221 |
* old_bintrans_invalidate(): |
222 |
* |
223 |
* Invalidate translations containing a certain physical address. |
224 |
*/ |
225 |
static void old_bintrans_invalidate(struct cpu *cpu, uint64_t paddr) |
226 |
{ |
227 |
int entry_index = PADDR_TO_INDEX(paddr); |
228 |
struct translation_page_entry *tep; |
229 |
#if 0 |
230 |
struct translation_page_entry *prev = NULL; |
231 |
#endif |
232 |
uint64_t paddr_page = paddr & ~0xfff; |
233 |
|
234 |
tep = cpu->mem->translation_page_entry_array[entry_index]; |
235 |
while (tep != NULL) { |
236 |
if (tep->paddr == paddr_page) |
237 |
#if 1 |
238 |
break; |
239 |
#else |
240 |
{ |
241 |
if (prev == NULL) |
242 |
cpu->mem->translation_page_entry_array[ |
243 |
entry_index] = tep->next; |
244 |
else |
245 |
prev->next = tep->next; |
246 |
return; |
247 |
} |
248 |
prev = tep; |
249 |
#endif |
250 |
tep = tep->next; |
251 |
} |
252 |
if (tep == NULL) |
253 |
return; |
254 |
|
255 |
if (!tep->page_is_potentially_in_use) |
256 |
return; |
257 |
|
258 |
tep->page_is_potentially_in_use = 0; |
259 |
memset(&tep->chunk[0], 0, sizeof(tep->chunk)); |
260 |
memset(&tep->flags[0], 0, sizeof(tep->flags)); |
261 |
} |
262 |
|
263 |
|
264 |
/* |
265 |
* bintrans_invalidate(): |
266 |
* |
267 |
* Invalidate translations containing a certain physical address. |
268 |
*/ |
269 |
void bintrans_invalidate(struct cpu *cpu, uint64_t paddr) |
270 |
{ |
271 |
if (cpu->machine->old_bintrans_enable) { |
272 |
old_bintrans_invalidate(cpu, paddr); |
273 |
return; |
274 |
} |
275 |
|
276 |
/* TODO */ |
277 |
/* printf("bintrans_invalidate(): TODO\n"); */ |
278 |
} |
279 |
|
280 |
|
281 |
/* |
282 |
* bintrans_restart(): |
283 |
* |
284 |
* Starts over by throwing away the bintrans cache contents. |
285 |
*/ |
286 |
void bintrans_restart(struct cpu *cpu) |
287 |
{ |
288 |
int i, n = 1 << BINTRANS_CACHE_N_INDEX_BITS; |
289 |
|
290 |
for (i=0; i<n; i++) |
291 |
cpu->mem->translation_page_entry_array[i] = NULL; |
292 |
|
293 |
cpu->mem->translation_code_chunk_space_head = 0; |
294 |
cpu->mem->n_quick_jumps = 0; |
295 |
|
296 |
/* debug("bintrans: Starting over!\n"); */ |
297 |
clear_all_chunks_from_all_tables(cpu); |
298 |
} |
299 |
|
300 |
|
301 |
/* |
302 |
* enter_chunks_into_tables(): |
303 |
*/ |
304 |
static void enter_chunks_into_tables(struct cpu *cpu, uint64_t vaddr, |
305 |
uint32_t *chunk0) |
306 |
{ |
307 |
int a, b; |
308 |
struct vth32_table *tbl1; |
309 |
|
310 |
switch (cpu->cd.mips.cpu_type.mmu_model) { |
311 |
case MMU3K: |
312 |
a = (vaddr >> 22) & 0x3ff; |
313 |
b = (vaddr >> 12) & 0x3ff; |
314 |
tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; |
315 |
if (tbl1->haddr_entry[b] != NULL) |
316 |
tbl1->bintrans_chunks[b] = chunk0; |
317 |
break; |
318 |
default: |
319 |
; |
320 |
} |
321 |
} |
322 |
|
323 |
|
324 |
/* |
325 |
* old_bintrans_attempt_translate(): |
326 |
* |
327 |
* Attempt to translate a chunk of code, starting at 'paddr'. If successful, |
328 |
* the code chunk is run. |
329 |
* |
330 |
* Returns the number of executed instructions. |
331 |
*/ |
332 |
int old_bintrans_attempt_translate(struct cpu *cpu, uint64_t paddr) |
333 |
{ |
334 |
uint64_t paddr_page; |
335 |
int offset_within_page; |
336 |
int entry_index; |
337 |
unsigned char *host_mips_page; |
338 |
unsigned char *ca, *ca_justdid, *ca2; |
339 |
int res, hi6, special6, regimm5; |
340 |
unsigned char instr[4]; |
341 |
int old_n_executed; |
342 |
size_t p; |
343 |
int try_to_translate; |
344 |
int n_translated, translated; |
345 |
unsigned char *f; |
346 |
struct translation_page_entry *tep; |
347 |
size_t chunk_len; |
348 |
int rs,rt,rd,sa,imm; |
349 |
uint32_t *potential_chunk_p; /* for branches */ |
350 |
int byte_order_cached_bigendian; |
351 |
int delayed_branch, stop_after_delayed_branch; |
352 |
uint64_t delayed_branch_new_p; |
353 |
int prev_p; |
354 |
|
355 |
|
356 |
/* Abort if the current "environment" isn't safe enough: */ |
357 |
if (cpu->cd.mips.delay_slot || cpu->cd.mips.nullify_next || |
358 |
(paddr & 3) != 0) |
359 |
return cpu->cd.mips.bintrans_instructions_executed; |
360 |
|
361 |
byte_order_cached_bigendian = (cpu->byte_order == EMUL_BIG_ENDIAN); |
362 |
|
363 |
/* Is this a part of something that is already translated? */ |
364 |
paddr_page = paddr & ~0xfff; |
365 |
offset_within_page = (paddr & 0xfff) >> 2; |
366 |
entry_index = PADDR_TO_INDEX(paddr); |
367 |
tep = cpu->mem->translation_page_entry_array[entry_index]; |
368 |
while (tep != NULL) { |
369 |
if (tep->paddr == paddr_page) { |
370 |
int mask; |
371 |
|
372 |
if (tep->chunk[offset_within_page] != 0) { |
373 |
f = (size_t)tep->chunk[offset_within_page] + |
374 |
cpu->mem->translation_code_chunk_space; |
375 |
goto run_it; /* see further down */ |
376 |
} |
377 |
|
378 |
mask = 1 << (offset_within_page & 7); |
379 |
if (tep->flags[offset_within_page >> 3] & mask) |
380 |
return cpu->cd.mips. |
381 |
bintrans_instructions_executed; |
382 |
break; |
383 |
} |
384 |
tep = tep->next; |
385 |
} |
386 |
|
387 |
#if 1 |
388 |
/* printf("A paddr=%016llx\n", (long long)paddr); */ |
389 |
/* Sometimes this works. */ |
390 |
quick_attempt_translate_again: |
391 |
#endif |
392 |
/*printf("B: "); |
393 |
printf("v=%016llx p=%016llx h=%p paddr=%016llx\n", |
394 |
(long long)cpu->cd.mips.pc_last_virtual_page, |
395 |
(long long)cpu->cd.mips.pc_last_physical_page, |
396 |
cpu->cd.mips.pc_last_host_4k_page,(long long)paddr); |
397 |
*/ |
398 |
/* |
399 |
* If the chunk space is all used up, we need to start over from |
400 |
* an empty chunk space. |
401 |
*/ |
402 |
if (cpu->mem->translation_code_chunk_space_head >= |
403 |
cpu->machine->bintrans_size) { |
404 |
bintrans_restart(cpu); |
405 |
tep = NULL; |
406 |
} |
407 |
|
408 |
|
409 |
host_mips_page = cpu->cd.mips.pc_bintrans_host_4kpage; |
410 |
if (host_mips_page == NULL) |
411 |
return cpu->cd.mips.bintrans_instructions_executed; |
412 |
|
413 |
|
414 |
if (tep == NULL) { |
415 |
/* Allocate a new translation page entry: */ |
416 |
tep = (void *)(size_t) (cpu->mem->translation_code_chunk_space |
417 |
+ cpu->mem->translation_code_chunk_space_head); |
418 |
cpu->mem->translation_code_chunk_space_head += |
419 |
sizeof(struct translation_page_entry); |
420 |
|
421 |
/* ... and align again: */ |
422 |
cpu->mem->translation_code_chunk_space_head = |
423 |
((cpu->mem->translation_code_chunk_space_head - 1) | 63) |
424 |
+ 1; |
425 |
|
426 |
/* Add the entry to the array: */ |
427 |
memset(tep, 0, sizeof(struct translation_page_entry)); |
428 |
tep->next = cpu->mem->translation_page_entry_array[entry_index]; |
429 |
cpu->mem->translation_page_entry_array[entry_index] = tep; |
430 |
tep->paddr = paddr_page; |
431 |
} |
432 |
|
433 |
/* printf("translation_page_entry_array[%i] = %p, ofs = %i\n", |
434 |
entry_index, cpu->mem->translation_page_entry_array[entry_index], |
435 |
offset_within_page); */ |
436 |
|
437 |
/* ca is the "chunk address"; where to start generating a chunk: */ |
438 |
ca = cpu->mem->translation_code_chunk_space |
439 |
+ cpu->mem->translation_code_chunk_space_head; |
440 |
|
441 |
|
442 |
/* |
443 |
* Make sure that this page will not be written to by translated |
444 |
* code: |
445 |
*/ |
446 |
mips_invalidate_translation_caches_paddr(cpu, paddr); |
447 |
|
448 |
/* |
449 |
* Try to translate a chunk of code: |
450 |
*/ |
451 |
p = paddr & 0xfff; |
452 |
prev_p = p >> 2; |
453 |
try_to_translate = 1; |
454 |
n_translated = 0; |
455 |
res = 0; |
456 |
delayed_branch = 0; |
457 |
stop_after_delayed_branch = 0; |
458 |
delayed_branch_new_p = 0; |
459 |
rt = 0; |
460 |
cpu->mem->n_quick_jumps = cpu->mem->quick_jumps_index = 0; |
461 |
|
462 |
while (try_to_translate) { |
463 |
ca_justdid = ca; |
464 |
translated = 0; |
465 |
|
466 |
/* Read an instruction word from host memory: */ |
467 |
*((uint32_t *)&instr[0]) = *((uint32_t *)(host_mips_page + p)); |
468 |
|
469 |
if (byte_order_cached_bigendian) { |
470 |
int tmp; |
471 |
tmp = instr[0]; instr[0] = instr[3]; instr[3] = tmp; |
472 |
tmp = instr[1]; instr[1] = instr[2]; instr[2] = tmp; |
473 |
} |
474 |
|
475 |
hi6 = instr[3] >> 2; |
476 |
|
477 |
/* Check for instructions that can be translated: */ |
478 |
switch (hi6) { |
479 |
|
480 |
case HI6_SPECIAL: |
481 |
special6 = instr[0] & 0x3f; |
482 |
rs = ((instr[3] & 3) << 3) + ((instr[2] >> 5) & 7); |
483 |
rd = (instr[1] >> 3) & 31; |
484 |
rt = instr[2] & 31; |
485 |
sa = ((instr[1] & 7) << 2) + ((instr[0] >> 6) & 3); |
486 |
switch (special6) { |
487 |
case SPECIAL_JR: |
488 |
case SPECIAL_JALR: |
489 |
translated = try_to_translate = |
490 |
bintrans_write_instruction__jr(&ca, rs, rd, |
491 |
special6); |
492 |
n_translated += translated; |
493 |
delayed_branch = 2; |
494 |
delayed_branch_new_p = -1; /* anything, |
495 |
not within this physical page */ |
496 |
if (special6 == SPECIAL_JR) |
497 |
stop_after_delayed_branch = 1; |
498 |
break; |
499 |
case SPECIAL_SYSCALL: |
500 |
case SPECIAL_BREAK: |
501 |
if (cpu->machine->userland_emul != NULL) { |
502 |
int mask = 1 << (prev_p & 7); |
503 |
bintrans_write_chunkreturn_fail(&ca); |
504 |
tep->flags[prev_p >> 3] |= mask; |
505 |
try_to_translate = 0; |
506 |
} else { |
507 |
translated = bintrans_write_instruction__tlb_rfe_etc(&ca, |
508 |
special6 == SPECIAL_BREAK? CALL_BREAK : CALL_SYSCALL); |
509 |
n_translated += translated; |
510 |
try_to_translate = 0; |
511 |
} |
512 |
break; |
513 |
case SPECIAL_ADDU: |
514 |
case SPECIAL_DADDU: |
515 |
case SPECIAL_SUBU: |
516 |
case SPECIAL_DSUBU: |
517 |
case SPECIAL_AND: |
518 |
case SPECIAL_OR: |
519 |
case SPECIAL_NOR: |
520 |
case SPECIAL_XOR: |
521 |
case SPECIAL_SLL: |
522 |
case SPECIAL_SLLV: |
523 |
case SPECIAL_DSLL: |
524 |
case SPECIAL_DSLL32: |
525 |
case SPECIAL_SRA: |
526 |
case SPECIAL_SRAV: |
527 |
case SPECIAL_SRLV: |
528 |
case SPECIAL_SRL: |
529 |
case SPECIAL_DSRA: |
530 |
case SPECIAL_DSRA32: |
531 |
case SPECIAL_DSRL: |
532 |
case SPECIAL_DSRL32: |
533 |
case SPECIAL_SLT: |
534 |
case SPECIAL_SLTU: |
535 |
case SPECIAL_MOVZ: |
536 |
case SPECIAL_MOVN: |
537 |
case SPECIAL_DIV: |
538 |
case SPECIAL_DIVU: |
539 |
case SPECIAL_MULT: |
540 |
case SPECIAL_MULTU: |
541 |
case SPECIAL_SYNC: |
542 |
/* treat SYNC as a nop :-) */ |
543 |
if (special6 == SPECIAL_SYNC) { |
544 |
rd = rt = rs = sa = 0; |
545 |
special6 = SPECIAL_SLL; |
546 |
} |
547 |
translated = try_to_translate = bintrans_write_instruction__addu_etc(&ca, rd, rs, rt, sa, special6); |
548 |
n_translated += translated; |
549 |
break; |
550 |
case SPECIAL_MFHI: |
551 |
case SPECIAL_MFLO: |
552 |
case SPECIAL_MTHI: |
553 |
case SPECIAL_MTLO: |
554 |
translated = try_to_translate = bintrans_write_instruction__mfmthilo(&ca, |
555 |
(special6 == SPECIAL_MFHI || special6 == SPECIAL_MFLO)? rd : rs, |
556 |
special6 == SPECIAL_MFHI || special6 == SPECIAL_MFLO, |
557 |
special6 == SPECIAL_MFHI || special6 == SPECIAL_MTHI); |
558 |
n_translated += translated; |
559 |
break; |
560 |
default: |
561 |
/* Untranslatable: */ |
562 |
/* TODO: this code should only be in one place */ |
563 |
bintrans_write_chunkreturn_fail(&ca); |
564 |
{ |
565 |
int mask = 1 << (prev_p & 7); |
566 |
tep->flags[prev_p >> 3] |= mask; |
567 |
} |
568 |
try_to_translate = 0; |
569 |
} |
570 |
break; |
571 |
|
572 |
case HI6_REGIMM: |
573 |
regimm5 = instr[2] & 0x1f; |
574 |
switch (regimm5) { |
575 |
case REGIMM_BLTZ: |
576 |
case REGIMM_BGEZ: |
577 |
rs = ((instr[3] & 3) << 3) + ((instr[2] >> 5) & 7); |
578 |
imm = (instr[1] << 8) + instr[0]; |
579 |
if (imm >= 32768) |
580 |
imm -= 65536; |
581 |
translated = try_to_translate = bintrans_write_instruction__branch(&ca, hi6, regimm5, rt, rs, imm); |
582 |
n_translated += translated; |
583 |
delayed_branch = 2; |
584 |
delayed_branch_new_p = p + 4 + 4*imm; |
585 |
break; |
586 |
default: |
587 |
try_to_translate = 0; |
588 |
/* Untranslatable: */ |
589 |
/* TODO: this code should only be in one place */ |
590 |
bintrans_write_chunkreturn_fail(&ca); |
591 |
{ |
592 |
int mask = 1 << (prev_p & 7); |
593 |
tep->flags[prev_p >> 3] |= mask; |
594 |
} |
595 |
try_to_translate = 0; |
596 |
} |
597 |
break; |
598 |
|
599 |
case HI6_J: |
600 |
case HI6_JAL: |
601 |
imm = (((instr[3] & 3) << 24) + (instr[2] << 16) + |
602 |
(instr[1] << 8) + instr[0]) & 0x03ffffff; |
603 |
translated = try_to_translate = bintrans_write_instruction__jal(&ca, imm, hi6 == HI6_JAL); |
604 |
n_translated += translated; |
605 |
delayed_branch = 2; |
606 |
delayed_branch_new_p = -1; |
607 |
if (hi6 == HI6_J) |
608 |
stop_after_delayed_branch = 1; |
609 |
break; |
610 |
|
611 |
case HI6_BEQ: |
612 |
case HI6_BEQL: |
613 |
case HI6_BNE: |
614 |
case HI6_BNEL: |
615 |
case HI6_BLEZ: |
616 |
case HI6_BLEZL: |
617 |
case HI6_BGTZ: |
618 |
case HI6_BGTZL: |
619 |
rs = ((instr[3] & 3) << 3) + ((instr[2] >> 5) & 7); |
620 |
rt = instr[2] & 31; |
621 |
imm = (instr[1] << 8) + instr[0]; |
622 |
if (imm >= 32768) |
623 |
imm -= 65536; |
624 |
translated = try_to_translate = bintrans_write_instruction__branch(&ca, hi6, 0, rt, rs, imm); |
625 |
n_translated += translated; |
626 |
delayed_branch = 2; |
627 |
delayed_branch_new_p = p + 4 + 4*imm; |
628 |
break; |
629 |
|
630 |
case HI6_ADDI: |
631 |
case HI6_ADDIU: |
632 |
case HI6_SLTI: |
633 |
case HI6_SLTIU: |
634 |
case HI6_ANDI: |
635 |
case HI6_ORI: |
636 |
case HI6_XORI: |
637 |
case HI6_DADDI: |
638 |
case HI6_DADDIU: |
639 |
translated = try_to_translate = |
640 |
bintrans_write_instruction__addiu_etc(&ca, |
641 |
instr[2] & 31, |
642 |
((instr[3] & 3) << 3) + ((instr[2] >> 5) & 7), |
643 |
(instr[1] << 8) + instr[0], hi6); |
644 |
n_translated += translated; |
645 |
break; |
646 |
|
647 |
case HI6_LUI: |
648 |
translated = try_to_translate = |
649 |
bintrans_write_instruction__lui(&ca, |
650 |
instr[2] & 31, (instr[1] << 8) + instr[0]); |
651 |
n_translated += translated; |
652 |
break; |
653 |
|
654 |
case HI6_COP0: |
655 |
switch (instr[3]) { |
656 |
case 0x40: |
657 |
if (instr[0] == 0 && (instr[1]&7)==0) { |
658 |
if ((instr[2] & 0xe0)==0) { |
659 |
/* mfc0: */ |
660 |
rt = instr[2] & 31; |
661 |
rd = (instr[1] >> 3) & 31; |
662 |
translated = try_to_translate = bintrans_write_instruction__mfc_mtc(cpu->mem, &ca, 0, 0, rt, rd, 0); |
663 |
n_translated += translated; |
664 |
} else if ((instr[2] & 0xe0)==0x20) { |
665 |
/* dmfc0: */ |
666 |
rt = instr[2] & 31; |
667 |
rd = (instr[1] >> 3) & 31; |
668 |
translated = try_to_translate = bintrans_write_instruction__mfc_mtc(cpu->mem, &ca, 0, 1, rt, rd, 0); |
669 |
n_translated += translated; |
670 |
} else if ((instr[2] & 0xe0)==0x80) { |
671 |
/* mtc0: */ |
672 |
rt = instr[2] & 31; |
673 |
rd = (instr[1] >> 3) & 31; |
674 |
translated = try_to_translate = bintrans_write_instruction__mfc_mtc(cpu->mem, &ca, 0, 0, rt, rd, 1); |
675 |
n_translated += translated; |
676 |
} else if ((instr[2] & 0xe0)==0xa0) { |
677 |
/* dmtc0: */ |
678 |
rt = instr[2] & 31; |
679 |
rd = (instr[1] >> 3) & 31; |
680 |
translated = try_to_translate = bintrans_write_instruction__mfc_mtc(cpu->mem, &ca, 0, 1, rt, rd, 1); |
681 |
n_translated += translated; |
682 |
} |
683 |
} else |
684 |
try_to_translate = 0; |
685 |
break; |
686 |
case 0x42: |
687 |
if (instr[2] == 0x00 && instr[1] == 0x00 && instr[0] == 0x10) { |
688 |
/* rfe: */ |
689 |
translated = bintrans_write_instruction__tlb_rfe_etc(&ca, CALL_RFE); |
690 |
n_translated += translated; |
691 |
try_to_translate = 0; |
692 |
} else if (instr[2] == 0x00 && instr[1] == 0x00 && instr[0] == 0x18) { |
693 |
/* eret: */ |
694 |
translated = bintrans_write_instruction__tlb_rfe_etc(&ca, CALL_ERET); |
695 |
n_translated += translated; |
696 |
try_to_translate = 0; |
697 |
} else if (instr[2] == 0 && instr[1] == 0 && instr[0] == 2) { |
698 |
/* tlbwi: */ |
699 |
translated = try_to_translate = bintrans_write_instruction__tlb_rfe_etc(&ca, CALL_TLBWI); |
700 |
n_translated += translated; |
701 |
} else if (instr[2] == 0 && instr[1] == 0 && instr[0] == 6) { |
702 |
/* tlbwr: */ |
703 |
translated = try_to_translate = bintrans_write_instruction__tlb_rfe_etc(&ca, CALL_TLBWR); |
704 |
n_translated += translated; |
705 |
} else if (instr[2] == 0 && instr[1] == 0 && instr[0] == 8) { |
706 |
/* tlbp: */ |
707 |
translated = try_to_translate = bintrans_write_instruction__tlb_rfe_etc(&ca, CALL_TLBP); |
708 |
n_translated += translated; |
709 |
} else if (instr[2] == 0 && instr[1] == 0 && instr[0] == 1) { |
710 |
/* tlbr: */ |
711 |
translated = try_to_translate = bintrans_write_instruction__tlb_rfe_etc(&ca, CALL_TLBR); |
712 |
n_translated += translated; |
713 |
} else if (instr[2] == 0 && instr[1] == 0 && (instr[0] == 0x21 || instr[0] == 0x22)) { |
714 |
/* standby and suspend on VR41xx etc ==> NOP */ |
715 |
translated = try_to_translate = bintrans_write_instruction__addu_etc(&ca, 0, 0, 0, 0, SPECIAL_SLL); |
716 |
n_translated += translated; |
717 |
} else |
718 |
try_to_translate = 0; |
719 |
break; |
720 |
default: |
721 |
try_to_translate = 0; |
722 |
} |
723 |
break; |
724 |
|
725 |
#if 0 |
726 |
case HI6_LQ_MDMX: |
727 |
#endif |
728 |
case HI6_LD: |
729 |
case HI6_LWU: |
730 |
case HI6_LW: |
731 |
case HI6_LWL: |
732 |
case HI6_LWR: |
733 |
case HI6_LHU: |
734 |
case HI6_LH: |
735 |
case HI6_LBU: |
736 |
case HI6_LB: |
737 |
#if 0 |
738 |
case HI6_SQ: |
739 |
#endif |
740 |
case HI6_SD: |
741 |
case HI6_SW: |
742 |
case HI6_SWL: |
743 |
case HI6_SWR: |
744 |
case HI6_SH: |
745 |
case HI6_SB: |
746 |
rs = ((instr[3] & 3) << 3) + ((instr[2] >> 5) & 7); |
747 |
rt = instr[2] & 31; |
748 |
imm = (instr[1] << 8) + instr[0]; |
749 |
if (imm >= 32768) |
750 |
imm -= 65536; |
751 |
translated = try_to_translate = bintrans_write_instruction__loadstore(cpu->mem, &ca, rt, imm, rs, hi6, byte_order_cached_bigendian); |
752 |
n_translated += translated; |
753 |
break; |
754 |
|
755 |
case HI6_CACHE: |
756 |
translated = try_to_translate = bintrans_write_instruction__addu_etc(&ca, 0, 0, 0, 0, SPECIAL_SLL); |
757 |
n_translated += translated; |
758 |
break; |
759 |
|
760 |
default: |
761 |
/* Untranslatable: */ |
762 |
/* TODO: this code should only be in one place */ |
763 |
bintrans_write_chunkreturn_fail(&ca); |
764 |
{ |
765 |
int mask = 1 << (prev_p & 7); |
766 |
tep->flags[prev_p >> 3] |= mask; |
767 |
} |
768 |
try_to_translate = 0; |
769 |
} |
770 |
|
771 |
if (translated && delayed_branch) { |
772 |
delayed_branch --; |
773 |
if (delayed_branch == 0) { |
774 |
int forward; |
775 |
|
776 |
/* |
777 |
* p is 0x000 .. 0xffc. If the jump is to |
778 |
* within the same page, then we can use |
779 |
* the same translation page to check if |
780 |
* there already is a translation. |
781 |
*/ |
782 |
if ((delayed_branch_new_p & ~0xfff) == 0) |
783 |
potential_chunk_p = |
784 |
&tep->chunk[delayed_branch_new_p/4]; |
785 |
else |
786 |
potential_chunk_p = NULL; |
787 |
|
788 |
forward = delayed_branch_new_p > p; |
789 |
|
790 |
bintrans_write_instruction__delayedbranch( |
791 |
cpu->mem, &ca, |
792 |
potential_chunk_p, &tep->chunk[0], 0, |
793 |
delayed_branch_new_p & 0xfff, forward); |
794 |
#if 0 |
795 |
if (stop_after_delayed_branch) |
796 |
try_to_translate = 0; |
797 |
#endif |
798 |
} |
799 |
} |
800 |
|
801 |
if (translated) { |
802 |
int i; |
803 |
|
804 |
if (tep->chunk[prev_p] == 0) |
805 |
tep->chunk[prev_p] = (uint32_t) |
806 |
((size_t)ca_justdid - |
807 |
(size_t)cpu->mem->translation_code_chunk_space); |
808 |
|
809 |
/* Quickjump to the translated instruction from some |
810 |
previous instruction? */ |
811 |
for (i=0; i<cpu->mem->n_quick_jumps; i++) |
812 |
if (cpu->mem->quick_jump_page_offset[i] == p) |
813 |
bintrans_write_quickjump(cpu->mem, |
814 |
cpu->mem->quick_jump_host_address[i], |
815 |
tep->chunk[prev_p]); |
816 |
} |
817 |
|
818 |
if (translated && try_to_translate && prev_p < 1023) { |
819 |
int mask = 1 << ((prev_p+1) & 7); |
820 |
|
821 |
if (tep->flags[(prev_p+1) >> 3] & mask |
822 |
&& !delayed_branch) { |
823 |
bintrans_write_chunkreturn_fail(&ca); |
824 |
/* try_to_translate = 0; */ |
825 |
break; |
826 |
} |
827 |
|
828 |
/* Glue together with previously translated code, |
829 |
if any: */ |
830 |
if (tep->chunk[prev_p+1] != 0 && !delayed_branch) { |
831 |
bintrans_write_instruction__delayedbranch(cpu->mem, |
832 |
&ca, &tep->chunk[prev_p+1], NULL, 1, p+4, 1); |
833 |
/* try_to_translate = 0; */ |
834 |
break; |
835 |
} |
836 |
|
837 |
if (n_translated > 80 && !delayed_branch) { |
838 |
bintrans_write_instruction__delayedbranch(cpu->mem, |
839 |
&ca, &tep->chunk[prev_p+1], NULL, 1, p+4, 1); |
840 |
/* try_to_translate = 0; */ |
841 |
break; |
842 |
} |
843 |
} |
844 |
|
845 |
p += sizeof(instr); |
846 |
prev_p ++; |
847 |
|
848 |
/* If we have reached a different (MIPS) page, then stop translating. */ |
849 |
if (p == 0x1000) |
850 |
try_to_translate = 0; |
851 |
} |
852 |
|
853 |
tep->page_is_potentially_in_use = 1; |
854 |
|
855 |
/* Not enough translated? Then abort. */ |
856 |
if (n_translated < 1) { |
857 |
int mask = 1 << (offset_within_page & 7); |
858 |
tep->flags[offset_within_page >> 3] |= mask; |
859 |
return cpu->cd.mips.bintrans_instructions_executed; |
860 |
} |
861 |
|
862 |
/* ca2 = ptr to the head of the new code chunk */ |
863 |
ca2 = cpu->mem->translation_code_chunk_space + |
864 |
cpu->mem->translation_code_chunk_space_head; |
865 |
|
866 |
/* Add return code: */ |
867 |
bintrans_write_chunkreturn(&ca); |
868 |
|
869 |
/* chunk_len = nr of bytes occupied by the new code chunk */ |
870 |
chunk_len = (size_t)ca - (size_t)ca2; |
871 |
|
872 |
/* Invalidate the host's instruction cache, if necessary: */ |
873 |
bintrans_host_cacheinvalidate(ca2, chunk_len); |
874 |
|
875 |
cpu->mem->translation_code_chunk_space_head += chunk_len; |
876 |
|
877 |
/* Align the code chunk space: */ |
878 |
cpu->mem->translation_code_chunk_space_head = |
879 |
((cpu->mem->translation_code_chunk_space_head - 1) | 63) + 1; |
880 |
|
881 |
|
882 |
/* RUN the code chunk: */ |
883 |
f = ca2; |
884 |
|
885 |
run_it: |
886 |
/* printf("BEFORE: pc=%016llx r31=%016llx\n", |
887 |
(long long)cpu->pc, (long long)cpu->cd.mips.gpr[31]); */ |
888 |
|
889 |
enter_chunks_into_tables(cpu, cpu->pc, &tep->chunk[0]); |
890 |
|
891 |
old_n_executed = cpu->cd.mips.bintrans_instructions_executed; |
892 |
|
893 |
bintrans_runchunk(cpu, f); |
894 |
|
895 |
/* printf("AFTER: pc=%016llx r31=%016llx\n", |
896 |
(long long)cpu->pc, (long long)cpu->cd.mips.gpr[31]); */ |
897 |
|
898 |
if (!cpu->cd.mips.delay_slot && !cpu->cd.mips.nullify_next && |
899 |
cpu->cd.mips.bintrans_instructions_executed < N_SAFE_BINTRANS_LIMIT |
900 |
&& (cpu->pc & 3) == 0 |
901 |
&& cpu->cd.mips.bintrans_instructions_executed != old_n_executed) { |
902 |
int ok = 0, a, b; |
903 |
struct vth32_table *tbl1; |
904 |
|
905 |
if (cpu->mem->bintrans_32bit_only || |
906 |
(cpu->pc & 0xffffffff80000000ULL) == 0 || |
907 |
(cpu->pc & 0xffffffff80000000ULL) == 0xffffffff80000000ULL) { |
908 |
/* 32-bit special case: */ |
909 |
a = (cpu->pc >> 22) & 0x3ff; |
910 |
b = (cpu->pc >> 12) & 0x3ff; |
911 |
|
912 |
/* TODO: There is a bug here; if caches are disabled, and |
913 |
for some reason the code jumps to a different page, then |
914 |
this would jump to code in the cache! The fix is |
915 |
to check for cache isolation, and if so, use the |
916 |
kernel table. Otherwise use table0. */ |
917 |
|
918 |
/* tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; */ |
919 |
|
920 |
tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0[a]; |
921 |
if (tbl1->haddr_entry[b] != NULL) { |
922 |
paddr = tbl1->paddr_entry[b] | (cpu->pc & 0xfff); |
923 |
ok = 1; |
924 |
} |
925 |
} |
926 |
|
927 |
/* General case, or if the special case above failed: */ |
928 |
/* (This may cause exceptions.) */ |
929 |
if (!ok) { |
930 |
uint64_t old_pc = cpu->cd.mips.pc_last = cpu->pc; |
931 |
ok = cpu->translate_address(cpu, cpu->pc, &paddr, FLAG_INSTR); |
932 |
|
933 |
if (!ok && old_pc != cpu->pc) { |
934 |
/* pc is something like ...0080 or ...0000 or so. */ |
935 |
paddr = cpu->pc & 0xfff; |
936 |
ok = 1; |
937 |
|
938 |
cpu->cd.mips.pc_last_host_4k_page = NULL; |
939 |
cpu->cd.mips.pc_bintrans_host_4kpage = NULL; |
940 |
} |
941 |
} |
942 |
|
943 |
if (ok) { |
944 |
paddr_page = paddr & ~0xfff; |
945 |
offset_within_page = (paddr & 0xfff) >> 2; |
946 |
entry_index = PADDR_TO_INDEX(paddr); |
947 |
tep = cpu->mem->translation_page_entry_array[entry_index]; |
948 |
while (tep != NULL) { |
949 |
if (tep->paddr == paddr_page) { |
950 |
int mask; |
951 |
if (tep->chunk[offset_within_page] != 0) { |
952 |
f = (size_t)tep->chunk[offset_within_page] + |
953 |
cpu->mem->translation_code_chunk_space; |
954 |
goto run_it; |
955 |
} |
956 |
mask = 1 << (offset_within_page & 7); |
957 |
if (tep->flags[offset_within_page >> 3] & mask) |
958 |
return cpu->cd.mips.bintrans_instructions_executed; |
959 |
break; |
960 |
} |
961 |
tep = tep->next; |
962 |
} |
963 |
|
964 |
#if 1 |
965 |
/* We have no translation. */ |
966 |
if ((cpu->pc & 0xfff00000) == 0xbfc00000 && |
967 |
cpu->machine->prom_emulation) |
968 |
return cpu->cd.mips.bintrans_instructions_executed; |
969 |
|
970 |
/* This special hack might make the time spent |
971 |
in the main cpu_run_instr() lower: */ |
972 |
/* TODO: This doesn't seem to work with R4000 etc? */ |
973 |
if (cpu->mem->bintrans_32bit_only) { |
974 |
/* || (cpu->pc & 0xffffffff80000000ULL) == 0 || |
975 |
(cpu->pc & 0xffffffff80000000ULL) == 0xffffffff80000000ULL) { */ |
976 |
int ok = 1; |
977 |
/* 32-bit special case: */ |
978 |
a = (cpu->pc >> 22) & 0x3ff; |
979 |
b = (cpu->pc >> 12) & 0x3ff; |
980 |
if (cpu->cd.mips.vaddr_to_hostaddr_table0 != |
981 |
cpu->cd.mips.vaddr_to_hostaddr_table0_kernel) |
982 |
ok = 0; |
983 |
tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; |
984 |
if (ok && tbl1->haddr_entry[b] != NULL) { |
985 |
cpu->cd.mips.pc_last_virtual_page = cpu->pc & ~0xfff; |
986 |
cpu->cd.mips.pc_last_physical_page = paddr & ~0xfff; |
987 |
cpu->cd.mips.pc_last_host_4k_page = (unsigned char *) |
988 |
(((size_t)tbl1->haddr_entry[b]) & ~1); |
989 |
cpu->cd.mips.pc_bintrans_host_4kpage = cpu->cd.mips.pc_last_host_4k_page; |
990 |
cpu->cd.mips.pc_bintrans_paddr = paddr; |
991 |
|
992 |
/* |
993 |
printf("C: "); |
994 |
printf("v=%016llx p=%016llx h=%p paddr=%016llx\n", |
995 |
(long long)cpu->cd.mips.pc_last_virtual_page, |
996 |
(long long)cpu->cd.mips.pc_last_physical_page, |
997 |
cpu->cd.mips.pc_last_host_4k_page,(long long)paddr); |
998 |
*/ |
999 |
goto quick_attempt_translate_again; |
1000 |
} |
1001 |
} |
1002 |
#endif |
1003 |
|
1004 |
/* Return. */ |
1005 |
} |
1006 |
} |
1007 |
|
1008 |
return cpu->cd.mips.bintrans_instructions_executed; |
1009 |
} |
1010 |
|
1011 |
|
1012 |
/* |
1013 |
* bintrans_attempt_translate(): |
1014 |
* |
1015 |
* Attempt to translate a chunk of code, starting at 'paddr'. If successful, |
1016 |
* the code chunk is run. |
1017 |
* |
1018 |
* Returns the number of executed instructions. |
1019 |
*/ |
1020 |
int bintrans_attempt_translate(struct cpu *cpu, uint64_t paddr) |
1021 |
{ |
1022 |
if (cpu->machine->old_bintrans_enable) |
1023 |
return old_bintrans_attempt_translate(cpu, paddr); |
1024 |
|
1025 |
/* TODO */ |
1026 |
/* printf("bintrans_attempt_translate(): TODO\n"); */ |
1027 |
|
1028 |
return 0; |
1029 |
} |
1030 |
|
1031 |
|
1032 |
/* |
1033 |
* old_bintrans_init_cpu(): |
1034 |
* |
1035 |
* This must be called for each cpu wishing to use bintrans. This should |
1036 |
* be called after bintrans_init(), but before any other function in this |
1037 |
* module. |
1038 |
*/ |
1039 |
void old_bintrans_init_cpu(struct cpu *cpu) |
1040 |
{ |
1041 |
int i, offset; |
1042 |
|
1043 |
cpu->cd.mips.chunk_base_address = cpu->mem->translation_code_chunk_space; |
1044 |
cpu->cd.mips.bintrans_loadstore_32bit = bintrans_loadstore_32bit; |
1045 |
cpu->cd.mips.bintrans_jump_to_32bit_pc = bintrans_jump_to_32bit_pc; |
1046 |
cpu->cd.mips.bintrans_fast_tlbwri = coproc_tlbwri; |
1047 |
cpu->cd.mips.bintrans_fast_tlbpr = coproc_tlbpr; |
1048 |
cpu->cd.mips.bintrans_fast_rfe = coproc_rfe; |
1049 |
cpu->cd.mips.bintrans_fast_eret = coproc_eret; |
1050 |
cpu->cd.mips.bintrans_simple_exception = mips_cpu_cause_simple_exception; |
1051 |
cpu->cd.mips.fast_vaddr_to_hostaddr = fast_vaddr_to_hostaddr; |
1052 |
|
1053 |
/* Initialize vaddr->hostaddr translation tables: */ |
1054 |
cpu->cd.mips.vaddr_to_hostaddr_nulltable = |
1055 |
zeroed_alloc(sizeof(struct vth32_table)); |
1056 |
|
1057 |
/* Data cache: */ |
1058 |
offset = 0; |
1059 |
cpu->cd.mips.vaddr_to_hostaddr_r2k3k_dcachetable = |
1060 |
zeroed_alloc(sizeof(struct vth32_table)); |
1061 |
for (i=0; i<1024; i++) { |
1062 |
cpu->cd.mips.vaddr_to_hostaddr_r2k3k_dcachetable->haddr_entry[i] = |
1063 |
(void *)(((size_t)cpu->cd.mips.cache[0]+offset) | 1); |
1064 |
offset = (offset + 4096) % cpu->cd.mips.cache_size[0]; |
1065 |
} |
1066 |
cpu->cd.mips.vaddr_to_hostaddr_r2k3k_dcachetable->refcount = 1024; |
1067 |
|
1068 |
/* Instruction cache: */ |
1069 |
offset = 0; |
1070 |
cpu->cd.mips.vaddr_to_hostaddr_r2k3k_icachetable = |
1071 |
zeroed_alloc(sizeof(struct vth32_table)); |
1072 |
for (i=0; i<1024; i++) { |
1073 |
cpu->cd.mips.vaddr_to_hostaddr_r2k3k_icachetable->haddr_entry[i] = |
1074 |
(void *)(((size_t)cpu->cd.mips.cache[1]+offset) | 1); |
1075 |
offset = (offset + 4096) % cpu->cd.mips.cache_size[1]; |
1076 |
} |
1077 |
cpu->cd.mips.vaddr_to_hostaddr_r2k3k_icachetable->refcount = 1024; |
1078 |
|
1079 |
cpu->cd.mips.vaddr_to_hostaddr_table0_kernel = |
1080 |
zeroed_alloc(1024 * sizeof(struct vth32_table *)); |
1081 |
cpu->cd.mips.vaddr_to_hostaddr_table0_user = |
1082 |
zeroed_alloc(1024 * sizeof(struct vth32_table *)); |
1083 |
cpu->cd.mips.vaddr_to_hostaddr_table0_cacheisol_i = |
1084 |
zeroed_alloc(1024 * sizeof(struct vth32_table *)); |
1085 |
cpu->cd.mips.vaddr_to_hostaddr_table0_cacheisol_d = |
1086 |
zeroed_alloc(1024 * sizeof(struct vth32_table *)); |
1087 |
|
1088 |
for (i=0; i<1024; i++) { |
1089 |
cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[i] = cpu->cd.mips.vaddr_to_hostaddr_nulltable; |
1090 |
cpu->cd.mips.vaddr_to_hostaddr_table0_user[i] = cpu->cd.mips.vaddr_to_hostaddr_nulltable; |
1091 |
cpu->cd.mips.vaddr_to_hostaddr_table0_cacheisol_i[i] = cpu->cd.mips.vaddr_to_hostaddr_r2k3k_icachetable; |
1092 |
cpu->cd.mips.vaddr_to_hostaddr_table0_cacheisol_d[i] = cpu->cd.mips.vaddr_to_hostaddr_r2k3k_dcachetable; |
1093 |
} |
1094 |
|
1095 |
cpu->cd.mips.vaddr_to_hostaddr_table0 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel; |
1096 |
|
1097 |
cpu->mem->bintrans_32bit_only = (cpu->cd.mips.cpu_type.isa_level <= 2 |
1098 |
|| cpu->cd.mips.cpu_type.isa_level == 32); |
1099 |
} |
1100 |
|
1101 |
|
1102 |
/* |
1103 |
* bintrans_init_cpu(): |
1104 |
* |
1105 |
* This must be called for each cpu wishing to use bintrans. This should |
1106 |
* be called after bintrans_init(), but before any other function in this |
1107 |
* module. |
1108 |
*/ |
1109 |
void bintrans_init_cpu(struct cpu *cpu) |
1110 |
{ |
1111 |
if (cpu->machine->old_bintrans_enable) { |
1112 |
old_bintrans_init_cpu(cpu); |
1113 |
return; |
1114 |
} |
1115 |
|
1116 |
/* TODO */ |
1117 |
debug("\nbintrans_init_cpu(): New bintrans: TODO"); |
1118 |
} |
1119 |
|
1120 |
|
1121 |
/* |
1122 |
* bintrans_init(): |
1123 |
* |
1124 |
* Should be called before any other bintrans_*() function is used. |
1125 |
*/ |
1126 |
void bintrans_init(struct machine *machine, struct memory *mem) |
1127 |
{ |
1128 |
int res, i, n = 1 << BINTRANS_CACHE_N_INDEX_BITS; |
1129 |
size_t s; |
1130 |
|
1131 |
mem->translation_page_entry_array = malloc(sizeof( |
1132 |
struct translation_page_entry *) * |
1133 |
(1 << BINTRANS_CACHE_N_INDEX_BITS)); |
1134 |
if (mem->translation_page_entry_array == NULL) { |
1135 |
fprintf(stderr, "old_bintrans_init(): out of memory\n"); |
1136 |
exit(1); |
1137 |
} |
1138 |
|
1139 |
/* |
1140 |
* The entry array must be NULLed, as these are pointers to |
1141 |
* translation page entries. |
1142 |
*/ |
1143 |
for (i=0; i<n; i++) |
1144 |
mem->translation_page_entry_array[i] = NULL; |
1145 |
|
1146 |
/* Allocate the large code chunk space: */ |
1147 |
s = machine->bintrans_size + CODE_CHUNK_SPACE_MARGIN; |
1148 |
mem->translation_code_chunk_space = (unsigned char *) mmap(NULL, s, |
1149 |
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); |
1150 |
|
1151 |
/* If mmap() failed, try malloc(): */ |
1152 |
if (mem->translation_code_chunk_space == NULL) { |
1153 |
mem->translation_code_chunk_space = malloc(s); |
1154 |
if (mem->translation_code_chunk_space == NULL) { |
1155 |
fprintf(stderr, "old_bintrans_init(): out of " |
1156 |
"memory (2)\n"); |
1157 |
exit(1); |
1158 |
} |
1159 |
} |
1160 |
|
1161 |
debug("bintrans: "BACKEND_NAME", %i MB translation cache at %p\n", |
1162 |
(int)(s/1048576), mem->translation_code_chunk_space); |
1163 |
|
1164 |
/* |
1165 |
* The translation_code_chunk_space does not need to be zeroed, |
1166 |
* but the pointers to where in the chunk space we are about to |
1167 |
* add new chunks must be initialized to the beginning of the |
1168 |
* chunk space. |
1169 |
*/ |
1170 |
mem->translation_code_chunk_space_head = 0; |
1171 |
|
1172 |
/* |
1173 |
* Some operating systems (for example OpenBSD using the default |
1174 |
* stack protection settings in GCC) don't allow code to be |
1175 |
* dynamically created in memory and executed. This will attempt |
1176 |
* to enable execution of the code chunk space. |
1177 |
* |
1178 |
* NOTE/TODO: A Linux man page for mprotect from 1997 says that |
1179 |
* "POSIX.1b says that mprotect can be used only on regions |
1180 |
* of memory obtained from mmap(2).". If malloc() isn't implemented |
1181 |
* using mmap(), then this could be a problem. |
1182 |
*/ |
1183 |
res = mprotect((void *)mem->translation_code_chunk_space, |
1184 |
s, PROT_READ | PROT_WRITE | PROT_EXEC); |
1185 |
if (res) |
1186 |
debug("warning: mprotect() failed with errno %i." |
1187 |
" this usually doesn't really matter...\n", errno); |
1188 |
|
1189 |
bintrans_backend_init(); |
1190 |
|
1191 |
if (!machine->old_bintrans_enable) { |
1192 |
debug("bintrans_init(): TODO: New bintrans (?)\n"); |
1193 |
} |
1194 |
} |
1195 |
|
1196 |
|
1197 |
#endif /* BINTRANS */ |