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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 43 - (show annotations)
Mon Oct 8 16:22:43 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 14582 byte(s)
0.4.6
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.389 2007/06/15 17:02:37 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 "settings.h"
43 #include "timer.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 CHECK_ALLOCATION(cpu_type_name = strdup(name));
75
76 cpu = zeroed_alloc(sizeof(struct cpu));
77
78 CHECK_ALLOCATION(cpu->path = malloc(strlen(machine->path) + 15));
79 snprintf(cpu->path, strlen(machine->path) + 15,
80 "%s.cpu[%i]", machine->path, cpu_id);
81
82 cpu->memory_rw = NULL;
83 cpu->name = cpu_type_name;
84 cpu->mem = mem;
85 cpu->machine = machine;
86 cpu->cpu_id = cpu_id;
87 cpu->byte_order = EMUL_UNDEFINED_ENDIAN;
88 cpu->running = 0;
89
90 cpu->sampling_paddr = zeroed_alloc(N_PADDR_SAMPLES * sizeof(uint64_t));
91
92 /* Create settings, and attach to the machine: */
93 cpu->settings = settings_new();
94 snprintf(tmpstr, sizeof(tmpstr), "cpu[%i]", cpu_id);
95 settings_add(machine->settings, tmpstr, 1,
96 SETTINGS_TYPE_SUBSETTINGS, 0, cpu->settings);
97
98 settings_add(cpu->settings, "name", 0, SETTINGS_TYPE_STRING,
99 SETTINGS_FORMAT_STRING, (void *) &cpu->name);
100 settings_add(cpu->settings, "running", 0, SETTINGS_TYPE_UINT8,
101 SETTINGS_FORMAT_YESNO, (void *) &cpu->running);
102
103 cpu_create_or_reset_tc(cpu);
104
105 fp = first_cpu_family;
106
107 while (fp != NULL) {
108 if (fp->cpu_new != NULL) {
109 if (fp->cpu_new(cpu, mem, machine, cpu_id,
110 cpu_type_name)) {
111 /* Sanity check: */
112 if (cpu->memory_rw == NULL) {
113 fatal("\ncpu_new(): memory_rw == "
114 "NULL\n");
115 exit(1);
116 }
117 break;
118 }
119 }
120
121 fp = fp->next;
122 }
123
124 if (fp == NULL) {
125 fatal("\ncpu_new(): unknown cpu type '%s'\n", cpu_type_name);
126 return NULL;
127 }
128
129 fp->init_tables(cpu);
130
131 if (cpu->byte_order == EMUL_UNDEFINED_ENDIAN) {
132 fatal("\ncpu_new(): Internal bug: Endianness not set.\n");
133 exit(1);
134 }
135
136 return cpu;
137 }
138
139
140 /*
141 * cpu_destroy():
142 *
143 * Destroy a cpu object.
144 */
145 void cpu_destroy(struct cpu *cpu)
146 {
147 if (cpu->sampling_timer != NULL)
148 timer_remove(cpu->sampling_timer);
149
150 settings_remove(cpu->settings, "name");
151 settings_remove(cpu->settings, "running");
152
153 /* Remove any remaining level-1 settings: */
154 settings_remove_all(cpu->settings);
155
156 settings_destroy(cpu->settings);
157
158 if (cpu->path != NULL)
159 free(cpu->path);
160
161 /* TODO: This assumes that zeroed_alloc() actually succeeded
162 with using mmap(), and not malloc()! */
163 munmap((void *)cpu, sizeof(struct cpu));
164 }
165
166
167 /*
168 * cpu_tlbdump():
169 *
170 * Called from the debugger to dump the TLB in a readable format.
171 * x is the cpu number to dump, or -1 to dump all CPUs.
172 *
173 * If rawflag is nonzero, then the TLB contents isn't formated nicely,
174 * just dumped.
175 */
176 void cpu_tlbdump(struct machine *m, int x, int rawflag)
177 {
178 if (m->cpu_family == NULL || m->cpu_family->tlbdump == NULL)
179 fatal("cpu_tlbdump(): NULL\n");
180 else
181 m->cpu_family->tlbdump(m, x, rawflag);
182 }
183
184
185 /*
186 * cpu_disassemble_instr():
187 *
188 * Convert an instruction word into human readable format, for instruction
189 * tracing.
190 */
191 int cpu_disassemble_instr(struct machine *m, struct cpu *cpu,
192 unsigned char *instr, int running, uint64_t addr)
193 {
194 if (m->cpu_family == NULL || m->cpu_family->disassemble_instr == NULL) {
195 fatal("cpu_disassemble_instr(): NULL\n");
196 return 0;
197 } else
198 return m->cpu_family->disassemble_instr(cpu, instr,
199 running, addr);
200 }
201
202
203 /*
204 * cpu_register_dump():
205 *
206 * Dump cpu registers in a relatively readable format.
207 *
208 * gprs: set to non-zero to dump GPRs. (CPU dependent.)
209 * coprocs: set bit 0..x to dump registers in coproc 0..x. (CPU dependent.)
210 */
211 void cpu_register_dump(struct machine *m, struct cpu *cpu,
212 int gprs, int coprocs)
213 {
214 if (m->cpu_family == NULL || m->cpu_family->register_dump == NULL)
215 fatal("cpu_register_dump(): NULL\n");
216 else
217 m->cpu_family->register_dump(cpu, gprs, coprocs);
218 }
219
220
221 /*
222 * cpu_functioncall_trace():
223 *
224 * This function should be called if machine->show_trace_tree is enabled, and
225 * a function call is being made. f contains the address of the function.
226 */
227 void cpu_functioncall_trace(struct cpu *cpu, uint64_t f)
228 {
229 int show_symbolic_function_name = 1;
230 int i, n_args = -1;
231 char *symbol;
232 uint64_t offset;
233
234 /* Special hack for M88K userspace: */
235 if (cpu->machine->arch == ARCH_M88K &&
236 !(cpu->cd.m88k.cr[M88K_CR_PSR] & M88K_PSR_MODE))
237 show_symbolic_function_name = 0;
238
239 if (cpu->machine->ncpus > 1)
240 fatal("cpu%i:\t", cpu->cpu_id);
241
242 if (cpu->trace_tree_depth > 100)
243 cpu->trace_tree_depth = 100;
244 for (i=0; i<cpu->trace_tree_depth; i++)
245 fatal(" ");
246
247 cpu->trace_tree_depth ++;
248
249 fatal("<");
250 symbol = get_symbol_name_and_n_args(&cpu->machine->symbol_context,
251 f, &offset, &n_args);
252 if (symbol != NULL && show_symbolic_function_name)
253 fatal("%s", symbol);
254 else {
255 if (cpu->is_32bit)
256 fatal("0x%"PRIx32, (uint32_t) f);
257 else
258 fatal("0x%"PRIx64, (uint64_t) f);
259 }
260 fatal("(");
261
262 if (cpu->machine->cpu_family->functioncall_trace != NULL)
263 cpu->machine->cpu_family->functioncall_trace(cpu, f, n_args);
264
265 fatal(")>\n");
266
267 #ifdef PRINT_MEMORY_CHECKSUM
268 /* Temporary hack for finding bugs: */
269 fatal("call chksum=%016"PRIx64"\n", memory_checksum(cpu->mem));
270 #endif
271 }
272
273
274 /*
275 * cpu_functioncall_trace_return():
276 *
277 * This function should be called if machine->show_trace_tree is enabled, and
278 * a function is being returned from.
279 *
280 * TODO: Print return value? This could be implemented similar to the
281 * cpu->functioncall_trace function call above.
282 */
283 void cpu_functioncall_trace_return(struct cpu *cpu)
284 {
285 cpu->trace_tree_depth --;
286 if (cpu->trace_tree_depth < 0)
287 cpu->trace_tree_depth = 0;
288 }
289
290
291 /*
292 * cpu_create_or_reset_tc():
293 *
294 * Create the translation cache in memory (ie allocate memory for it), if
295 * necessary, and then reset it to an initial state.
296 */
297 void cpu_create_or_reset_tc(struct cpu *cpu)
298 {
299 size_t s = dyntrans_cache_size + DYNTRANS_CACHE_MARGIN;
300
301 if (cpu->translation_cache == NULL) {
302 cpu->translation_cache = zeroed_alloc(s);
303
304 if (native_code_translation_enabled) {
305 mprotect(cpu->translation_cache, s,
306 PROT_READ | PROT_WRITE | PROT_EXEC);
307 }
308 }
309
310 /* Create an empty table at the beginning of the translation cache: */
311 memset(cpu->translation_cache, 0, sizeof(uint32_t)
312 * N_BASE_TABLE_ENTRIES);
313
314 cpu->translation_cache_cur_ofs =
315 N_BASE_TABLE_ENTRIES * sizeof(uint32_t);
316
317 /*
318 * There might be other translation pointers that still point to
319 * within the translation_cache region. Let's invalidate those too:
320 */
321 if (cpu->invalidate_code_translation != NULL)
322 cpu->invalidate_code_translation(cpu, 0, INVALIDATE_ALL);
323 }
324
325
326 /*
327 * cpu_dumpinfo():
328 *
329 * Dumps info about a CPU using debug(). "cpu0: CPUNAME, running" (or similar)
330 * is outputed, and it is up to CPU dependent code to complete the line.
331 */
332 void cpu_dumpinfo(struct machine *m, struct cpu *cpu)
333 {
334 debug("cpu%i: %s, %s", cpu->cpu_id, cpu->name,
335 cpu->running? "running" : "stopped");
336
337 if (m->cpu_family == NULL || m->cpu_family->dumpinfo == NULL)
338 fatal("cpu_dumpinfo(): NULL\n");
339 else
340 m->cpu_family->dumpinfo(cpu);
341 }
342
343
344 /*
345 * cpu_list_available_types():
346 *
347 * Print a list of available CPU types for each cpu family.
348 */
349 void cpu_list_available_types(void)
350 {
351 struct cpu_family *fp;
352 int iadd = DEBUG_INDENTATION;
353
354 fp = first_cpu_family;
355
356 if (fp == NULL) {
357 debug("No CPUs defined!\n");
358 return;
359 }
360
361 while (fp != NULL) {
362 debug("%s:\n", fp->name);
363 debug_indentation(iadd);
364 if (fp->list_available_types != NULL)
365 fp->list_available_types();
366 else
367 debug("(internal error: list_available_types"
368 " = NULL)\n");
369 debug_indentation(-iadd);
370
371 fp = fp->next;
372 }
373 }
374
375
376 /*
377 * cpu_run_deinit():
378 *
379 * Shuts down all CPUs in a machine when ending a simulation. (This function
380 * should only need to be called once for each machine.)
381 */
382 void cpu_run_deinit(struct machine *machine)
383 {
384 int te;
385
386 /*
387 * Two last ticks of every hardware device. This will allow e.g.
388 * framebuffers to draw the last updates to the screen before halting.
389 *
390 * TODO: This should be refactored when redesigning the mainbus
391 * concepts!
392 */
393 for (te=0; te<machine->tick_functions.n_entries; te++) {
394 machine->tick_functions.f[te](machine->cpus[0],
395 machine->tick_functions.extra[te]);
396 machine->tick_functions.f[te](machine->cpus[0],
397 machine->tick_functions.extra[te]);
398 }
399
400 if (machine->show_nr_of_instructions)
401 cpu_show_cycles(machine, 1);
402
403 fflush(stdout);
404 }
405
406
407 /*
408 * cpu_show_cycles():
409 *
410 * If show_nr_of_instructions is on, then print a line to stdout about how
411 * many instructions/cycles have been executed so far.
412 */
413 void cpu_show_cycles(struct machine *machine, int forced)
414 {
415 uint64_t offset, pc;
416 char *symbol;
417 int64_t mseconds, ninstrs, is, avg;
418 struct timeval tv;
419 struct cpu *cpu = machine->cpus[machine->bootstrap_cpu];
420
421 static int64_t mseconds_last = 0;
422 static int64_t ninstrs_last = -1;
423
424 pc = cpu->pc;
425
426 gettimeofday(&tv, NULL);
427 mseconds = (tv.tv_sec - cpu->starttime.tv_sec) * 1000
428 + (tv.tv_usec - cpu->starttime.tv_usec) / 1000;
429
430 if (mseconds == 0)
431 mseconds = 1;
432
433 if (mseconds - mseconds_last == 0)
434 mseconds ++;
435
436 ninstrs = cpu->ninstrs_since_gettimeofday;
437
438 /* RETURN here, unless show_nr_of_instructions (-N) is turned on: */
439 if (!machine->show_nr_of_instructions && !forced)
440 goto do_return;
441
442 printf("[ %"PRIi64" instrs", (int64_t) cpu->ninstrs);
443
444 /* Instructions per second, and average so far: */
445 is = 1000 * (ninstrs-ninstrs_last) / (mseconds-mseconds_last);
446 avg = (long long)1000 * ninstrs / mseconds;
447 if (is < 0)
448 is = 0;
449 if (avg < 0)
450 avg = 0;
451
452 if (cpu->has_been_idling) {
453 printf("; idling");
454 cpu->has_been_idling = 0;
455 } else
456 printf("; i/s=%"PRIi64" avg=%"PRIi64, is, avg);
457
458 symbol = get_symbol_name(&machine->symbol_context, pc, &offset);
459
460 if (machine->ncpus == 1) {
461 if (cpu->is_32bit)
462 printf("; pc=0x%08"PRIx32, (uint32_t) pc);
463 else
464 printf("; pc=0x%016"PRIx64, (uint64_t) pc);
465 }
466
467 /* Special hack for M88K userland: (Don't show symbols.) */
468 if (cpu->machine->arch == ARCH_M88K &&
469 !(cpu->cd.m88k.cr[M88K_CR_PSR] & M88K_PSR_MODE))
470 symbol = NULL;
471
472 if (symbol != NULL)
473 printf(" <%s>", symbol);
474 printf(" ]\n");
475
476 do_return:
477 ninstrs_last = ninstrs;
478 mseconds_last = mseconds;
479 }
480
481
482 /*
483 * cpu_run_init():
484 *
485 * Prepare to run instructions on all CPUs in this machine. (This function
486 * should only need to be called once for each machine.)
487 */
488 void cpu_run_init(struct machine *machine)
489 {
490 int i;
491 for (i=0; i<machine->ncpus; i++) {
492 struct cpu *cpu = machine->cpus[i];
493
494 cpu->ninstrs_flush = 0;
495 cpu->ninstrs = 0;
496 cpu->ninstrs_show = 0;
497
498 /* For performance measurement: */
499 gettimeofday(&cpu->starttime, NULL);
500 cpu->ninstrs_since_gettimeofday = 0;
501 }
502 }
503
504
505 /*
506 * add_cpu_family():
507 *
508 * Allocates a cpu_family struct and calls an init function for the
509 * family to fill in reasonable data and pointers.
510 */
511 static void add_cpu_family(int (*family_init)(struct cpu_family *), int arch)
512 {
513 struct cpu_family *fp, *tmp;
514 int res;
515
516 CHECK_ALLOCATION(fp = malloc(sizeof(struct cpu_family)));
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 * This function calls add_cpu_family() for each processor architecture.
572 * ADD_ALL_CPU_FAMILIES is defined in the config.h file generated by the
573 * configure script.
574 */
575 void cpu_init(void)
576 {
577 ADD_ALL_CPU_FAMILIES;
578 }
579

  ViewVC Help
Powered by ViewVC 1.1.26