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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 40 - (show annotations)
Mon Oct 8 16:22:11 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 15000 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1539 2007/05/01 04:03:51 debug Exp $
20070415	Landisk PCLOCK should be 33.33 MHz, not 50 MHz. (This makes
		the clock run at correct speed.)
		FINALLY found and fixed the bug which caused OpenBSD/landisk
		to randomly bug out: an &-sign was missing in the special case
		handling of FPSCR in the 'LDS.L @Rm+,FPSCR' instruction.
		Adding similar special case handling for 'LDC.L @Rm+,SR'
		(calling sh_update_sr() instead of just loading).
		Implementing the 'FCNVSD FPUL,DRn' and 'FCNVDS DRm,FPUL'
		SuperH instructions.
		The 'LDC Rm,SR' instruction now immediately breaks out of the
		dyntrans loop if an interrupt is to be triggered.
20070416	In memory_rw.c, if mapping a page as writable, make sure to
		invalidate code translations even if the data access was a
		read.
		Minor SuperH updates.
20070418	Removing the dummy M68K emulation mode.
		Minor SH update (turning unnecessary sts_mach_rn, sts_macl_rn,
		and sts_pr_rn instruction handlers into mov_rm_rn).
20070419	Beginning to add a skeleton for an M88K mode: Adding a hack to
		allow OpenBSD/m88k a.out binaries to be loaded, and disassembly
		of a few simple 88K instructions.
		Commenting out the 'LDC Rm,SR' fix from a few days ago, because
		it made Linux/dreamcast bug out.
		Adding a hack to dev_sh4.c (an extra translation cache
		invalidation), which allows OpenBSD/landisk to boot ok after
		an install. Upgrading the Landisk machine mode to stable,
		updating documentation, etc.
20070420	Experimenting with adding a PCI controller (pcic) to dev_sh4.
		Adding a dummy Realtek 8139C+ skeleton device (dev_rtl8139c).
		Implementing the first M88K instructions (br, or[.u] imm), and
		adding disassembly of some more instructions.
20070421	Continuing a little on dev_rtl8139c.
20070422	Implementing the 9346 EEPROM "read" command for dev_rtl8139c.
		Finally found and fixed an old bug in the log n symbol search
		(it sometimes missed symbols). Debug trace (-i, -t etc) should
		now show more symbols. :-)
20070423	Continuing a little on M88K disassembly.
20070428	Fixing a memset arg order bug in src/net/net.c (thanks to
		Nigel Horne for noticing the bug).
		Applying parts of a patch from Carl van Schaik to clear out
		bottom bits of MIPS addresses more correctly, when using large
		page sizes, and doing some other minor cleanup/refactoring.
		Fixing a couple of warnings given by gcc with the -W option (a
		few more warnings than just plain -Wall).
		Reducing SuperH dyntrans physical address space from 64-bit to
		32-bit (since SH5/SH64 isn't imlemented yet anyway).
		Adding address-to-symbol annotation to a few more instructions
		in the SuperH instruction trace output.
		Beginning regression testing for the next release.
		Reverting the value of SCIF_DELAYED_TX_VALUE from 1 to 2,
		because OpenBSD/landisk may otherwise hang randomly.
20070429	The ugly hack/workaround to get OpenBSD/landisk booting without
		crashing does NOT work anymore (with the April 21 snapshot
		of OpenBSD/landisk). Strangely enough, removing the hack
		completely causes OpenBSD/landisk to work (!).
		More regression testing (re-testing everything SuperH-related,
		and some other things).
		Cobalt interrupts were actually broken; fixing by commenting
		out the DEC21143s in the Cobalt machine.
20070430	More regression testing.
20070501	Updating the OpenBSD/landisk install instructions to use
		4.1 instead of the current snapshot.
		GAAAH! OpenBSD/landisk 4.1 _needs_ the ugly hack/workaround;
		reintroducing it again. (The 4.1 kernel is actually from
		2007-03-11.)
		Simplifying the NetBSD/evbarm install instructions a bit.
		More regression testing.

==============  RELEASE 0.4.5.1  ==============


1 /*
2 * Copyright (C) 2005-2007 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.c,v 1.376 2007/04/19 15:18:15 debug Exp $
29 *
30 * Common routines for CPU emulation. (Not specific to any CPU type.)
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/mman.h>
37 #include <string.h>
38
39 #include "cpu.h"
40 #include "machine.h"
41 #include "memory.h"
42 #include "misc.h"
43 #include "settings.h"
44
45
46 extern size_t dyntrans_cache_size;
47 extern int native_code_translation_enabled;
48
49 static struct cpu_family *first_cpu_family = NULL;
50
51
52 /*
53 * cpu_new():
54 *
55 * Create a new cpu object. Each family is tried in sequence until a
56 * CPU family recognizes the cpu_type_name.
57 *
58 * If there was no match, NULL is returned. Otherwise, a pointer to an
59 * initialized cpu struct is returned.
60 */
61 struct cpu *cpu_new(struct memory *mem, struct machine *machine,
62 int cpu_id, char *name)
63 {
64 struct cpu *cpu;
65 struct cpu_family *fp;
66 char *cpu_type_name;
67 char tmpstr[30];
68
69 if (name == NULL) {
70 fprintf(stderr, "cpu_new(): cpu name = NULL?\n");
71 exit(1);
72 }
73
74 cpu_type_name = strdup(name);
75 if (cpu_type_name == NULL) {
76 fprintf(stderr, "cpu_new(): out of memory\n");
77 exit(1);
78 }
79
80 cpu = zeroed_alloc(sizeof(struct cpu));
81
82 cpu->path = malloc(strlen(machine->path) + 15);
83 if (cpu->path == NULL) {
84 fprintf(stderr, "cpu_new(): out of memory\n");
85 exit(1);
86 }
87 snprintf(cpu->path, strlen(machine->path) + 15,
88 "%s.cpu[%i]", machine->path, cpu_id);
89
90 cpu->memory_rw = NULL;
91 cpu->name = cpu_type_name;
92 cpu->mem = mem;
93 cpu->machine = machine;
94 cpu->cpu_id = cpu_id;
95 cpu->byte_order = EMUL_UNDEFINED_ENDIAN;
96 cpu->running = 0;
97
98 /* Create settings, and attach to the machine: */
99 cpu->settings = settings_new();
100 snprintf(tmpstr, sizeof(tmpstr), "cpu[%i]", cpu_id);
101 settings_add(machine->settings, tmpstr, 1,
102 SETTINGS_TYPE_SUBSETTINGS, 0, cpu->settings);
103
104 settings_add(cpu->settings, "name", 0, SETTINGS_TYPE_STRING,
105 SETTINGS_FORMAT_STRING, (void *) &cpu->name);
106 settings_add(cpu->settings, "running", 0, SETTINGS_TYPE_INT,
107 SETTINGS_FORMAT_YESNO, (void *) &cpu->running);
108
109 cpu_create_or_reset_tc(cpu);
110
111 fp = first_cpu_family;
112
113 while (fp != NULL) {
114 if (fp->cpu_new != NULL) {
115 if (fp->cpu_new(cpu, mem, machine, cpu_id,
116 cpu_type_name)) {
117 /* Sanity check: */
118 if (cpu->memory_rw == NULL) {
119 fatal("\ncpu_new(): memory_rw == "
120 "NULL\n");
121 exit(1);
122 }
123 break;
124 }
125 }
126
127 fp = fp->next;
128 }
129
130 if (fp == NULL) {
131 fatal("\ncpu_new(): unknown cpu type '%s'\n", cpu_type_name);
132 return NULL;
133 }
134
135 fp->init_tables(cpu);
136
137 if (cpu->byte_order == EMUL_UNDEFINED_ENDIAN) {
138 fatal("\ncpu_new(): Internal bug: Endianness not set.\n");
139 exit(1);
140 }
141
142 return cpu;
143 }
144
145
146 /*
147 * cpu_destroy():
148 *
149 * Destroy a cpu object.
150 */
151 void cpu_destroy(struct cpu *cpu)
152 {
153 settings_remove(cpu->settings, "name");
154 settings_remove(cpu->settings, "running");
155
156 /* Remove any remaining level-1 settings: */
157 settings_remove_all(cpu->settings);
158
159 settings_destroy(cpu->settings);
160
161 if (cpu->path != NULL)
162 free(cpu->path);
163
164 /* TODO: This assumes that zeroed_alloc() actually succeeded
165 with using mmap(), and not malloc()! */
166 munmap((void *)cpu, sizeof(struct cpu));
167 }
168
169
170 /*
171 * cpu_tlbdump():
172 *
173 * Called from the debugger to dump the TLB in a readable format.
174 * x is the cpu number to dump, or -1 to dump all CPUs.
175 *
176 * If rawflag is nonzero, then the TLB contents isn't formated nicely,
177 * just dumped.
178 */
179 void cpu_tlbdump(struct machine *m, int x, int rawflag)
180 {
181 if (m->cpu_family == NULL || m->cpu_family->tlbdump == NULL)
182 fatal("cpu_tlbdump(): NULL\n");
183 else
184 m->cpu_family->tlbdump(m, x, rawflag);
185 }
186
187
188 /*
189 * cpu_disassemble_instr():
190 *
191 * Convert an instruction word into human readable format, for instruction
192 * tracing.
193 */
194 int cpu_disassemble_instr(struct machine *m, struct cpu *cpu,
195 unsigned char *instr, int running, uint64_t addr)
196 {
197 if (m->cpu_family == NULL || m->cpu_family->disassemble_instr == NULL) {
198 fatal("cpu_disassemble_instr(): NULL\n");
199 return 0;
200 } else
201 return m->cpu_family->disassemble_instr(cpu, instr,
202 running, addr);
203 }
204
205
206 /*
207 * cpu_register_dump():
208 *
209 * Dump cpu registers in a relatively readable format.
210 *
211 * gprs: set to non-zero to dump GPRs. (CPU dependent.)
212 * coprocs: set bit 0..x to dump registers in coproc 0..x. (CPU dependent.)
213 */
214 void cpu_register_dump(struct machine *m, struct cpu *cpu,
215 int gprs, int coprocs)
216 {
217 if (m->cpu_family == NULL || m->cpu_family->register_dump == NULL)
218 fatal("cpu_register_dump(): NULL\n");
219 else
220 m->cpu_family->register_dump(cpu, gprs, coprocs);
221 }
222
223
224 /*
225 * cpu_functioncall_trace():
226 *
227 * This function should be called if machine->show_trace_tree is enabled, and
228 * a function call is being made. f contains the address of the function.
229 */
230 void cpu_functioncall_trace(struct cpu *cpu, uint64_t f)
231 {
232 int i, n_args = -1;
233 char *symbol;
234 uint64_t offset;
235
236 if (cpu->machine->ncpus > 1)
237 fatal("cpu%i:\t", cpu->cpu_id);
238
239 cpu->trace_tree_depth ++;
240 if (cpu->trace_tree_depth > 100)
241 cpu->trace_tree_depth = 100;
242 for (i=0; i<cpu->trace_tree_depth; i++)
243 fatal(" ");
244
245 fatal("<");
246 symbol = get_symbol_name_and_n_args(&cpu->machine->symbol_context,
247 f, &offset, &n_args);
248 if (symbol != NULL)
249 fatal("%s", symbol);
250 else {
251 if (cpu->is_32bit)
252 fatal("0x%"PRIx32, (uint32_t) f);
253 else
254 fatal("0x%"PRIx64, (uint64_t) f);
255 }
256 fatal("(");
257
258 if (cpu->machine->cpu_family->functioncall_trace != NULL)
259 cpu->machine->cpu_family->functioncall_trace(cpu, f, n_args);
260
261 fatal(")>\n");
262
263 #ifdef PRINT_MEMORY_CHECKSUM
264 /* Temporary hack for finding bugs: */
265 fatal("call chksum=%016"PRIx64"\n", memory_checksum(cpu->mem));
266 #endif
267 }
268
269
270 /*
271 * cpu_functioncall_trace_return():
272 *
273 * This function should be called if machine->show_trace_tree is enabled, and
274 * a function is being returned from.
275 *
276 * TODO: Print return value? This could be implemented similar to the
277 * cpu->functioncall_trace function call above.
278 */
279 void cpu_functioncall_trace_return(struct cpu *cpu)
280 {
281 cpu->trace_tree_depth --;
282 if (cpu->trace_tree_depth < 0)
283 cpu->trace_tree_depth = 0;
284 }
285
286
287 /*
288 * cpu_create_or_reset_tc():
289 *
290 * Create the translation cache in memory (ie allocate memory for it), if
291 * necessary, and then reset it to an initial state.
292 */
293 void cpu_create_or_reset_tc(struct cpu *cpu)
294 {
295 size_t s = dyntrans_cache_size + DYNTRANS_CACHE_MARGIN;
296
297 if (cpu->translation_cache == NULL) {
298 cpu->translation_cache = zeroed_alloc(s);
299
300 #ifdef NATIVE_CODE_GENERATION
301 if (native_code_translation_enabled) {
302 mprotect(cpu->translation_cache, s,
303 PROT_READ | PROT_WRITE | PROT_EXEC);
304 }
305 #endif
306 }
307
308 #ifdef NATIVE_CODE_GENERATION
309 if (native_code_translation_enabled && cpu->inr.inr_entries == NULL)
310 cpu->inr.inr_entries = zeroed_alloc(
311 sizeof(struct inr_entry) * INR_MAX_ENTRIES);
312
313 cpu->inr.nr_inr_entries_used = 0;
314 #endif
315
316 /* Create an empty table at the beginning of the translation cache: */
317 memset(cpu->translation_cache, 0, sizeof(uint32_t)
318 * N_BASE_TABLE_ENTRIES);
319
320 cpu->translation_cache_cur_ofs =
321 N_BASE_TABLE_ENTRIES * sizeof(uint32_t);
322
323 /*
324 * There might be other translation pointers that still point to
325 * within the translation_cache region. Let's invalidate those too:
326 */
327 if (cpu->invalidate_code_translation != NULL)
328 cpu->invalidate_code_translation(cpu, 0, INVALIDATE_ALL);
329 }
330
331
332 /*
333 * cpu_dumpinfo():
334 *
335 * Dumps info about a CPU using debug(). "cpu0: CPUNAME, running" (or similar)
336 * is outputed, and it is up to CPU dependent code to complete the line.
337 */
338 void cpu_dumpinfo(struct machine *m, struct cpu *cpu)
339 {
340 debug("cpu%i: %s, %s", cpu->cpu_id, cpu->name,
341 cpu->running? "running" : "stopped");
342
343 if (m->cpu_family == NULL || m->cpu_family->dumpinfo == NULL)
344 fatal("cpu_dumpinfo(): NULL\n");
345 else
346 m->cpu_family->dumpinfo(cpu);
347 }
348
349
350 /*
351 * cpu_list_available_types():
352 *
353 * Print a list of available CPU types for each cpu family.
354 */
355 void cpu_list_available_types(void)
356 {
357 struct cpu_family *fp;
358 int iadd = DEBUG_INDENTATION;
359
360 fp = first_cpu_family;
361
362 if (fp == NULL) {
363 debug("No CPUs defined!\n");
364 return;
365 }
366
367 while (fp != NULL) {
368 debug("%s:\n", fp->name);
369 debug_indentation(iadd);
370 if (fp->list_available_types != NULL)
371 fp->list_available_types();
372 else
373 debug("(internal error: list_available_types"
374 " = NULL)\n");
375 debug_indentation(-iadd);
376
377 fp = fp->next;
378 }
379 }
380
381
382 /*
383 * cpu_run_deinit():
384 *
385 * Shuts down all CPUs in a machine when ending a simulation. (This function
386 * should only need to be called once for each machine.)
387 */
388 void cpu_run_deinit(struct machine *machine)
389 {
390 int te;
391
392 /*
393 * Two last ticks of every hardware device. This will allow e.g.
394 * framebuffers to draw the last updates to the screen before halting.
395 *
396 * TODO: This should be refactored when redesigning the mainbus
397 * concepts!
398 */
399 for (te=0; te<machine->n_tick_entries; te++) {
400 machine->tick_func[te](machine->cpus[0],
401 machine->tick_extra[te]);
402 machine->tick_func[te](machine->cpus[0],
403 machine->tick_extra[te]);
404 }
405
406 if (machine->show_nr_of_instructions)
407 cpu_show_cycles(machine, 1);
408
409 fflush(stdout);
410 }
411
412
413 /*
414 * cpu_show_cycles():
415 *
416 * If show_nr_of_instructions is on, then print a line to stdout about how
417 * many instructions/cycles have been executed so far.
418 */
419 void cpu_show_cycles(struct machine *machine, int forced)
420 {
421 uint64_t offset, pc;
422 char *symbol;
423 int64_t mseconds, ninstrs, is, avg;
424 struct timeval tv;
425 struct cpu *cpu = machine->cpus[machine->bootstrap_cpu];
426
427 static int64_t mseconds_last = 0;
428 static int64_t ninstrs_last = -1;
429
430 pc = cpu->pc;
431
432 gettimeofday(&tv, NULL);
433 mseconds = (tv.tv_sec - machine->starttime.tv_sec) * 1000
434 + (tv.tv_usec - machine->starttime.tv_usec) / 1000;
435
436 if (mseconds == 0)
437 mseconds = 1;
438
439 if (mseconds - mseconds_last == 0)
440 mseconds ++;
441
442 ninstrs = machine->ninstrs_since_gettimeofday;
443
444 /* RETURN here, unless show_nr_of_instructions (-N) is turned on: */
445 if (!machine->show_nr_of_instructions && !forced)
446 goto do_return;
447
448 printf("[ %"PRIi64" instrs", (int64_t)machine->ninstrs);
449
450 /* Instructions per second, and average so far: */
451 is = 1000 * (ninstrs-ninstrs_last) / (mseconds-mseconds_last);
452 avg = (long long)1000 * ninstrs / mseconds;
453 if (is < 0)
454 is = 0;
455 if (avg < 0)
456 avg = 0;
457
458 if (cpu->has_been_idling) {
459 printf("; idling");
460 cpu->has_been_idling = 0;
461 } else
462 printf("; i/s=%"PRIi64" avg=%"PRIi64, is, avg);
463
464 symbol = get_symbol_name(&machine->symbol_context, pc, &offset);
465
466 if (machine->ncpus == 1) {
467 if (cpu->is_32bit)
468 printf("; pc=0x%08"PRIx32, (uint32_t) pc);
469 else
470 printf("; pc=0x%016"PRIx64, (uint64_t) pc);
471 }
472
473 if (symbol != NULL)
474 printf(" <%s>", symbol);
475 printf(" ]\n");
476
477 do_return:
478 ninstrs_last = ninstrs;
479 mseconds_last = mseconds;
480 }
481
482
483 /*
484 * cpu_run_init():
485 *
486 * Prepare to run instructions on all CPUs in this machine. (This function
487 * should only need to be called once for each machine.)
488 */
489 void cpu_run_init(struct machine *machine)
490 {
491 machine->ninstrs_flush = 0;
492 machine->ninstrs = 0;
493 machine->ninstrs_show = 0;
494
495 /* For performance measurement: */
496 gettimeofday(&machine->starttime, NULL);
497 machine->ninstrs_since_gettimeofday = 0;
498 }
499
500
501 /*
502 * add_cpu_family():
503 *
504 * Allocates a cpu_family struct and calls an init function for the
505 * family to fill in reasonable data and pointers.
506 */
507 static void add_cpu_family(int (*family_init)(struct cpu_family *), int arch)
508 {
509 struct cpu_family *fp, *tmp;
510 int res;
511
512 fp = malloc(sizeof(struct cpu_family));
513 if (fp == NULL) {
514 fprintf(stderr, "add_cpu_family(): out of memory\n");
515 exit(1);
516 }
517 memset(fp, 0, sizeof(struct cpu_family));
518
519 /*
520 * family_init() returns 1 if the struct has been filled with
521 * valid data, 0 if suppor for the cpu family isn't compiled
522 * into the emulator.
523 */
524 res = family_init(fp);
525 if (!res) {
526 free(fp);
527 return;
528 }
529 fp->arch = arch;
530 fp->next = NULL;
531
532 /* Add last in family chain: */
533 tmp = first_cpu_family;
534 if (tmp == NULL) {
535 first_cpu_family = fp;
536 } else {
537 while (tmp->next != NULL)
538 tmp = tmp->next;
539 tmp->next = fp;
540 }
541 }
542
543
544 /*
545 * cpu_family_ptr_by_number():
546 *
547 * Returns a pointer to a CPU family based on the ARCH_* integers.
548 */
549 struct cpu_family *cpu_family_ptr_by_number(int arch)
550 {
551 struct cpu_family *fp;
552 fp = first_cpu_family;
553
554 /* YUCK! This is too hardcoded! TODO */
555
556 while (fp != NULL) {
557 if (arch == fp->arch)
558 return fp;
559 fp = fp->next;
560 }
561
562 return NULL;
563 }
564
565
566 /*
567 * cpu_init():
568 *
569 * Should be called before any other cpu_*() function.
570 *
571 * TODO: Make this nicer by moving out the conditional stuff to
572 * an automagically generated file? Or a define in config.h?
573 */
574 void cpu_init(void)
575 {
576 /* Note: These are registered in alphabetic order. */
577
578 #ifdef ENABLE_ALPHA
579 add_cpu_family(alpha_cpu_family_init, ARCH_ALPHA);
580 #endif
581
582 #ifdef ENABLE_ARM
583 add_cpu_family(arm_cpu_family_init, ARCH_ARM);
584 #endif
585
586 #ifdef ENABLE_AVR
587 add_cpu_family(avr_cpu_family_init, ARCH_AVR);
588 #endif
589
590 #ifdef ENABLE_M88K
591 add_cpu_family(m88k_cpu_family_init, ARCH_M88K);
592 #endif
593
594 #ifdef ENABLE_MIPS
595 add_cpu_family(mips_cpu_family_init, ARCH_MIPS);
596 #endif
597
598 #ifdef ENABLE_PPC
599 add_cpu_family(ppc_cpu_family_init, ARCH_PPC);
600 #endif
601
602 #ifdef ENABLE_SH
603 add_cpu_family(sh_cpu_family_init, ARCH_SH);
604 #endif
605
606 #ifdef ENABLE_SPARC
607 add_cpu_family(sparc_cpu_family_init, ARCH_SPARC);
608 #endif
609 }
610

  ViewVC Help
Powered by ViewVC 1.1.26