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