/[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 38 - (show annotations)
Mon Oct 8 16:21:53 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.1515 2007/04/14 05:39:46 debug Exp $
20070324	Adding a "--debug" option to the configure script, to disable
		optimizations in unstable development builds.
		Moving out SCSI-specific stuff from diskimage.c into a new
		diskimage_scsicmd.c.
		Applying Hĺvard Eidnes' patch for SCSICDROM_READ_DISKINFO and
		SCSICDROM_READ_TRACKINFO. (Not really tested yet.)
		Implementing disk image "overlays" (to allow simple roll-back
		to previous disk state). Adding a 'V' disk flag for this, and
		updating the man page and misc.html.
20070325	Stability fix to cpu_dyntrans.c, when multiple physical pages
		share the same initial table entry. (The ppp == NULL check
		should be physpage_ofs == 0.) Bug found by analysing GXemul
		against a version patched for Godson.
		Fixing a second occurance of the same problem (also in
		cpu_dyntrans.c).
		Fixing a MAJOR physical page leak in cpu_dyntrans.c; pages
		weren't _added_ to the set of translated pages, they _replaced_
		all previous pages. It's amazing that this bug has been able
		to live for this long. (Triggered when emulating >128MB RAM.)
20070326	Removing the GDB debugging stub support; it was too hackish
		and ugly.
20070328	Moving around some native code generation skeleton code.
20070329	The -lm check in the configure script now also checks for sin()
		in addition to sqrt(). (Thanks to Nigel Horne for noticing that
		sqrt was not enough on Fedora Core 6.) (Not verified yet.)
20070330	Fixing an indexing bug in dev_sh4.c, found by using gcc version
		4.3.0 20070323.
20070331	Some more experimentation with native code generation.
20070404	Attempting to fix some more SH4 SCIF interrupt bugs; rewriting
		the SH interrupt assertion/deassertion code somewhat.
20070410	Splitting src/file.c into separate files in src/file/.
		Cleanup: Removing the dummy TS7200, Walnut, PB1000, and
		Meshcube emulation modes, and dev_epcom and dev_au1x00.
		Removing the experimental CHIP8/RCA180x code; it wasn't really
		working much lately, anyway. It was fun while it lasted.
		Also removing the experimental Transputer CPU support.
20070412	Moving the section about how the dynamic translation system
		works from intro.html to a separate translation.html file.
		Minor SH fixes; attempting to get OpenBSD/landisk to run
		without randomly bugging out, but no success yet.
20070413	SH SCI (serial bit interface) should now work together with a
		(new) RS5C313 clock device (for Landisk emulation).
20070414	Moving Redhat/MIPS down from supported to experimental, in
		guestoses.html.
		Preparing for a new release; doing some regression testing etc.

==============  RELEASE 0.4.5  ==============


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.374 2007/04/10 17:26:19 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_M68K
591 add_cpu_family(m68k_cpu_family_init, ARCH_M68K);
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