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