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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (show annotations)
Mon Oct 8 16:18:38 2007 UTC (12 years, 2 months ago) by dpavlin
File MIME type: text/plain
File size: 24450 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) 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: cpu_dyntrans.c,v 1.29 2005/08/16 05:37:10 debug Exp $
29 *
30 * Common dyntrans routines. Included from cpu_*.c.
31 */
32
33
34 #ifdef DYNTRANS_CPU_RUN_INSTR
35 /*
36 * XXX_cpu_run_instr():
37 *
38 * Execute one or more instructions on a specific CPU, using dyntrans.
39 *
40 * Return value is the number of instructions executed during this call,
41 * 0 if no instructions were executed.
42 */
43 int DYNTRANS_CPU_RUN_INSTR(struct emul *emul, struct cpu *cpu)
44 {
45 #ifdef DYNTRANS_ARM
46 uint32_t cached_pc;
47 #else
48 uint64_t cached_pc;
49 #endif
50 int low_pc, n_instrs;
51
52 DYNTRANS_PC_TO_POINTERS(cpu);
53
54 #ifdef DYNTRANS_ARM
55 cached_pc = cpu->cd.arm.r[ARM_PC] & ~3;
56 #else
57 cached_pc = cpu->pc & ~3;
58 #endif
59
60 cpu->n_translated_instrs = 0;
61 cpu->running_translated = 1;
62
63 if (single_step || cpu->machine->instruction_trace) {
64 /*
65 * Single-step:
66 */
67 struct DYNTRANS_IC *ic = cpu->cd.DYNTRANS_ARCH.next_ic
68 #ifndef DYNTRANS_VARIABLE_INSTRUCTION_LENGTH
69 ++
70 #endif
71 ;
72 if (cpu->machine->instruction_trace) {
73 #ifdef DYNTRANS_X86
74 unsigned char instr[17];
75 cpu->cd.x86.cursegment = X86_S_CS;
76 cpu->cd.x86.seg_override = 0;
77 #else
78 #ifdef DYNTRANS_M68K
79 unsigned char instr[16]; /* TODO: 16? */
80 #else
81 unsigned char instr[4]; /* General case... */
82 #endif
83 #endif
84 if (!cpu->memory_rw(cpu, cpu->mem, cached_pc, &instr[0],
85 sizeof(instr), MEM_READ, CACHE_INSTRUCTION)) {
86 fatal("XXX_cpu_run_instr(): could not read "
87 "the instruction\n");
88 } else
89 cpu_disassemble_instr(cpu->machine, cpu,
90 instr, 1, 0, 0);
91 }
92
93 /* When single-stepping, multiple instruction calls cannot
94 be combined into one. This clears all translations: */
95 if (cpu->cd.DYNTRANS_ARCH.cur_physpage->flags & COMBINATIONS) {
96 int i;
97 for (i=0; i<DYNTRANS_IC_ENTRIES_PER_PAGE; i++)
98 cpu->cd.DYNTRANS_ARCH.cur_physpage->ics[i].f =
99 #ifdef DYNTRANS_DUALMODE_32
100 cpu->is_32bit?
101 instr32(to_be_translated) :
102 #endif
103 instr(to_be_translated);
104 fatal("[ Note: The translation of physical page 0x%llx"
105 " contained combinations of instructions; these "
106 "are now flushed because we are single-stepping."
107 " ]\n", (long long)cpu->cd.DYNTRANS_ARCH.
108 cur_physpage->physaddr);
109 cpu->cd.DYNTRANS_ARCH.cur_physpage->flags &=
110 ~(COMBINATIONS | TRANSLATIONS);
111 }
112
113 /* Execute just one instruction: */
114 ic->f(cpu, ic);
115 n_instrs = 1;
116 } else {
117 /* Execute multiple instructions: */
118 n_instrs = 0;
119 for (;;) {
120 struct DYNTRANS_IC *ic;
121
122 #ifdef DYNTRANS_VARIABLE_INSTRUCTION_LENGTH
123 #define I ic = cpu->cd.DYNTRANS_ARCH.next_ic; ic->f(cpu, ic);
124 #else
125 #define I ic = cpu->cd.DYNTRANS_ARCH.next_ic ++; ic->f(cpu, ic);
126 #endif
127 I; I; I; I; I; I; I; I; I; I;
128 I; I; I; I; I; I; I; I; I; I;
129 I; I; I; I; I; I; I; I; I; I;
130 I; I; I; I; I; I; I; I; I; I;
131 I; I; I; I; I; I; I; I; I; I;
132
133 I; I; I; I; I; I; I; I; I; I;
134 I; I; I; I; I; I; I; I; I; I;
135 I; I; I; I; I; I; I; I; I; I;
136 I; I; I; I; I; I; I; I; I; I;
137 I; I; I; I; I; I; I; I; I; I;
138
139 I; I; I; I; I; I; I; I; I; I;
140 I; I; I; I; I; I; I; I; I; I;
141
142 n_instrs += 120;
143
144 if (!cpu->running_translated ||
145 n_instrs + cpu->n_translated_instrs >= 16384)
146 break;
147 }
148 }
149
150
151 /*
152 * Update the program counter and return the correct number of
153 * executed instructions:
154 */
155 low_pc = ((size_t)cpu->cd.DYNTRANS_ARCH.next_ic - (size_t)
156 cpu->cd.DYNTRANS_ARCH.cur_ic_page) / sizeof(struct DYNTRANS_IC);
157
158 if (low_pc >= 0 && low_pc < DYNTRANS_IC_ENTRIES_PER_PAGE) {
159 #ifdef DYNTRANS_ARM
160 cpu->cd.arm.r[ARM_PC] &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1)<<2);
161 cpu->cd.arm.r[ARM_PC] += (low_pc << 2);
162 cpu->pc = cpu->cd.arm.r[ARM_PC];
163 #else
164 cpu->pc &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1) <<
165 DYNTRANS_INSTR_ALIGNMENT_SHIFT);
166 cpu->pc += (low_pc << DYNTRANS_INSTR_ALIGNMENT_SHIFT);
167 #endif
168 } else if (low_pc == DYNTRANS_IC_ENTRIES_PER_PAGE) {
169 /* Switch to next page: */
170 #ifdef DYNTRANS_ARM
171 cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
172 cpu->cd.arm.r[ARM_PC] += (ARM_IC_ENTRIES_PER_PAGE << 2);
173 cpu->pc = cpu->cd.arm.r[ARM_PC];
174 #else
175 cpu->pc &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1) <<
176 DYNTRANS_INSTR_ALIGNMENT_SHIFT);
177 cpu->pc += (DYNTRANS_IC_ENTRIES_PER_PAGE <<
178 DYNTRANS_INSTR_ALIGNMENT_SHIFT);
179 #endif
180 } else {
181 /* debug("debug: Outside a page (This is actually ok)\n"); */
182 }
183
184 return n_instrs + cpu->n_translated_instrs;
185 }
186 #endif /* DYNTRANS_CPU_RUN_INSTR */
187
188
189
190 #ifdef DYNTRANS_FUNCTION_TRACE
191 /*
192 * XXX_cpu_functioncall_trace():
193 *
194 * Without this function, the main trace tree function prints something
195 * like <f()> or <0x1234()> on a function call. It is up to this
196 * function to print the arguments passed.
197 */
198 void DYNTRANS_FUNCTION_TRACE(struct cpu *cpu, uint64_t f, int n_args)
199 {
200 char strbuf[50];
201 char *symbol;
202 uint64_t ot;
203 int x, print_dots = 1, n_args_to_print =
204 #ifdef DYNTRANS_ALPHA
205 6
206 #else
207 4 /* Most non-Alpha archs */
208 #endif
209 ;
210
211 if (n_args >= 0 && n_args <= n_args_to_print) {
212 print_dots = 0;
213 n_args_to_print = n_args;
214 }
215
216 /*
217 * TODO: The type of each argument should be taken from the symbol
218 * table, in some way.
219 *
220 * The code here does a kind of "heuristic guess" regarding what the
221 * argument values might mean. Sometimes the output looks weird, but
222 * usually it looks good enough.
223 *
224 * Print ".." afterwards to show that there might be more arguments
225 * than were passed in register.
226 */
227 for (x=0; x<n_args_to_print; x++) {
228 int64_t d;
229 #ifdef DYNTRANS_X86
230 d = 0; /* TODO */
231 #else
232 /* Args in registers: */
233 d = cpu->cd.DYNTRANS_ARCH.
234 #ifdef DYNTRANS_ALPHA
235 r[ALPHA_A0
236 #endif
237 #ifdef DYNTRANS_ARM
238 r[0
239 #endif
240 #ifdef DYNTRANS_IA64
241 r[0 /* TODO */
242 #endif
243 #ifdef DYNTRANS_M68K
244 d[0 /* TODO */
245 #endif
246 #ifdef DYNTRANS_MIPS
247 gpr[MIPS_GPR_A0
248 #endif
249 #ifdef DYNTRANS_PPC
250 gpr[3
251 #endif
252 #ifdef DYNTRANS_SPARC
253 r_i[0
254 #endif
255 + x];
256 #endif
257 symbol = get_symbol_name(&cpu->machine->symbol_context, d, &ot);
258
259 if (d > -256 && d < 256)
260 fatal("%i", (int)d);
261 else if (memory_points_to_string(cpu, cpu->mem, d, 1))
262 fatal("\"%s\"", memory_conv_to_string(cpu,
263 cpu->mem, d, strbuf, sizeof(strbuf)));
264 else if (symbol != NULL && ot == 0)
265 fatal("&%s", symbol);
266 else {
267 if (cpu->is_32bit)
268 fatal("0x%x", (int)d);
269 else
270 fatal("0x%llx", (long long)d);
271 }
272
273 if (x < n_args_to_print - 1)
274 fatal(",");
275 }
276
277 if (print_dots)
278 fatal(",..");
279 }
280 #endif
281
282
283
284 #ifdef DYNTRANS_TC_ALLOCATE_DEFAULT_PAGE
285 /* forward declaration of to_be_translated and end_of_page: */
286 static void instr(to_be_translated)(struct cpu *, struct DYNTRANS_IC *);
287 static void instr(end_of_page)(struct cpu *,struct DYNTRANS_IC *);
288 #ifdef DYNTRANS_DUALMODE_32
289 static void instr32(to_be_translated)(struct cpu *, struct DYNTRANS_IC *);
290 #endif
291 /*
292 * XXX_tc_allocate_default_page():
293 *
294 * Create a default page (with just pointers to instr(to_be_translated)
295 * at cpu->translation_cache_cur_ofs.
296 */
297 static void DYNTRANS_TC_ALLOCATE_DEFAULT_PAGE(struct cpu *cpu,
298 uint64_t physaddr)
299 {
300 struct DYNTRANS_TC_PHYSPAGE *ppp;
301 int i;
302
303 /* Create the physpage header: */
304 ppp = (struct DYNTRANS_TC_PHYSPAGE *)(cpu->translation_cache
305 + cpu->translation_cache_cur_ofs);
306 ppp->next_ofs = 0;
307 ppp->physaddr = physaddr;
308
309 /* TODO: Is this faster than copying an entire template page? */
310
311 for (i=0; i<DYNTRANS_IC_ENTRIES_PER_PAGE; i++)
312 ppp->ics[i].f =
313 #ifdef DYNTRANS_DUALMODE_32
314 cpu->is_32bit? instr32(to_be_translated) :
315 #endif
316 instr(to_be_translated);
317
318 ppp->ics[DYNTRANS_IC_ENTRIES_PER_PAGE].f = instr(end_of_page);
319
320 cpu->translation_cache_cur_ofs += sizeof(struct DYNTRANS_TC_PHYSPAGE);
321 }
322 #endif /* DYNTRANS_TC_ALLOCATE_DEFAULT_PAGE */
323
324
325
326 #ifdef DYNTRANS_PC_TO_POINTERS_FUNC
327 /*
328 * XXX_pc_to_pointers():
329 *
330 * This function uses the current program counter (a virtual address) to
331 * find out which physical translation page to use, and then sets the current
332 * translation page pointers to that page.
333 *
334 * If there was no translation page for that physical page, then an empty
335 * one is created.
336 */
337 void DYNTRANS_PC_TO_POINTERS_FUNC(struct cpu *cpu)
338 {
339 #ifdef DYNTRANS_32
340 uint32_t
341 #else
342 uint64_t
343 #endif
344 cached_pc, physaddr, physpage_ofs;
345 int pagenr, table_index;
346 uint32_t *physpage_entryp;
347 struct DYNTRANS_TC_PHYSPAGE *ppp;
348
349 #ifdef DYNTRANS_32
350 int index;
351 cached_pc = cpu->pc;
352 index = cached_pc >> 12;
353 ppp = cpu->cd.DYNTRANS_ARCH.phys_page[index];
354 if (ppp != NULL)
355 goto have_it;
356 #else
357 #ifdef DYNTRANS_ALPHA
358 uint32_t a, b;
359 int kernel = 0;
360 struct alpha_vph_page *vph_p;
361 cached_pc = cpu->pc;
362 a = (cached_pc >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1);
363 b = (cached_pc >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1);
364 if ((cached_pc >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) {
365 vph_p = cpu->cd.alpha.vph_table0_kernel[a];
366 kernel = 1;
367 } else
368 vph_p = cpu->cd.alpha.vph_table0[a];
369 if (vph_p != cpu->cd.alpha.vph_default_page) {
370 ppp = vph_p->phys_page[b];
371 if (ppp != NULL)
372 goto have_it;
373 }
374 #else
375 #ifdef DYNTRANS_IA64
376 fatal("IA64 todo\n");
377 #else
378 #error Neither alpha, ia64, nor 32-bit?
379 #endif
380 #endif
381 #endif
382
383 /*
384 * TODO: virtual to physical address translation
385 */
386 physaddr = cached_pc & ~( ((DYNTRANS_IC_ENTRIES_PER_PAGE-1) <<
387 DYNTRANS_INSTR_ALIGNMENT_SHIFT) |
388 ((1 << DYNTRANS_INSTR_ALIGNMENT_SHIFT)-1) );
389
390 if (cpu->translation_cache_cur_ofs >= DYNTRANS_CACHE_SIZE)
391 cpu_create_or_reset_tc(cpu);
392
393 pagenr = DYNTRANS_ADDR_TO_PAGENR(physaddr);
394 table_index = PAGENR_TO_TABLE_INDEX(pagenr);
395
396 physpage_entryp = &(((uint32_t *)cpu->translation_cache)[table_index]);
397 physpage_ofs = *physpage_entryp;
398 ppp = NULL;
399
400 /* Traverse the physical page chain: */
401 while (physpage_ofs != 0) {
402 ppp = (struct DYNTRANS_TC_PHYSPAGE *)(cpu->translation_cache
403 + physpage_ofs);
404 /* If we found the page in the cache, then we're done: */
405 if (ppp->physaddr == physaddr)
406 break;
407 /* Try the next page in the chain: */
408 physpage_ofs = ppp->next_ofs;
409 }
410
411 /* If the offset is 0 (or ppp is NULL), then we need to create a
412 new "default" empty translation page. */
413
414 if (ppp == NULL) {
415 /* fatal("CREATING page %lli (physaddr 0x%llx), table index "
416 "%i\n", (long long)pagenr, (long long)physaddr,
417 (int)table_index); */
418 *physpage_entryp = physpage_ofs =
419 cpu->translation_cache_cur_ofs;
420
421 /* Allocate a default page, with to_be_translated entries: */
422 DYNTRANS_TC_ALLOCATE(cpu, physaddr);
423
424 ppp = (struct DYNTRANS_TC_PHYSPAGE *)(cpu->translation_cache
425 + physpage_ofs);
426 }
427
428 #ifdef DYNTRANS_32
429 if (cpu->cd.DYNTRANS_ARCH.host_load[index] != NULL)
430 cpu->cd.DYNTRANS_ARCH.phys_page[index] = ppp;
431 #endif
432
433 #ifdef DYNTRANS_ALPHA
434 if (vph_p->host_load[b] != NULL)
435 vph_p->phys_page[b] = ppp;
436 #endif
437
438 have_it:
439 cpu->cd.DYNTRANS_ARCH.cur_physpage = ppp;
440 cpu->cd.DYNTRANS_ARCH.cur_ic_page = &ppp->ics[0];
441 cpu->cd.DYNTRANS_ARCH.next_ic = cpu->cd.DYNTRANS_ARCH.cur_ic_page +
442 DYNTRANS_PC_TO_IC_ENTRY(cached_pc);
443
444 /* printf("cached_pc=0x%016llx pagenr=%lli table_index=%lli, "
445 "physpage_ofs=0x%016llx\n", (long long)cached_pc, (long long)pagenr,
446 (long long)table_index, (long long)physpage_ofs); */
447 }
448 #endif /* DYNTRANS_PC_TO_POINTERS_FUNC */
449
450
451
452 #ifdef DYNTRANS_INVAL_ENTRY
453 /*
454 * XXX_invalidate_tlb_entry():
455 *
456 * Invalidate one translation entry (based on virtual address).
457 */
458 void DYNTRANS_INVALIDATE_TLB_ENTRY(struct cpu *cpu,
459 #ifdef DYNTRANS_32
460 uint32_t
461 #else
462 uint64_t
463 #endif
464 vaddr_page)
465 {
466 #ifdef DYNTRANS_1LEVEL
467 uint32_t index = vaddr_page >> 12;
468 cpu->cd.DYNTRANS_ARCH.host_load[index] = NULL;
469 cpu->cd.DYNTRANS_ARCH.host_store[index] = NULL;
470 cpu->cd.DYNTRANS_ARCH.phys_addr[index] = 0;
471 cpu->cd.DYNTRANS_ARCH.phys_page[index] = NULL;
472 #else
473 /* 2-level: */
474 #ifdef DYNTRANS_ALPHA
475 struct alpha_vph_page *vph_p;
476 uint32_t a, b;
477 int kernel = 0;
478
479 a = (vaddr_page >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1);
480 b = (vaddr_page >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1);
481 if ((vaddr_page >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) {
482 vph_p = cpu->cd.alpha.vph_table0_kernel[a];
483 kernel = 1;
484 } else
485 vph_p = cpu->cd.alpha.vph_table0[a];
486
487 if (vph_p == cpu->cd.alpha.vph_default_page) {
488 fatal("alpha_invalidate_tlb_entry(): huh? Problem 1.\n");
489 exit(1);
490 }
491
492 vph_p->host_load[b] = NULL;
493 vph_p->host_store[b] = NULL;
494 vph_p->phys_addr[b] = 0;
495 vph_p->phys_page[b] = NULL;
496 vph_p->refcount --;
497 if (vph_p->refcount < 0) {
498 fatal("alpha_invalidate_tlb_entry(): huh? Problem 2.\n");
499 exit(1);
500 }
501 if (vph_p->refcount == 0) {
502 vph_p->next = cpu->cd.alpha.vph_next_free_page;
503 cpu->cd.alpha.vph_next_free_page = vph_p;
504 if (kernel)
505 cpu->cd.alpha.vph_table0_kernel[a] =
506 cpu->cd.alpha.vph_default_page;
507 else
508 cpu->cd.alpha.vph_table0[a] =
509 cpu->cd.alpha.vph_default_page;
510 }
511 #else /* !DYNTRANS_ALPHA */
512 #ifdef DYNTRANS_IA64
513 fatal("IA64: blah blah TODO\n");
514 #else
515 #error Not yet for non-1-level, non-Alpha, non-ia64
516 #endif /* !DYNTRANS_IA64 */
517 #endif /* !DYNTRANS_ALPHA */
518 #endif
519 }
520 #endif
521
522
523 #ifdef DYNTRANS_INVALIDATE_TC_PADDR
524 /*
525 * XXX_invalidate_translation_caches_paddr():
526 *
527 * Invalidate all entries matching a specific physical address.
528 */
529 void DYNTRANS_INVALIDATE_TC_PADDR(struct cpu *cpu, uint64_t paddr)
530 {
531 int r;
532 #ifdef DYNTRANS_32
533 uint32_t
534 #else
535 uint64_t
536 #endif
537 paddr_page = paddr &
538 #ifdef DYNTRANS_8K
539 ~0x1fff
540 #else
541 ~0xfff
542 #endif
543 ;
544
545 for (r=0; r<DYNTRANS_MAX_VPH_TLB_ENTRIES; r++) {
546 if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid &&
547 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].paddr_page ==
548 paddr_page) {
549 DYNTRANS_INVALIDATE_TLB_ENTRY(cpu,
550 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page);
551 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid = 0;
552 }
553 }
554 }
555 #endif /* DYNTRANS_INVALIDATE_TC_PADDR */
556
557
558
559 #ifdef DYNTRANS_INVALIDATE_TC_CODE
560 /*
561 * XXX_invalidate_code_translation_caches():
562 *
563 * Invalidate all entries matching a specific virtual address.
564 */
565 void DYNTRANS_INVALIDATE_TC_CODE(struct cpu *cpu)
566 {
567 int r;
568 #ifdef DYNTRANS_32
569 uint32_t
570 #else
571 uint64_t
572 #endif
573 vaddr_page;
574
575 for (r=0; r<DYNTRANS_MAX_VPH_TLB_ENTRIES; r++) {
576 if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid) {
577 vaddr_page = cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r]
578 .vaddr_page & ~(DYNTRANS_PAGESIZE-1);
579 #ifdef DYNTRANS_1LEVEL
580 {
581 uint32_t index = vaddr_page >> 12;
582 cpu->cd.DYNTRANS_ARCH.phys_page[index] = NULL;
583 }
584 #else
585 {
586 /* 2-level: */
587 #ifdef DYNTRANS_ALPHA
588 struct alpha_vph_page *vph_p;
589 uint32_t a, b;
590 int kernel = 0;
591
592 a = (vaddr_page >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1);
593 b = (vaddr_page >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1);
594 if ((vaddr_page >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) {
595 vph_p = cpu->cd.alpha.vph_table0_kernel[a];
596 kernel = 1;
597 } else
598 vph_p = cpu->cd.alpha.vph_table0[a];
599 vph_p->phys_page[b] = NULL;
600 #else /* !DYNTRANS_ALPHA */
601 #ifdef DYNTRANS_IA64
602 fatal("IA64: blah yo yo TODO\n");
603 #else
604 #error Not yet for non-Alpha, non-1Level, non-ia64
605 #endif /* !DYNTRANS_IA64 */
606 #endif /* !DYNTRANS_ALPHA */
607 }
608 #endif
609 }
610 }
611 }
612 #endif /* DYNTRANS_INVALIDATE_TC_CODE */
613
614
615
616 #ifdef DYNTRANS_UPDATE_TRANSLATION_TABLE
617 /*
618 * XXX_update_translation_table():
619 *
620 * Update the virtual memory translation tables.
621 */
622 void DYNTRANS_UPDATE_TRANSLATION_TABLE(struct cpu *cpu, uint64_t vaddr_page,
623 unsigned char *host_page, int writeflag, uint64_t paddr_page)
624 {
625 int64_t lowest, highest = -1;
626 int found, r, lowest_index;
627
628 #ifdef DYNTRANS_ALPHA
629 uint32_t a, b;
630 struct alpha_vph_page *vph_p;
631 int kernel = 0;
632 /* fatal("update_translation_table(): v=0x%llx, h=%p w=%i"
633 " p=0x%llx\n", (long long)vaddr_page, host_page, writeflag,
634 (long long)paddr_page); */
635 #else
636 #ifdef DYNTRANS_32
637 uint32_t index;
638 vaddr_page &= 0xffffffffULL;
639 paddr_page &= 0xffffffffULL;
640 /* fatal("update_translation_table(): v=0x%x, h=%p w=%i"
641 " p=0x%x\n", (int)vaddr_page, host_page, writeflag,
642 (int)paddr_page); */
643 #else /* !DYNTRANS_32 */
644 #ifdef DYNTRANS_IA64
645 fatal("IA64 update todo\n");
646 #else
647 #error Neither 32-bit, IA64, nor Alpha?
648 #endif
649 #endif
650 #endif
651
652 /* Scan the current TLB entries: */
653 found = -1; lowest_index = 0;
654 lowest = cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[0].timestamp;
655 for (r=0; r<DYNTRANS_MAX_VPH_TLB_ENTRIES; r++) {
656 if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].timestamp < lowest) {
657 lowest = cpu->cd.DYNTRANS_ARCH.
658 vph_tlb_entry[r].timestamp;
659 lowest_index = r;
660 }
661 if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].timestamp > highest)
662 highest = cpu->cd.DYNTRANS_ARCH.
663 vph_tlb_entry[r].timestamp;
664 if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid &&
665 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page ==
666 vaddr_page) {
667 found = r;
668 break;
669 }
670 }
671
672 if (found < 0) {
673 /* Create the new TLB entry, overwriting the oldest one: */
674 r = lowest_index;
675 if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid) {
676 /* This one has to be invalidated first: */
677 DYNTRANS_INVALIDATE_TLB_ENTRY(cpu,
678 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page);
679 }
680
681 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid = 1;
682 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].host_page = host_page;
683 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].paddr_page = paddr_page;
684 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page = vaddr_page;
685 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].writeflag = writeflag;
686 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].timestamp = highest + 1;
687
688 /* Add the new translation to the table: */
689 #ifdef DYNTRANS_ALPHA
690 a = (vaddr_page >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1);
691 b = (vaddr_page >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1);
692 if ((vaddr_page >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) {
693 vph_p = cpu->cd.alpha.vph_table0_kernel[a];
694 kernel = 1;
695 } else
696 vph_p = cpu->cd.alpha.vph_table0[a];
697 if (vph_p == cpu->cd.alpha.vph_default_page) {
698 if (cpu->cd.alpha.vph_next_free_page != NULL) {
699 if (kernel)
700 vph_p = cpu->cd.alpha.vph_table0_kernel
701 [a] = cpu->cd.alpha.
702 vph_next_free_page;
703 else
704 vph_p = cpu->cd.alpha.vph_table0[a] =
705 cpu->cd.alpha.vph_next_free_page;
706 cpu->cd.alpha.vph_next_free_page = vph_p->next;
707 } else {
708 if (kernel)
709 vph_p = cpu->cd.alpha.vph_table0_kernel
710 [a] = malloc(sizeof(struct
711 alpha_vph_page));
712 else
713 vph_p = cpu->cd.alpha.vph_table0[a] =
714 malloc(sizeof(struct
715 alpha_vph_page));
716 memset(vph_p, 0, sizeof(struct alpha_vph_page));
717 }
718 }
719 vph_p->refcount ++;
720 vph_p->host_load[b] = host_page;
721 vph_p->host_store[b] = writeflag? host_page : NULL;
722 vph_p->phys_addr[b] = paddr_page;
723 vph_p->phys_page[b] = NULL;
724 #else
725 #ifdef DYNTRANS_32
726 index = vaddr_page >> 12;
727 cpu->cd.DYNTRANS_ARCH.host_load[index] = host_page;
728 cpu->cd.DYNTRANS_ARCH.host_store[index] =
729 writeflag? host_page : NULL;
730 cpu->cd.DYNTRANS_ARCH.phys_addr[index] = paddr_page;
731 cpu->cd.DYNTRANS_ARCH.phys_page[index] = NULL;;
732 #endif /* 32 */
733 #endif /* !ALPHA */
734 } else {
735 /*
736 * The translation was already in the TLB.
737 * Writeflag = 0: Do nothing.
738 * Writeflag = 1: Make sure the page is writable.
739 * Writeflag = -1: Downgrade to readonly.
740 */
741 #ifdef DYNTRANS_ALPHA
742 a = (vaddr_page >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1);
743 b = (vaddr_page >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1);
744 if ((vaddr_page >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) {
745 vph_p = cpu->cd.alpha.vph_table0_kernel[a];
746 kernel = 1;
747 } else
748 vph_p = cpu->cd.alpha.vph_table0[a];
749 cpu->cd.alpha.vph_tlb_entry[found].timestamp = highest + 1;
750 if (vph_p->phys_addr[b] == paddr_page) {
751 if (writeflag == 1)
752 vph_p->host_store[b] = host_page;
753 if (writeflag == -1)
754 vph_p->host_store[b] = NULL;
755 } else {
756 /* Change the entire physical/host mapping: */
757 vph_p->host_load[b] = host_page;
758 vph_p->host_store[b] = writeflag? host_page : NULL;
759 vph_p->phys_addr[b] = paddr_page;
760 }
761 #else
762 #ifdef DYNTRANS_32
763 index = vaddr_page >> 12;
764 cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[found].timestamp =
765 highest + 1;
766 if (cpu->cd.DYNTRANS_ARCH.phys_addr[index] == paddr_page) {
767 if (writeflag == 1)
768 cpu->cd.DYNTRANS_ARCH.host_store[index] =
769 host_page;
770 if (writeflag == -1)
771 cpu->cd.DYNTRANS_ARCH.host_store[index] = NULL;
772 } else {
773 /* Change the entire physical/host mapping: */
774 cpu->cd.DYNTRANS_ARCH.host_load[index] = host_page;
775 cpu->cd.DYNTRANS_ARCH.host_store[index] =
776 writeflag? host_page : NULL;
777 cpu->cd.DYNTRANS_ARCH.phys_addr[index] = paddr_page;
778 }
779 #endif /* 32 */
780 #endif /* !ALPHA */
781 }
782 }
783 #endif /* DYNTRANS_UPDATE_TRANSLATION_TABLE */
784
785
786
787 #ifdef DYNTRANS_TO_BE_TRANSLATED_HEAD
788 /*
789 * Check for breakpoints.
790 */
791 if (!single_step_breakpoint) {
792 int i;
793 for (i=0; i<cpu->machine->n_breakpoints; i++)
794 if (cpu->pc == cpu->machine->breakpoint_addr[i]) {
795 if (!cpu->machine->instruction_trace) {
796 int old_quiet_mode = quiet_mode;
797 quiet_mode = 0;
798 DISASSEMBLE(cpu, ib, 1, 0, 0);
799 quiet_mode = old_quiet_mode;
800 }
801 fatal("BREAKPOINT: pc = 0x%llx\n(The "
802 "instruction has not yet executed.)\n",
803 (long long)cpu->pc);
804 single_step_breakpoint = 1;
805 single_step = 1;
806 goto stop_running_translated;
807 }
808 }
809 #endif /* DYNTRANS_TO_BE_TRANSLATED_HEAD */
810
811
812
813 #ifdef DYNTRANS_TO_BE_TRANSLATED_TAIL
814 /*
815 * If we end up here, then an instruction was translated.
816 */
817 translated;
818
819 /*
820 * Now it is time to check for combinations of instructions that can
821 * be converted into a single function call.
822 *
823 * Note: Single-stepping or instruction tracing doesn't work with
824 * instruction combination.
825 */
826 if (!single_step && !cpu->machine->instruction_trace)
827 COMBINE_INSTRUCTIONS(cpu, ic, addr);
828
829 /* ... and finally execute the translated instruction: */
830 if (single_step_breakpoint) {
831 /*
832 * Special case when single-stepping: Execute the translated
833 * instruction, but then replace it with a "to be translated"
834 * directly afterwards.
835 */
836 single_step_breakpoint = 0;
837 ic->f(cpu, ic);
838 ic->f =
839 #ifdef DYNTRANS_DUALMODE_32
840 cpu->is_32bit? instr32(to_be_translated) :
841 #endif
842 instr(to_be_translated);
843 } else
844 ic->f(cpu, ic);
845
846 return;
847
848
849 bad: /*
850 * Nothing was translated. (Unimplemented or illegal instruction.)
851 */
852
853 quiet_mode = 0;
854 fatal("to_be_translated(): TODO: unimplemented instruction");
855
856 if (cpu->machine->instruction_trace)
857 #ifdef DYNTRANS_32
858 fatal(" at 0x%x\n", (int)cpu->pc);
859 #else
860 fatal(" at 0x%llx\n", (long long)cpu->pc);
861 #endif
862 else {
863 fatal(":\n");
864 DISASSEMBLE(cpu, ib, 1, 0, 0);
865 }
866
867 cpu->running = 0;
868 cpu->dead = 1;
869 stop_running_translated:
870 debugger_n_steps_left_before_interaction = 0;
871 cpu->running_translated = 0;
872 ic = cpu->cd.DYNTRANS_ARCH.next_ic = &nothing_call;
873 cpu->cd.DYNTRANS_ARCH.next_ic ++;
874
875 /* Execute the "nothing" instruction: */
876 ic->f(cpu, ic);
877 #endif /* DYNTRANS_TO_BE_TRANSLATED_TAIL */
878

  ViewVC Help
Powered by ViewVC 1.1.26