/[gxemul]/trunk/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 /trunk/src/bintrans.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (show annotations)
Mon Oct 8 16:18:38 2007 UTC (11 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 38613 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.905 2005/08/16 09:16:24 debug Exp $
20050628	Continuing the work on the ARM translation engine. end_of_page
		works. Experimenting with load/store translation caches
		(virtual -> physical -> host).
20050629	More ARM stuff (memory access translation cache, mostly). This
		might break a lot of stuff elsewhere, probably some MIPS-
		related translation things.
20050630	Many load/stores are now automatically generated and included
		into cpu_arm_instr.c; 1024 functions in total (!).
		Fixes based on feedback from Alec Voropay: only print 8 hex
		digits instead of 16 in some cases when emulating 32-bit
		machines; similar 8 vs 16 digit fix for breakpoint addresses;
		4Kc has 16 TLB entries, not 48; the MIPS config select1
		register is now printed with "reg ,0".
		Also changing many other occurances of 16 vs 8 digit output.
		Adding cache associativity fields to mips_cpu_types.h; updating
		some other cache fields; making the output of
		mips_cpu_dumpinfo() look nicer.
		Generalizing the bintrans stuff for device accesses to also
		work with the new translation system. (This might also break
		some MIPS things.)
		Adding multi-load/store instructions to the ARM disassembler
		and the translator, and some optimizations of various kinds.
20050701	Adding a simple dev_disk (it can read/write sectors from
		disk images).
20050712	Adding dev_ether (a simple ethernet send/receive device).
		Debugger command "ninstrs" for toggling show_nr_of_instructions
		during runtime.
		Removing the framebuffer logo.
20050713	Continuing on dev_ether.
		Adding a dummy cpu_alpha (again).
20050714	More work on cpu_alpha.
20050715	More work on cpu_alpha. Many instructions work, enough to run
		a simple framebuffer fill test (similar to the ARM test).
20050716	More Alpha stuff.
20050717	Minor updates (Alpha stuff).
20050718	Minor updates (Alpha stuff).
20050719	Generalizing some Alpha instructions.
20050720	More Alpha-related updates.
20050721	Continuing on cpu_alpha. Importing rpb.h from NetBSD/alpha.
20050722	Alpha-related updates: userland stuff (Hello World using
		write() compiled statically for FreeBSD/Alpha runs fine), and
		more instructions are now implemented.
20050723	Fixing ldq_u and stq_u.
		Adding more instructions (conditional moves, masks, extracts,
		shifts).
20050724	More FreeBSD/Alpha userland stuff, and adding some more
		instructions (inserts).
20050725	Continuing on the Alpha stuff. (Adding dummy ldt/stt.)
		Adding a -A command line option to turn off alignment checks
		in some cases (for translated code).
		Trying to remove the old bintrans code which updated the pc
		and nr_of_executed_instructions for every instruction.
20050726	Making another attempt att removing the pc/nr of instructions
		code. This time it worked, huge performance increase for
		artificial test code, but performance loss for real-world
		code :-( so I'm scrapping that code for now.
		Tiny performance increase on Alpha (by using ret instead of
		jmp, to play nice with the Alpha's branch prediction) for the
		old MIPS bintrans backend.
20050727	Various minor fixes and cleanups.
20050728	Switching from a 2-level virtual to host/physical translation
		system for ARM emulation, to a 1-level translation.
		Trying to switch from 2-level to 1-level for the MIPS bintrans
		system as well (Alpha only, so far), but there is at least one
		problem: caches and/or how they work with device mappings.
20050730	Doing the 2-level to 1-level conversion for the i386 backend.
		The cache/device bug is still there for R2K/3K :(
		Various other minor updates (Malta etc).
		The mc146818 clock now updates the UIP bit in a way which works
		better with Linux for at least sgimips and Malta emulation.
		Beginning the work on refactoring the dyntrans system.
20050731	Continuing the dyntrans refactoring.
		Fixing a small but serious host alignment bug in memory_rw.
		Adding support for big-endian load/stores to the i386 bintrans
		backend.
		Another minor i386 bintrans backend update: stores from the
		zero register are now one (or two) loads shorter.
		The slt and sltu instructions were incorrectly implemented for
		the i386 backend; only using them for 32-bit mode for now.
20050801	Continuing the dyntrans refactoring.
		Cleanup of the ns16550 serial controller (removing unnecessary
		code).
		Bugfix (memory corruption bug) in dev_gt, and a patch/hack from
		Alec Voropay for Linux/Malta.
20050802	More cleanup/refactoring of the dyntrans subsystem: adding
		phys_page pointers to the lookup tables, for quick jumps
		between translated pages.
		Better fix for the ns16550 device (but still no real FIFO
		functionality).
		Converting cpu_ppc to the new dyntrans system. This means that
		I will have to start from scratch with implementing each
		instruction, and figure out how to implement dual 64/32-bit
		modes etc.
		Removing the URISC CPU family, because it was useless.
20050803	When selecting a machine type, the main type can now be omitted
		if the subtype name is unique. (I.e. -E can be omitted.)
		Fixing a dyntrans/device update bug. (Writes to offset 0 of
		a device could sometimes go unnoticed.)
		Adding an experimental "instruction combination" hack for
		ARM for memset-like byte fill loops.
20050804	Minor progress on cpu_alpha and related things.
		Finally fixing the MIPS dmult/dmultu bugs.
		Fixing some minor TODOs.
20050805	Generalizing the 8259 PIC. It now also works with Cobalt
		and evbmips emulation, in addition to the x86 hack.
		Finally converting the ns16550 device to use devinit.
		Continuing the work on the dyntrans system. Thinking about
		how to add breakpoints.
20050806	More dyntrans updates. Breakpoints seem to work now.
20050807	Minor updates: cpu_alpha and related things; removing
		dev_malta (as it isn't used any more).
		Dyntrans: working on general "show trace tree" support.
		The trace tree stuff now works with both the old MIPS code and
		with newer dyntrans modes. :)
		Continuing on Alpha-related stuff (trying to get *BSD to boot
		a bit further, adding more instructions, etc).
20050808	Adding a dummy IA64 cpu family, and continuing the refactoring
		of the dyntrans system.
		Removing the regression test stuff, because it was more or
		less useless.
		Adding loadlinked/storeconditional type instructions to the
		Alpha emulation. (Needed for Linux/alpha. Not very well tested
		yet.)
20050809	The function call trace tree now prints a per-function nr of
		arguments. (Semi-meaningless, since that data isn't read yet
		from the ELFs; some hardcoded symbols such as memcpy() and
		strlen() work fine, though.)
		More dyntrans refactoring; taking out more of the things that
		are common to all cpu families.
20050810	Working on adding support for "dual mode" for PPC dyntrans
		(i.e. both 64-bit and 32-bit modes).
		(Re)adding some simple PPC instructions.
20050811	Adding a dummy M68K cpu family. The dyntrans system isn't ready
		for variable-length ISAs yet, so it's completely bogus so far.
		Re-adding more PPC instructions.
		Adding a hack to src/file.c which allows OpenBSD/mac68k a.out
		kernels to be loaded.
		Beginning to add PPC loads/stores. So far they only work in
		32-bit mode.
20050812	The configure file option "add_remote" now accepts symbolic
		host names, in addition to numeric IPv4 addresses.
		Re-adding more PPC instructions.
20050814	Continuing to port back more PPC instructions.
		Found and fixed the cache/device write-update bug for 32-bit
		MIPS bintrans. :-)
		Triggered a really weird and annoying bug in Compaq's C
		compiler; ccc sometimes outputs code which loads from an
		address _before_ checking whether the pointer was NULL or not.
		(I'm not sure how to handle this problem.)
20050815	Removing all of the old x86 instruction execution code; adding
		a new (dummy) dyntrans module for x86.
		Taking the first steps to extend the dyntrans system to support
		variable-length instructions.
		Slowly preparing for the next release.
20050816	Adding a dummy SPARC cpu module.
		Minor updates (documentation etc) for the release.

==============  RELEASE 0.3.5  ==============


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 */

  ViewVC Help
Powered by ViewVC 1.1.26