/[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

Annotation of /upstream/0.4.6/src/cpu.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 43 - (hide annotations)
Mon Oct 8 16:22:43 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 14582 byte(s)
0.4.6
1 dpavlin 2 /*
2 dpavlin 34 * Copyright (C) 2005-2007 Anders Gavare. All rights reserved.
3 dpavlin 2 *
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 dpavlin 42 * $Id: cpu.c,v 1.389 2007/06/15 17:02:37 debug Exp $
29 dpavlin 2 *
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 dpavlin 22 #include <sys/mman.h>
37 dpavlin 2 #include <string.h>
38    
39     #include "cpu.h"
40     #include "machine.h"
41 dpavlin 12 #include "memory.h"
42 dpavlin 32 #include "settings.h"
43 dpavlin 42 #include "timer.h"
44 dpavlin 2
45    
46 dpavlin 34 extern size_t dyntrans_cache_size;
47     extern int native_code_translation_enabled;
48    
49 dpavlin 2 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 dpavlin 32 *
58     * If there was no match, NULL is returned. Otherwise, a pointer to an
59     * initialized cpu struct is returned.
60 dpavlin 2 */
61     struct cpu *cpu_new(struct memory *mem, struct machine *machine,
62     int cpu_id, char *name)
63     {
64 dpavlin 10 struct cpu *cpu;
65 dpavlin 2 struct cpu_family *fp;
66     char *cpu_type_name;
67 dpavlin 32 char tmpstr[30];
68 dpavlin 2
69     if (name == NULL) {
70     fprintf(stderr, "cpu_new(): cpu name = NULL?\n");
71     exit(1);
72     }
73    
74 dpavlin 42 CHECK_ALLOCATION(cpu_type_name = strdup(name));
75 dpavlin 2
76 dpavlin 12 cpu = zeroed_alloc(sizeof(struct cpu));
77 dpavlin 10
78 dpavlin 42 CHECK_ALLOCATION(cpu->path = malloc(strlen(machine->path) + 15));
79 dpavlin 34 snprintf(cpu->path, strlen(machine->path) + 15,
80     "%s.cpu[%i]", machine->path, cpu_id);
81    
82 dpavlin 32 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 dpavlin 10
90 dpavlin 42 cpu->sampling_paddr = zeroed_alloc(N_PADDR_SAMPLES * sizeof(uint64_t));
91    
92 dpavlin 32 /* 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 dpavlin 42 settings_add(cpu->settings, "running", 0, SETTINGS_TYPE_UINT8,
101 dpavlin 32 SETTINGS_FORMAT_YESNO, (void *) &cpu->running);
102    
103 dpavlin 12 cpu_create_or_reset_tc(cpu);
104    
105 dpavlin 2 fp = first_cpu_family;
106    
107     while (fp != NULL) {
108     if (fp->cpu_new != NULL) {
109 dpavlin 10 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 dpavlin 2 exit(1);
116     }
117 dpavlin 26 break;
118 dpavlin 2 }
119     }
120    
121     fp = fp->next;
122     }
123    
124 dpavlin 26 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 dpavlin 32 if (cpu->byte_order == EMUL_UNDEFINED_ENDIAN) {
132     fatal("\ncpu_new(): Internal bug: Endianness not set.\n");
133     exit(1);
134     }
135    
136 dpavlin 26 return cpu;
137 dpavlin 2 }
138    
139    
140     /*
141 dpavlin 32 * cpu_destroy():
142     *
143     * Destroy a cpu object.
144     */
145     void cpu_destroy(struct cpu *cpu)
146     {
147 dpavlin 42 if (cpu->sampling_timer != NULL)
148     timer_remove(cpu->sampling_timer);
149    
150 dpavlin 32 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 dpavlin 34 if (cpu->path != NULL)
159     free(cpu->path);
160    
161 dpavlin 32 /* 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 dpavlin 2 * 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 dpavlin 24 unsigned char *instr, int running, uint64_t addr)
193 dpavlin 2 {
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 dpavlin 24 running, addr);
200 dpavlin 2 }
201    
202    
203     /*
204     * cpu_register_dump():
205     *
206     * Dump cpu registers in a relatively readable format.
207     *
208 dpavlin 18 * 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 dpavlin 2 */
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 dpavlin 12 * 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 dpavlin 42 int show_symbolic_function_name = 1;
230 dpavlin 12 int i, n_args = -1;
231     char *symbol;
232     uint64_t offset;
233    
234 dpavlin 42 /* 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 dpavlin 12 if (cpu->machine->ncpus > 1)
240     fatal("cpu%i:\t", cpu->cpu_id);
241    
242 dpavlin 14 if (cpu->trace_tree_depth > 100)
243     cpu->trace_tree_depth = 100;
244 dpavlin 12 for (i=0; i<cpu->trace_tree_depth; i++)
245     fatal(" ");
246    
247 dpavlin 42 cpu->trace_tree_depth ++;
248    
249 dpavlin 12 fatal("<");
250     symbol = get_symbol_name_and_n_args(&cpu->machine->symbol_context,
251     f, &offset, &n_args);
252 dpavlin 42 if (symbol != NULL && show_symbolic_function_name)
253 dpavlin 12 fatal("%s", symbol);
254     else {
255     if (cpu->is_32bit)
256 dpavlin 24 fatal("0x%"PRIx32, (uint32_t) f);
257 dpavlin 12 else
258 dpavlin 24 fatal("0x%"PRIx64, (uint64_t) f);
259 dpavlin 12 }
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 dpavlin 24
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 dpavlin 12 }
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 dpavlin 34 size_t s = dyntrans_cache_size + DYNTRANS_CACHE_MARGIN;
300 dpavlin 12
301 dpavlin 34 if (cpu->translation_cache == NULL) {
302 dpavlin 24 cpu->translation_cache = zeroed_alloc(s);
303 dpavlin 22
304 dpavlin 34 if (native_code_translation_enabled) {
305     mprotect(cpu->translation_cache, s,
306     PROT_READ | PROT_WRITE | PROT_EXEC);
307     }
308     }
309    
310 dpavlin 12 /* 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 dpavlin 14 if (cpu->invalidate_code_translation != NULL)
322     cpu->invalidate_code_translation(cpu, 0, INVALIDATE_ALL);
323 dpavlin 12 }
324    
325    
326     /*
327 dpavlin 2 * cpu_dumpinfo():
328     *
329     * Dumps info about a CPU using debug(). "cpu0: CPUNAME, running" (or similar)
330 dpavlin 18 * is outputed, and it is up to CPU dependent code to complete the line.
331 dpavlin 2 */
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 dpavlin 22 int iadd = DEBUG_INDENTATION;
353 dpavlin 2
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 dpavlin 12 void cpu_run_deinit(struct machine *machine)
383 dpavlin 2 {
384     int te;
385    
386     /*
387 dpavlin 28 * 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 dpavlin 2 */
393 dpavlin 42 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 dpavlin 2 }
399    
400 dpavlin 28 if (machine->show_nr_of_instructions)
401 dpavlin 10 cpu_show_cycles(machine, 1);
402 dpavlin 2
403     fflush(stdout);
404     }
405    
406    
407     /*
408     * cpu_show_cycles():
409     *
410 dpavlin 32 * 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 dpavlin 2 */
413 dpavlin 10 void cpu_show_cycles(struct machine *machine, int forced)
414 dpavlin 2 {
415     uint64_t offset, pc;
416     char *symbol;
417 dpavlin 12 int64_t mseconds, ninstrs, is, avg;
418 dpavlin 2 struct timeval tv;
419 dpavlin 32 struct cpu *cpu = machine->cpus[machine->bootstrap_cpu];
420 dpavlin 2
421     static int64_t mseconds_last = 0;
422     static int64_t ninstrs_last = -1;
423    
424 dpavlin 32 pc = cpu->pc;
425 dpavlin 2
426     gettimeofday(&tv, NULL);
427 dpavlin 42 mseconds = (tv.tv_sec - cpu->starttime.tv_sec) * 1000
428     + (tv.tv_usec - cpu->starttime.tv_usec) / 1000;
429 dpavlin 2
430     if (mseconds == 0)
431     mseconds = 1;
432    
433     if (mseconds - mseconds_last == 0)
434     mseconds ++;
435    
436 dpavlin 42 ninstrs = cpu->ninstrs_since_gettimeofday;
437 dpavlin 2
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 dpavlin 42 printf("[ %"PRIi64" instrs", (int64_t) cpu->ninstrs);
443 dpavlin 2
444     /* Instructions per second, and average so far: */
445 dpavlin 12 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 dpavlin 2
452 dpavlin 32 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 dpavlin 2 symbol = get_symbol_name(&machine->symbol_context, pc, &offset);
459    
460 dpavlin 12 if (machine->ncpus == 1) {
461 dpavlin 32 if (cpu->is_32bit)
462 dpavlin 24 printf("; pc=0x%08"PRIx32, (uint32_t) pc);
463 dpavlin 12 else
464 dpavlin 24 printf("; pc=0x%016"PRIx64, (uint64_t) pc);
465 dpavlin 12 }
466 dpavlin 2
467 dpavlin 42 /* 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 dpavlin 10 if (symbol != NULL)
473     printf(" <%s>", symbol);
474     printf(" ]\n");
475 dpavlin 2
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 dpavlin 12 void cpu_run_init(struct machine *machine)
489 dpavlin 2 {
490 dpavlin 42 int i;
491     for (i=0; i<machine->ncpus; i++) {
492     struct cpu *cpu = machine->cpus[i];
493 dpavlin 2
494 dpavlin 42 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 dpavlin 2 }
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 dpavlin 42 CHECK_ALLOCATION(fp = malloc(sizeof(struct cpu_family)));
517 dpavlin 2 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 dpavlin 32 *
571 dpavlin 42 * 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 dpavlin 2 */
575     void cpu_init(void)
576     {
577 dpavlin 42 ADD_ALL_CPU_FAMILIES;
578 dpavlin 2 }
579    

  ViewVC Help
Powered by ViewVC 1.1.26