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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 39 - (show annotations)
Mon Oct 8 16:22:02 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 15000 byte(s)
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