--- trunk/src/cpu.c 2007/10/08 16:17:48 2 +++ trunk/src/cpu.c 2007/10/08 16:22:32 42 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Anders Gavare. All rights reserved. + * Copyright (C) 2005-2007 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu.c,v 1.291 2005/03/13 09:36:08 debug Exp $ + * $Id: cpu.c,v 1.389 2007/06/15 17:02:37 debug Exp $ * * Common routines for CPU emulation. (Not specific to any CPU type.) */ @@ -33,16 +33,18 @@ #include #include #include +#include #include #include "cpu.h" #include "machine.h" -#include "misc.h" +#include "memory.h" +#include "settings.h" +#include "timer.h" -extern int quiet_mode; -extern int show_opcode_statistics; - +extern size_t dyntrans_cache_size; +extern int native_code_translation_enabled; static struct cpu_family *first_cpu_family = NULL; @@ -52,61 +54,113 @@ * * Create a new cpu object. Each family is tried in sequence until a * CPU family recognizes the cpu_type_name. + * + * If there was no match, NULL is returned. Otherwise, a pointer to an + * initialized cpu struct is returned. */ struct cpu *cpu_new(struct memory *mem, struct machine *machine, int cpu_id, char *name) { - struct cpu *c; + struct cpu *cpu; struct cpu_family *fp; char *cpu_type_name; + char tmpstr[30]; if (name == NULL) { fprintf(stderr, "cpu_new(): cpu name = NULL?\n"); exit(1); } - cpu_type_name = strdup(name); - if (cpu_type_name == NULL) { - fprintf(stderr, "cpu_new(): out of memory\n"); - exit(1); - } + CHECK_ALLOCATION(cpu_type_name = strdup(name)); + + cpu = zeroed_alloc(sizeof(struct cpu)); + + CHECK_ALLOCATION(cpu->path = malloc(strlen(machine->path) + 15)); + snprintf(cpu->path, strlen(machine->path) + 15, + "%s.cpu[%i]", machine->path, cpu_id); + + cpu->memory_rw = NULL; + cpu->name = cpu_type_name; + cpu->mem = mem; + cpu->machine = machine; + cpu->cpu_id = cpu_id; + cpu->byte_order = EMUL_UNDEFINED_ENDIAN; + cpu->running = 0; + + cpu->sampling_paddr = zeroed_alloc(N_PADDR_SAMPLES * sizeof(uint64_t)); + + /* Create settings, and attach to the machine: */ + cpu->settings = settings_new(); + snprintf(tmpstr, sizeof(tmpstr), "cpu[%i]", cpu_id); + settings_add(machine->settings, tmpstr, 1, + SETTINGS_TYPE_SUBSETTINGS, 0, cpu->settings); + + settings_add(cpu->settings, "name", 0, SETTINGS_TYPE_STRING, + SETTINGS_FORMAT_STRING, (void *) &cpu->name); + settings_add(cpu->settings, "running", 0, SETTINGS_TYPE_UINT8, + SETTINGS_FORMAT_YESNO, (void *) &cpu->running); + + cpu_create_or_reset_tc(cpu); fp = first_cpu_family; while (fp != NULL) { if (fp->cpu_new != NULL) { - c = fp->cpu_new(mem, machine, cpu_id, cpu_type_name); - if (c != NULL) { - /* Some sanity-checks: */ - if (c->memory_rw == NULL) { - fatal("No memory_rw?\n"); + if (fp->cpu_new(cpu, mem, machine, cpu_id, + cpu_type_name)) { + /* Sanity check: */ + if (cpu->memory_rw == NULL) { + fatal("\ncpu_new(): memory_rw == " + "NULL\n"); exit(1); } - - return c; + break; } } fp = fp->next; } - fprintf(stderr, "cpu_new(): unknown cpu type '%s'\n", cpu_type_name); - exit(1); + if (fp == NULL) { + fatal("\ncpu_new(): unknown cpu type '%s'\n", cpu_type_name); + return NULL; + } + + fp->init_tables(cpu); + + if (cpu->byte_order == EMUL_UNDEFINED_ENDIAN) { + fatal("\ncpu_new(): Internal bug: Endianness not set.\n"); + exit(1); + } + + return cpu; } /* - * cpu_show_full_statistics(): + * cpu_destroy(): * - * Show detailed statistics on opcode usage on each cpu. + * Destroy a cpu object. */ -void cpu_show_full_statistics(struct machine *m) +void cpu_destroy(struct cpu *cpu) { - if (m->cpu_family == NULL || - m->cpu_family->show_full_statistics == NULL) - fatal("cpu_show_full_statistics(): NULL\n"); - else - m->cpu_family->show_full_statistics(m); + if (cpu->sampling_timer != NULL) + timer_remove(cpu->sampling_timer); + + settings_remove(cpu->settings, "name"); + settings_remove(cpu->settings, "running"); + + /* Remove any remaining level-1 settings: */ + settings_remove_all(cpu->settings); + + settings_destroy(cpu->settings); + + if (cpu->path != NULL) + free(cpu->path); + + /* TODO: This assumes that zeroed_alloc() actually succeeded + with using mmap(), and not malloc()! */ + munmap((void *)cpu, sizeof(struct cpu)); } @@ -129,36 +183,20 @@ /* - * cpu_register_match(): - * - * Used by the debugger. - */ -void cpu_register_match(struct machine *m, char *name, - int writeflag, uint64_t *valuep, int *match_register) -{ - if (m->cpu_family == NULL || m->cpu_family->register_match == NULL) - fatal("cpu_register_match(): NULL\n"); - else - m->cpu_family->register_match(m, name, writeflag, - valuep, match_register); -} - - -/* * cpu_disassemble_instr(): * * Convert an instruction word into human readable format, for instruction * tracing. */ int cpu_disassemble_instr(struct machine *m, struct cpu *cpu, - unsigned char *instr, int running, uint64_t addr, int bintrans) + unsigned char *instr, int running, uint64_t addr) { if (m->cpu_family == NULL || m->cpu_family->disassemble_instr == NULL) { fatal("cpu_disassemble_instr(): NULL\n"); return 0; } else return m->cpu_family->disassemble_instr(cpu, instr, - running, addr, bintrans); + running, addr); } @@ -167,8 +205,8 @@ * * Dump cpu registers in a relatively readable format. * - * gprs: set to non-zero to dump GPRs. (CPU dependant.) - * coprocs: set bit 0..x to dump registers in coproc 0..x. (CPU dependant.) + * gprs: set to non-zero to dump GPRs. (CPU dependent.) + * coprocs: set bit 0..x to dump registers in coproc 0..x. (CPU dependent.) */ void cpu_register_dump(struct machine *m, struct cpu *cpu, int gprs, int coprocs) @@ -181,54 +219,107 @@ /* - * cpu_interrupt(): + * cpu_functioncall_trace(): * - * Assert an interrupt. - * Return value is 1 if the interrupt was asserted, 0 otherwise. + * This function should be called if machine->show_trace_tree is enabled, and + * a function call is being made. f contains the address of the function. */ -int cpu_interrupt(struct cpu *cpu, uint64_t irq_nr) +void cpu_functioncall_trace(struct cpu *cpu, uint64_t f) { - if (cpu->machine->cpu_family == NULL || - cpu->machine->cpu_family->interrupt == NULL) { - fatal("cpu_interrupt(): NULL\n"); - return 0; - } else - return cpu->machine->cpu_family->interrupt(cpu, irq_nr); + int show_symbolic_function_name = 1; + int i, n_args = -1; + char *symbol; + uint64_t offset; + + /* Special hack for M88K userspace: */ + if (cpu->machine->arch == ARCH_M88K && + !(cpu->cd.m88k.cr[M88K_CR_PSR] & M88K_PSR_MODE)) + show_symbolic_function_name = 0; + + if (cpu->machine->ncpus > 1) + fatal("cpu%i:\t", cpu->cpu_id); + + if (cpu->trace_tree_depth > 100) + cpu->trace_tree_depth = 100; + for (i=0; itrace_tree_depth; i++) + fatal(" "); + + cpu->trace_tree_depth ++; + + fatal("<"); + symbol = get_symbol_name_and_n_args(&cpu->machine->symbol_context, + f, &offset, &n_args); + if (symbol != NULL && show_symbolic_function_name) + fatal("%s", symbol); + else { + if (cpu->is_32bit) + fatal("0x%"PRIx32, (uint32_t) f); + else + fatal("0x%"PRIx64, (uint64_t) f); + } + fatal("("); + + if (cpu->machine->cpu_family->functioncall_trace != NULL) + cpu->machine->cpu_family->functioncall_trace(cpu, f, n_args); + + fatal(")>\n"); + +#ifdef PRINT_MEMORY_CHECKSUM + /* Temporary hack for finding bugs: */ + fatal("call chksum=%016"PRIx64"\n", memory_checksum(cpu->mem)); +#endif } /* - * cpu_interrupt_ack(): + * cpu_functioncall_trace_return(): + * + * This function should be called if machine->show_trace_tree is enabled, and + * a function is being returned from. * - * Acknowledge an interrupt. - * Return value is 1 if the interrupt was deasserted, 0 otherwise. + * TODO: Print return value? This could be implemented similar to the + * cpu->functioncall_trace function call above. */ -int cpu_interrupt_ack(struct cpu *cpu, uint64_t irq_nr) +void cpu_functioncall_trace_return(struct cpu *cpu) { - if (cpu->machine->cpu_family == NULL || - cpu->machine->cpu_family->interrupt_ack == NULL) { - /* debug("cpu_interrupt_ack(): NULL\n"); */ - return 0; - } else - return cpu->machine->cpu_family->interrupt_ack(cpu, irq_nr); + cpu->trace_tree_depth --; + if (cpu->trace_tree_depth < 0) + cpu->trace_tree_depth = 0; } /* - * cpu_run(): - * - * Run instructions on all CPUs in this machine, for a "medium duration" - * (or until all CPUs have halted). + * cpu_create_or_reset_tc(): * - * Return value is 1 if anything happened, 0 if all CPUs are stopped. + * Create the translation cache in memory (ie allocate memory for it), if + * necessary, and then reset it to an initial state. */ -int cpu_run(struct emul *emul, struct machine *m) +void cpu_create_or_reset_tc(struct cpu *cpu) { - if (m->cpu_family == NULL || m->cpu_family->run == NULL) { - fatal("cpu_run(): NULL\n"); - return 0; - } else - return m->cpu_family->run(emul, m); + size_t s = dyntrans_cache_size + DYNTRANS_CACHE_MARGIN; + + if (cpu->translation_cache == NULL) { + cpu->translation_cache = zeroed_alloc(s); + + if (native_code_translation_enabled) { + mprotect(cpu->translation_cache, s, + PROT_READ | PROT_WRITE | PROT_EXEC); + } + } + + /* Create an empty table at the beginning of the translation cache: */ + memset(cpu->translation_cache, 0, sizeof(uint32_t) + * N_BASE_TABLE_ENTRIES); + + cpu->translation_cache_cur_ofs = + N_BASE_TABLE_ENTRIES * sizeof(uint32_t); + + /* + * There might be other translation pointers that still point to + * within the translation_cache region. Let's invalidate those too: + */ + if (cpu->invalidate_code_translation != NULL) + cpu->invalidate_code_translation(cpu, 0, INVALIDATE_ALL); } @@ -236,7 +327,7 @@ * cpu_dumpinfo(): * * Dumps info about a CPU using debug(). "cpu0: CPUNAME, running" (or similar) - * is outputed, and it is up to CPU dependant code to complete the line. + * is outputed, and it is up to CPU dependent code to complete the line. */ void cpu_dumpinfo(struct machine *m, struct cpu *cpu) { @@ -258,7 +349,7 @@ void cpu_list_available_types(void) { struct cpu_family *fp; - int iadd = 4; + int iadd = DEBUG_INDENTATION; fp = first_cpu_family; @@ -288,30 +379,26 @@ * Shuts down all CPUs in a machine when ending a simulation. (This function * should only need to be called once for each machine.) */ -void cpu_run_deinit(struct emul *emul, struct machine *machine) +void cpu_run_deinit(struct machine *machine) { int te; /* - * Two last ticks of every hardware device. This will allow - * framebuffers to draw the last updates to the screen before - * halting. + * Two last ticks of every hardware device. This will allow e.g. + * framebuffers to draw the last updates to the screen before halting. + * + * TODO: This should be refactored when redesigning the mainbus + * concepts! */ - for (te=0; ten_tick_entries; te++) { - machine->tick_func[te](machine->cpus[0], - machine->tick_extra[te]); - machine->tick_func[te](machine->cpus[0], - machine->tick_extra[te]); + for (te=0; tetick_functions.n_entries; te++) { + machine->tick_functions.f[te](machine->cpus[0], + machine->tick_functions.extra[te]); + machine->tick_functions.f[te](machine->cpus[0], + machine->tick_functions.extra[te]); } - debug("cpu_run_deinit(): All CPUs halted.\n"); - - if (machine->show_nr_of_instructions || !quiet_mode) - cpu_show_cycles(machine, &machine->starttime, - machine->ncycles, 1); - - if (show_opcode_statistics) - cpu_show_full_statistics(machine); + if (machine->show_nr_of_instructions) + cpu_show_cycles(machine, 1); fflush(stdout); } @@ -320,40 +407,25 @@ /* * cpu_show_cycles(): * - * If automatic adjustment of clock interrupts is turned on, then recalculate - * emulated_hz. Also, if show_nr_of_instructions is on, then print a - * line to stdout about how many instructions/cycles have been executed so - * far. + * If show_nr_of_instructions is on, then print a line to stdout about how + * many instructions/cycles have been executed so far. */ -void cpu_show_cycles(struct machine *machine, - struct timeval *starttime, int64_t ncycles, int forced) +void cpu_show_cycles(struct machine *machine, int forced) { uint64_t offset, pc; - int is_32bit = 0, instrs_per_cycle; char *symbol; - int64_t mseconds, ninstrs; + int64_t mseconds, ninstrs, is, avg; struct timeval tv; - int h, m, s, ms, d; + struct cpu *cpu = machine->cpus[machine->bootstrap_cpu]; static int64_t mseconds_last = 0; static int64_t ninstrs_last = -1; - if (machine->arch != ARCH_MIPS) { - fatal("cpu_show_cycles(): not yet for !MIPS\n"); - return; - } - - if (machine->cpus[machine->bootstrap_cpu]->cd.mips.cpu_type.isa_level - < 3 || machine->cpus[machine->bootstrap_cpu]->cd.mips.cpu_type. - isa_level == 32) - is_32bit = 1; - pc = machine->cpus[machine->bootstrap_cpu]->pc; - instrs_per_cycle = machine->cpus[machine->bootstrap_cpu]-> - cd.mips.cpu_type.instrs_per_cycle; + pc = cpu->pc; gettimeofday(&tv, NULL); - mseconds = (tv.tv_sec - starttime->tv_sec) * 1000 - + (tv.tv_usec - starttime->tv_usec) / 1000; + mseconds = (tv.tv_sec - cpu->starttime.tv_sec) * 1000 + + (tv.tv_usec - cpu->starttime.tv_usec) / 1000; if (mseconds == 0) mseconds = 1; @@ -361,73 +433,45 @@ if (mseconds - mseconds_last == 0) mseconds ++; - ninstrs = ncycles * instrs_per_cycle; - - if (machine->automatic_clock_adjustment) { - static int first_adjustment = 1; - - /* Current nr of cycles per second: */ - int64_t cur_cycles_per_second = 1000 * - (ninstrs-ninstrs_last) / (mseconds-mseconds_last) - / instrs_per_cycle; - - if (cur_cycles_per_second < 1000000) - cur_cycles_per_second = 1000000; - - if (first_adjustment) { - machine->emulated_hz = cur_cycles_per_second; - first_adjustment = 0; - } else { - machine->emulated_hz = (15 * machine->emulated_hz + - cur_cycles_per_second) / 16; - } - - debug("[ updating emulated_hz to %lli Hz ]\n", - (long long)machine->emulated_hz); - } - + ninstrs = cpu->ninstrs_since_gettimeofday; /* RETURN here, unless show_nr_of_instructions (-N) is turned on: */ if (!machine->show_nr_of_instructions && !forced) goto do_return; - - printf("[ "); - - if (!machine->automatic_clock_adjustment) { - d = machine->emulated_hz / 1000; - if (d < 1) - d = 1; - ms = ncycles / d; - h = ms / 3600000; - ms -= 3600000 * h; - m = ms / 60000; - ms -= 60000 * m; - s = ms / 1000; - ms -= 1000 * s; - - printf("emulated time = %02i:%02i:%02i.%03i; ", h, m, s, ms); - } - - printf("cycles=%lli", (long long) ncycles); - - if (instrs_per_cycle > 1) - printf(" (%lli instrs)", (long long) ninstrs); + printf("[ %"PRIi64" instrs", (int64_t) cpu->ninstrs); /* Instructions per second, and average so far: */ - printf("; i/s=%lli avg=%lli", - (long long) ((long long)1000 * (ninstrs-ninstrs_last) - / (mseconds-mseconds_last)), - (long long) ((long long)1000 * ninstrs / mseconds)); + is = 1000 * (ninstrs-ninstrs_last) / (mseconds-mseconds_last); + avg = (long long)1000 * ninstrs / mseconds; + if (is < 0) + is = 0; + if (avg < 0) + avg = 0; + + if (cpu->has_been_idling) { + printf("; idling"); + cpu->has_been_idling = 0; + } else + printf("; i/s=%"PRIi64" avg=%"PRIi64, is, avg); symbol = get_symbol_name(&machine->symbol_context, pc, &offset); - if (is_32bit) - printf("; pc=%08x", (int)pc); - else - printf("; pc=%016llx", (long long)pc); + if (machine->ncpus == 1) { + if (cpu->is_32bit) + printf("; pc=0x%08"PRIx32, (uint32_t) pc); + else + printf("; pc=0x%016"PRIx64, (uint64_t) pc); + } + + /* Special hack for M88K userland: (Don't show symbols.) */ + if (cpu->machine->arch == ARCH_M88K && + !(cpu->cd.m88k.cr[M88K_CR_PSR] & M88K_PSR_MODE)) + symbol = NULL; - printf(" <%s> ]\n", symbol? symbol : "no symbol"); + if (symbol != NULL) + printf(" <%s>", symbol); + printf(" ]\n"); do_return: ninstrs_last = ninstrs; @@ -441,39 +485,20 @@ * Prepare to run instructions on all CPUs in this machine. (This function * should only need to be called once for each machine.) */ -void cpu_run_init(struct emul *emul, struct machine *machine) +void cpu_run_init(struct machine *machine) { - int ncpus = machine->ncpus; - int te; - - machine->a_few_cycles = 1048576; - machine->ncycles_flush = 0; - machine->ncycles = 0; - machine->ncycles_show = 0; - - /* - * Instead of doing { one cycle, check hardware ticks }, we - * can do { n cycles, check hardware ticks }, as long as - * n is at most as much as the lowest number of cycles/tick - * for any hardware device. - */ - for (te=0; ten_tick_entries; te++) { - if (machine->ticks_reset_value[te] < machine->a_few_cycles) - machine->a_few_cycles = machine->ticks_reset_value[te]; + int i; + for (i=0; incpus; i++) { + struct cpu *cpu = machine->cpus[i]; + + cpu->ninstrs_flush = 0; + cpu->ninstrs = 0; + cpu->ninstrs_show = 0; + + /* For performance measurement: */ + gettimeofday(&cpu->starttime, NULL); + cpu->ninstrs_since_gettimeofday = 0; } - - machine->a_few_cycles >>= 1; - if (machine->a_few_cycles < 1) - machine->a_few_cycles = 1; - - if (ncpus > 1 && machine->max_random_cycles_per_chunk == 0) - machine->a_few_cycles = 1; - - /* debug("cpu_run_init(): a_few_cycles = %i\n", - machine->a_few_cycles); */ - - /* For performance measurement: */ - gettimeofday(&machine->starttime, NULL); } @@ -488,11 +513,7 @@ struct cpu_family *fp, *tmp; int res; - fp = malloc(sizeof(struct cpu_family)); - if (fp == NULL) { - fprintf(stderr, "add_cpu_family(): out of memory\n"); - exit(1); - } + CHECK_ALLOCATION(fp = malloc(sizeof(struct cpu_family))); memset(fp, 0, sizeof(struct cpu_family)); /* @@ -546,15 +567,13 @@ * cpu_init(): * * Should be called before any other cpu_*() function. + * + * This function calls add_cpu_family() for each processor architecture. + * ADD_ALL_CPU_FAMILIES is defined in the config.h file generated by the + * configure script. */ void cpu_init(void) { - /* Note: These are registered in alphabetic order. */ - add_cpu_family(alpha_cpu_family_init, ARCH_ALPHA); - add_cpu_family(hppa_cpu_family_init, ARCH_HPPA); - add_cpu_family(mips_cpu_family_init, ARCH_MIPS); - add_cpu_family(ppc_cpu_family_init, ARCH_PPC); - add_cpu_family(sparc_cpu_family_init, ARCH_SPARC); - add_cpu_family(urisc_cpu_family_init, ARCH_URISC); + ADD_ALL_CPU_FAMILIES; }