/[gxemul]/upstream/0.3.1/src/bintrans.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.3.1/src/bintrans.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (show annotations)
Mon Oct 8 16:17:52 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 36698 byte(s)
0.3.1
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 */

  ViewVC Help
Powered by ViewVC 1.1.26