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

Annotation of /trunk/src/bintrans.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (hide annotations)
Mon Oct 8 16:18:38 2007 UTC (16 years, 6 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 dpavlin 2 /*
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 dpavlin 12 * $Id: bintrans.c,v 1.177 2005/08/14 15:47:36 debug Exp $
29 dpavlin 2 *
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 dpavlin 4 void bintrans_restart(struct cpu *cpu) { }
124 dpavlin 2 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 dpavlin 12 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 dpavlin 2 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 dpavlin 12 int bigendian, int do_alignment_check);
160 dpavlin 2 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 dpavlin 4 * 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 dpavlin 12 if (cpu->machine->arch != ARCH_MIPS)
280     return;
281    
282 dpavlin 4 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 dpavlin 12 debug("[ bintrans: Starting over! ]\n");
289 dpavlin 4 clear_all_chunks_from_all_tables(cpu);
290     }
291    
292    
293     /*
294 dpavlin 2 * 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 dpavlin 10 if (tbl1->haddr_entry[b*2] != NULL)
308 dpavlin 2 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 dpavlin 12 int delayed_branch, stop_after_delayed_branch, return_code_written;
344 dpavlin 2 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 dpavlin 4 offset_within_page = (paddr & 0xfff) >> 2;
358 dpavlin 2 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 dpavlin 4 int mask;
363 dpavlin 2
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 dpavlin 4
370     mask = 1 << (offset_within_page & 7);
371 dpavlin 2 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 dpavlin 4 bintrans_restart(cpu);
397 dpavlin 2 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 dpavlin 12 ((cpu->mem->translation_code_chunk_space_head - 1) | 31)+1;
416 dpavlin 2
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 dpavlin 4 prev_p = p >> 2;
444 dpavlin 2 try_to_translate = 1;
445     n_translated = 0;
446     res = 0;
447 dpavlin 12 return_code_written = 0;
448 dpavlin 2 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 dpavlin 12 return_code_written = 1;
499 dpavlin 2 } 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 dpavlin 12 translated = try_to_translate = bintrans_write_instruction__addu_etc(cpu->mem, &ca, rd, rs, rt, sa, special6);
541 dpavlin 2 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 dpavlin 12 return_code_written = 1;
563 dpavlin 2 }
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 dpavlin 12 return_code_written = 1;
590 dpavlin 2 }
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 dpavlin 4 translated = try_to_translate =
634 dpavlin 12 bintrans_write_instruction__addiu_etc(cpu->mem,
635     &ca, instr[2] & 31,
636 dpavlin 4 ((instr[3] & 3) << 3) + ((instr[2] >> 5) & 7),
637     (instr[1] << 8) + instr[0], hi6);
638 dpavlin 2 n_translated += translated;
639     break;
640    
641     case HI6_LUI:
642 dpavlin 4 translated = try_to_translate =
643     bintrans_write_instruction__lui(&ca,
644     instr[2] & 31, (instr[1] << 8) + instr[0]);
645 dpavlin 2 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 dpavlin 12 } 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 dpavlin 4 try_to_translate = 0;
686 dpavlin 12 return_code_written = 1;
687     }
688 dpavlin 2 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 dpavlin 12 translated = try_to_translate = bintrans_write_instruction__addu_etc(cpu->mem, &ca, 0, 0, 0, 0, SPECIAL_SLL);
719 dpavlin 2 n_translated += translated;
720 dpavlin 12 } 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 dpavlin 4 try_to_translate = 0;
729 dpavlin 12 return_code_written = 1;
730     }
731 dpavlin 2 break;
732     default:
733 dpavlin 12 /* 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 dpavlin 2 try_to_translate = 0;
741 dpavlin 12 return_code_written = 1;
742 dpavlin 2 }
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 dpavlin 12 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 dpavlin 2 n_translated += translated;
774     break;
775    
776     case HI6_CACHE:
777 dpavlin 12 translated = try_to_translate = bintrans_write_instruction__addu_etc(cpu->mem, &ca, 0, 0, 0, 0, SPECIAL_SLL);
778 dpavlin 2 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 dpavlin 12 return_code_written = 1;
791 dpavlin 2 }
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 dpavlin 12
817 dpavlin 2 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 dpavlin 4 if (translated && try_to_translate && prev_p < 1023) {
840     int mask = 1 << ((prev_p+1) & 7);
841 dpavlin 2
842 dpavlin 4 if (tep->flags[(prev_p+1) >> 3] & mask
843     && !delayed_branch) {
844     bintrans_write_chunkreturn_fail(&ca);
845 dpavlin 12 try_to_translate = 0;
846     return_code_written = 1;
847 dpavlin 4 break;
848     }
849 dpavlin 2
850 dpavlin 4 /* 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 dpavlin 12 try_to_translate = 0;
856     return_code_written = 1;
857 dpavlin 4 /* try_to_translate = 0; */
858     break;
859     }
860 dpavlin 2
861 dpavlin 12 if (n_translated > 120 && !delayed_branch) {
862 dpavlin 4 bintrans_write_instruction__delayedbranch(cpu->mem,
863     &ca, &tep->chunk[prev_p+1], NULL, 1, p+4, 1);
864 dpavlin 12 try_to_translate = 0;
865     return_code_written = 1;
866 dpavlin 4 break;
867     }
868 dpavlin 2 }
869    
870     p += sizeof(instr);
871 dpavlin 4 prev_p ++;
872 dpavlin 2
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 dpavlin 12 if (!return_code_written)
893     bintrans_write_chunkreturn(&ca);
894 dpavlin 2
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 dpavlin 12 ((cpu->mem->translation_code_chunk_space_head - 1) | 31) + 1;
906 dpavlin 2
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 dpavlin 10 if (tbl1->haddr_entry[b*2] != NULL) {
948 dpavlin 2 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 dpavlin 4 offset_within_page = (paddr & 0xfff) >> 2;
972 dpavlin 2 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 dpavlin 4 int mask;
977 dpavlin 2 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 dpavlin 4 mask = 1 << (offset_within_page & 7);
983 dpavlin 2 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 dpavlin 10 if ((cpu->pc & 0xdff00000) == 0x9fc00000 &&
993 dpavlin 2 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 dpavlin 10 if (ok && tbl1->haddr_entry[b*2] != NULL) {
1011 dpavlin 2 cpu->cd.mips.pc_last_virtual_page = cpu->pc & ~0xfff;
1012     cpu->cd.mips.pc_last_physical_page = paddr & ~0xfff;
1013 dpavlin 10 cpu->cd.mips.pc_last_host_4k_page = (unsigned char *)tbl1->haddr_entry[b*2];
1014 dpavlin 2 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 dpavlin 10 cpu->cd.mips.bintrans_load_32bit = bintrans_load_32bit;
1070     cpu->cd.mips.bintrans_store_32bit = bintrans_store_32bit;
1071 dpavlin 2 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 dpavlin 10 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 dpavlin 2 offset = (offset + 4096) % cpu->cd.mips.cache_size[0];
1093     }
1094     cpu->cd.mips.vaddr_to_hostaddr_r2k3k_dcachetable->refcount = 1024;
1095    
1096 dpavlin 12 /* 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 dpavlin 2 /* 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 dpavlin 10 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 dpavlin 2 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 dpavlin 4
1146     cpu->mem->bintrans_32bit_only = (cpu->cd.mips.cpu_type.isa_level <= 2
1147     || cpu->cd.mips.cpu_type.isa_level == 32);
1148 dpavlin 2 }
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