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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 44 - (show annotations)
Mon Oct 8 16:22:56 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 14256 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1632 2007/09/11 21:46:35 debug Exp $
20070616	Implementing the MIPS32/64 revision 2 "ror" instruction.
20070617	Adding a struct for each physpage which keeps track of which
		ranges within that page (base offset, length) that are
		continuously translatable. When running with native code
		generation enabled (-b), a range is added after each read-
		ahead loop.
		Experimenting with using the physical program counter sample
		data (implemented 20070608) together with the "translatable
		range" information, to figure out which physical address ranges
		would be worth translating to native code (if the number of
		samples falling within a range is above a certain threshold).
20070618	Adding automagic building of .index comment files for
		src/file/, src/promemul/, src src/useremul/ as well.
		Adding a "has been translated" bit to the ranges, so that only
		not-yet-translated ranges will be sampled.
20070619	Moving src/cpu.c and src/memory_rw.c into src/cpus/,
		src/device.c into src/devices/, and src/machine.c into
		src/machines/.
		Creating a skeleton cc/ld native backend module; beginning on
		the function which will detect cc command line, etc.
20070620	Continuing on the native code generation infrastructure.
20070621	Moving src/x11.c and src/console.c into a new src/console/
		subdir (for everything that is console or framebuffer related).
		Moving src/symbol*.c into a new src/symbol/, which should
		contain anything that is symbol handling related.
20070624	Making the program counter sampling threshold a "settings
		variable" (sampling_threshold), i.e. it can now be changed
		during runtime.
		Switching the RELEASE notes format from plain text to HTML.
		If the TMPDIR environment variable is set, it is used instead
		of "/tmp" for temporary files.
		Continuing on the cc/ld backend: simple .c code is generated,
		the compiler and linker are called, etc.
		Adding detection of host architecture to the configure script
		(again), and adding icache invalidation support (only
		implemented for Alpha hosts so far).
20070625	Simplifying the program counter sampling mechanism.
20070626	Removing the cc/ld native code generation stuff, program
		counter sampling, etc; it would not have worked well in the
		general case.
20070627	Removing everything related to native code generation.
20070629	Removing the (practically unusable) support for multiple
		emulations. (The single emulation allowed now still supports
		multiple simultaneous machines, as before.)
		Beginning on PCCTWO and M88K interrupts.
20070723	Adding a dummy skeleton for emulation of M32R processors.
20070901	Fixing a warning found by "gcc version 4.3.0 20070817
		(experimental)" on amd64.
20070905	Removing some more traces of the old "multiple emulations"
		code.
		Also looking in /usr/local/include and /usr/local/lib for
		X11 libs, when running configure.
20070909	Minor updates to the guest OS install instructions, in
		preparation for the NetBSD 4.0 release.
20070918	More testing of NetBSD 4.0 RC1.

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

  ViewVC Help
Powered by ViewVC 1.1.26