--- upstream/dynamips-0.2.5/mips64.c 2007/10/06 16:01:44 1 +++ trunk/mips64.c 2007/10/06 16:45:40 12 @@ -1,5 +1,5 @@ /* - * Cisco 7200 (Predator) simulation platform. + * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * XXX TODO: proper context save/restore for CPUs. @@ -16,10 +16,11 @@ #include #include "rbtree.h" -#include "mips64.h" -#include "dynamips.h" -#include "cp0.h" +#include "cpu.h" +#include "mips64_mem.h" #include "mips64_exec.h" +#include "mips64_jit.h" +#include "dynamips.h" #include "memory.h" #include "device.h" @@ -67,10 +68,8 @@ memset(&cpu->cp0.tlb,0,MIPS64_TLB_MAX_ENTRIES*sizeof(tlb_entry_t)); /* Restart the MTS subsystem */ - mts_shutdown(cpu); - mts64_init(cpu); - mts_init_memop_vectors(cpu); - cpu->mts_rebuild(cpu); + mips64_set_addr_mode(cpu,32/*64*/); /* zzz */ + cpu->gen->mts_rebuild(cpu->gen); /* Flush JIT structures */ mips64_jit_flush(cpu,0); @@ -80,14 +79,13 @@ /* Initialize a MIPS64 processor */ int mips64_init(cpu_mips_t *cpu) { - cpu->state = MIPS_CPU_SUSPENDED; cpu->addr_bus_mask = 0xFFFFFFFFFFFFFFFFULL; cpu->cp0.reg[MIPS_CP0_PRID] = MIPS_PRID_R4600; cpu->cp0.tlb_entries = MIPS64_TLB_STD_ENTRIES; /* Initialize idle timer */ - cpu->idle_max = 1500; - cpu->idle_sleep_time = 50000; + cpu->gen->idle_max = 1500; + cpu->gen->idle_sleep_time = 30000; /* Timer IRQ parameters (default frequency: 250 Hz <=> 4ms period) */ cpu->timer_irq_check_itv = 1000; @@ -96,12 +94,25 @@ /* Enable fast memory operations */ cpu->fast_memop = TRUE; + /* Enable/Disable direct block jump */ + cpu->exec_blk_direct_jump = cpu->vm->exec_blk_direct_jump; + /* Create the IRQ lock (for non-jit architectures) */ pthread_mutex_init(&cpu->irq_lock,NULL); /* Idle loop mutex and condition */ - pthread_mutex_init(&cpu->idle_mutex,NULL); - pthread_cond_init(&cpu->idle_cond,NULL); + pthread_mutex_init(&cpu->gen->idle_mutex,NULL); + pthread_cond_init(&cpu->gen->idle_cond,NULL); + + /* Set the CPU methods */ + cpu->gen->reg_set = (void *)mips64_reg_set; + cpu->gen->reg_dump = (void *)mips64_dump_regs; + cpu->gen->mmu_dump = (void *)mips64_tlb_dump; + cpu->gen->mmu_raw_dump = (void *)mips64_tlb_raw_dump; + cpu->gen->add_breakpoint = (void *)mips64_add_breakpoint; + cpu->gen->remove_breakpoint = (void *)mips64_remove_breakpoint; + cpu->gen->set_idle_pc = (void *)mips64_set_idle_pc; + cpu->gen->get_idling_pc = (void *)mips64_get_idling_pc; /* Set the startup parameters */ mips64_reset(cpu); @@ -112,9 +123,8 @@ void mips64_delete(cpu_mips_t *cpu) { if (cpu) { - mts_shutdown(cpu); + mips64_mem_shutdown(cpu); mips64_jit_shutdown(cpu); - free(cpu); } } @@ -127,25 +137,10 @@ cpu->cp0.tlb_entries = MIPS64_TLB_MAX_ENTRIES; } -/* Virtual idle loop */ -void mips64_idle_loop(cpu_mips_t *cpu) -{ - struct timespec t_spc; - m_tmcnt_t expire; - - expire = m_gettime_usec() + cpu->idle_sleep_time; - - pthread_mutex_lock(&cpu->idle_mutex); - t_spc.tv_sec = expire / 1000000; - t_spc.tv_nsec = (expire % 1000000) * 1000; - pthread_cond_timedwait(&cpu->idle_cond,&cpu->idle_mutex,&t_spc); - pthread_mutex_unlock(&cpu->idle_mutex); -} - -/* Break idle wait state */ -void mips64_idle_break_wait(cpu_mips_t *cpu) +/* Set idle PC value */ +void mips64_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr) { - pthread_cond_signal(&cpu->idle_cond); + CPU_MIPS64(cpu)->idle_pc = addr; } /* Timer IRQ */ @@ -162,7 +157,7 @@ threshold = cpu->timer_irq_freq * 10; expire = m_gettime_usec() + interval; - while(cpu->state != MIPS_CPU_HALTED) { + while(cpu->gen->state != CPU_STATE_HALTED) { pthread_mutex_lock(&umutex); t_spc.tv_sec = expire / 1000000; t_spc.tv_nsec = (expire % 1000000) * 1000; @@ -170,7 +165,7 @@ pthread_mutex_unlock(&umutex); if (likely(!cpu->irq_disable) && - likely(cpu->state == MIPS_CPU_RUNNING)) + likely(cpu->gen->state == CPU_STATE_RUNNING)) { cpu->timer_irq_pending++; @@ -192,26 +187,28 @@ return NULL; } +#define IDLE_HASH_SIZE 8192 + /* Idle PC hash item */ -struct mips64_idle_pc { +struct mips64_idle_pc_hash { m_uint64_t pc; u_int count; - struct mips64_idle_pc *next; + struct mips64_idle_pc_hash *next; }; -#define IDLE_HASH_SIZE 8192 -#define IDLE_MAX_RES 10 - /* Determine an "idling" PC */ -int mips64_get_idling_pc(cpu_mips_t *cpu) +int mips64_get_idling_pc(cpu_gen_t *cpu) { - struct mips64_idle_pc *res[IDLE_MAX_RES]; - struct mips64_idle_pc **pc_hash,*p; + cpu_mips_t *mcpu = CPU_MIPS64(cpu); + struct mips64_idle_pc_hash **pc_hash,*p; + struct cpu_idle_pc *res; u_int h_index,res_count; m_uint64_t cur_pc; int i; - if (cpu->idle_pc != 0) { + cpu->idle_pc_prop_count = 0; + + if (mcpu->idle_pc != 0) { printf("\nYou already use an idle PC, using the calibration would give " "incorrect results.\n"); return(-1); @@ -219,14 +216,14 @@ printf("\nPlease wait while gathering statistics...\n"); - pc_hash = calloc(IDLE_HASH_SIZE,sizeof(struct mips64_idle_pc *)); + pc_hash = calloc(IDLE_HASH_SIZE,sizeof(struct mips64_idle_pc_hash *)); /* Disable IRQ */ - cpu->irq_disable = TRUE; + mcpu->irq_disable = TRUE; /* Take 1000 measures, each mesure every 10ms */ for(i=0;i<1000;i++) { - cur_pc = cpu->pc; + cur_pc = mcpu->pc; h_index = (cur_pc >> 2) & (IDLE_HASH_SIZE-1); for(p=pc_hash[h_index];p;p=p->next) @@ -248,37 +245,82 @@ } /* Select PCs */ - memset(res,0,sizeof(res)); - for(i=0,res_count=0;inext) if ((p->count >= 20) && (p->count <= 80)) { - res[res_count++] = p; + res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; + + res->pc = p->pc; + res->count = p->count; - if (res_count >= IDLE_MAX_RES) + if (cpu->idle_pc_prop_count >= CPU_IDLE_PC_MAX_RES) goto done; } } done: /* Set idle PC */ - if (res_count) { + if (cpu->idle_pc_prop_count) { printf("Done. Suggested idling PC:\n"); - for(i=0;ipc,res[i]->count); - + for(i=0;iidle_pc_prop_count;i++) { + printf(" 0x%llx (count=%u)\n", + cpu->idle_pc_prop[i].pc, + cpu->idle_pc_prop[i].count); + } + printf("Restart the emulator with \"--idle-pc=0x%llx\" (for example)\n", - res[0]->pc); + cpu->idle_pc_prop[0].pc); } else { printf("Done. No suggestion for idling PC\n"); + + for(i=0;inext) { + printf(" 0x%16.16llx (%3u)\n",p->pc,p->count); + + if (cpu->idle_pc_prop_count < CPU_IDLE_PC_MAX_RES) { + res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; + + res->pc = p->pc; + res->count = p->count; + } + } + + printf("\n"); } /* Re-enable IRQ */ - cpu->irq_disable = FALSE; + mcpu->irq_disable = FALSE; return(0); } +/* Set an IRQ (VM IRQ standard routing) */ +void mips64_vm_set_irq(vm_instance_t *vm,u_int irq) +{ + cpu_mips_t *boot_cpu; + + boot_cpu = CPU_MIPS64(vm->boot_cpu); + + if (boot_cpu->irq_disable) { + boot_cpu->irq_pending = 0; + return; + } + + mips64_set_irq(boot_cpu,irq); + + if (boot_cpu->irq_idle_preempt[irq]) + cpu_idle_break_wait(vm->boot_cpu); +} + +/* Clear an IRQ (VM IRQ standard routing) */ +void mips64_vm_clear_irq(vm_instance_t *vm,u_int irq) +{ + cpu_mips_t *boot_cpu; + + boot_cpu = CPU_MIPS64(vm->boot_cpu); + mips64_clear_irq(boot_cpu,irq); +} + /* Update the IRQ flag (inline) */ static forced_inline int mips64_update_irq_flag_fast(cpu_mips_t *cpu) { @@ -420,7 +462,7 @@ cpu->gpr[MIPS_GPR_A0], cpu->gpr[MIPS_GPR_A1], cpu->gpr[MIPS_GPR_A2], cpu->gpr[MIPS_GPR_A3]); #endif - + /* XXX TODO: Branch Delay slot */ mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_SYSCALL,0); } @@ -429,7 +471,7 @@ fastcall void mips64_exec_break(cpu_mips_t *cpu,u_int code) { printf("MIPS64: BREAK instruction (code=%u)\n",code); - mips64_dump_regs(cpu); + mips64_dump_regs(cpu->gen); /* XXX TODO: Branch Delay slot */ mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_BP,0); @@ -488,24 +530,74 @@ /* Virtual breakpoint */ fastcall void mips64_run_breakpoint(cpu_mips_t *cpu) { - cpu_log(cpu,"BREAKPOINT", + cpu_log(cpu->gen,"BREAKPOINT", "Virtual breakpoint reached at PC=0x%llx\n",cpu->pc); - printf("[[[ Virtual Breakpoint reached at PC=0x%llx ]]]\n",cpu->pc); - mips64_dump_regs(cpu); - memlog_dump(cpu); + printf("[[[ Virtual Breakpoint reached at PC=0x%llx RA=0x%llx]]]\n", + cpu->pc,cpu->gpr[MIPS_GPR_RA]); + + mips64_dump_regs(cpu->gen); + memlog_dump(cpu->gen); +} + +/* Add a virtual breakpoint */ +int mips64_add_breakpoint(cpu_gen_t *cpu,m_uint64_t pc) +{ + cpu_mips_t *mcpu = CPU_MIPS64(cpu); + int i; + + for(i=0;ibreakpoints[i]) + break; + + if (i == MIPS64_MAX_BREAKPOINTS) + return(-1); + + mcpu->breakpoints[i] = pc; + mcpu->breakpoints_enabled = TRUE; + return(0); +} + +/* Remove a virtual breakpoint */ +void mips64_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t pc) +{ + cpu_mips_t *mcpu = CPU_MIPS64(cpu); + int i,j; + + for(i=0;ibreakpoints[i] == pc) + { + for(j=i;jbreakpoints[j] = mcpu->breakpoints[j+1]; + + mcpu->breakpoints[MIPS64_MAX_BREAKPOINTS-1] = 0; + } + + for(i=0;ibreakpoints[i] != 0) + return; + + mcpu->breakpoints_enabled = FALSE; } /* Debugging for register-jump to address 0 */ fastcall void mips64_debug_jr0(cpu_mips_t *cpu) { printf("MIPS64: cpu %p jumping to address 0...\n",cpu); - mips64_dump_regs(cpu); + mips64_dump_regs(cpu->gen); +} + +/* Set a register */ +void mips64_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val) +{ + if (reg < MIPS64_GPR_NR) + CPU_MIPS64(cpu)->gpr[reg] = val; } /* Dump registers of a MIPS64 processor */ -void mips64_dump_regs(cpu_mips_t *cpu) -{ +void mips64_dump_regs(cpu_gen_t *cpu) +{ + cpu_mips_t *mcpu = CPU_MIPS64(cpu); mips_insn_t *ptr,insn; char buffer[80]; int i; @@ -514,19 +606,19 @@ for(i=0;igpr[i*2], - mips64_gpr_reg_names[(i*2)+1], (i*2)+1, cpu->gpr[(i*2)+1]); + mips64_gpr_reg_names[i*2], i*2, mcpu->gpr[i*2], + mips64_gpr_reg_names[(i*2)+1], (i*2)+1, mcpu->gpr[(i*2)+1]); } - printf(" lo = 0x%16.16llx, hi = 0x%16.16llx\n", cpu->lo, cpu->hi); - printf(" pc = 0x%16.16llx, ll_bit = %u\n", cpu->pc, cpu->ll_bit); + printf(" lo = 0x%16.16llx, hi = 0x%16.16llx\n", mcpu->lo, mcpu->hi); + printf(" pc = 0x%16.16llx, ll_bit = %u\n", mcpu->pc, mcpu->ll_bit); /* Fetch the current instruction */ - ptr = cpu->mem_op_lookup(cpu,cpu->pc); + ptr = mcpu->mem_op_lookup(mcpu,mcpu->pc); if (ptr) { insn = vmtoh32(*ptr); - if (mips64_dump_insn(buffer,sizeof(buffer),1,cpu->pc,insn) != -1) + if (mips64_dump_insn(buffer,sizeof(buffer),1,mcpu->pc,insn) != -1) printf(" Instruction: %s\n",buffer); } @@ -534,17 +626,20 @@ for(i=0;iirq_count,cpu->irq_fp_count,cpu->irq_pending); + mcpu->irq_count,mcpu->irq_fp_count,mcpu->irq_pending); printf(" Timer IRQ count: %llu, pending: %u, timer drift: %u\n\n", - cpu->timer_irq_count,cpu->timer_irq_pending,cpu->timer_drift); + mcpu->timer_irq_count,mcpu->timer_irq_pending,mcpu->timer_drift); + printf(" Device access count: %llu\n",cpu->dev_access_counter); printf("\n"); } @@ -688,7 +783,7 @@ } /* cp0 register ? */ - if ((index = cp0_get_reg_index(buffer)) != -1) { + if ((index = mips64_cp0_get_reg_index(buffer)) != -1) { cpu->cp0.reg[index] = mips64_hex_u64(value,NULL); continue; } @@ -746,10 +841,10 @@ } } - cp0_map_all_tlb_to_mts(cpu); + mips64_cp0_map_all_tlb_to_mts(cpu); - mips64_dump_regs(cpu); - tlb_dump(cpu); + mips64_dump_regs(cpu->gen); + mips64_tlb_dump(cpu->gen); fclose(fd); return(0); @@ -760,6 +855,7 @@ { struct stat file_info; size_t len,clen; + m_uint32_t remain; void *haddr; FILE *bfd; @@ -793,10 +889,15 @@ else clen = len; + remain = MIPS_MIN_PAGE_SIZE; + remain -= (vaddr - (vaddr & MIPS_MIN_PAGE_MASK)); + + clen = m_min(clen,remain); + if (fread((u_char *)haddr,clen,1,bfd) != 1) break; - vaddr += MIPS_MIN_PAGE_SIZE; + vaddr += clen; len -= clen; } @@ -805,10 +906,11 @@ } /* Load an ELF image into the simulated memory */ -int mips64_load_elf_image(cpu_mips_t *cpu,char *filename, +int mips64_load_elf_image(cpu_mips_t *cpu,char *filename,int skip_load, m_uint32_t *entry_point) { m_uint64_t vaddr; + m_uint32_t remain; void *haddr; Elf32_Ehdr *ehdr; Elf32_Shdr *shdr; @@ -857,47 +959,54 @@ return(-1); } - for(i=0;ie_shnum;i++) { - scn = elf_getscn(img_elf,i); + if (!skip_load) { + for(i=0;ie_shnum;i++) { + scn = elf_getscn(img_elf,i); + + shdr = elf32_getshdr(scn); + name = elf_strptr(img_elf, ehdr->e_shstrndx, (size_t)shdr->sh_name); + len = shdr->sh_size; + + if (!(shdr->sh_flags & SHF_ALLOC) || !len) + continue; + + fseek(bfd,shdr->sh_offset,SEEK_SET); + vaddr = sign_extend(shdr->sh_addr,32); + + if (cpu->vm->debug_level > 0) { + printf(" * Adding section at virtual address 0x%8.8llx " + "(len=0x%8.8lx)\n",vaddr & 0xFFFFFFFF,(u_long)len); + } + + while(len > 0) + { + haddr = cpu->mem_op_lookup(cpu,vaddr); + + if (!haddr) { + fprintf(stderr,"load_elf_image: invalid load address 0x%llx\n", + vaddr); + return(-1); + } - shdr = elf32_getshdr(scn); - name = elf_strptr(img_elf, ehdr->e_shstrndx, (size_t)shdr->sh_name); - len = shdr->sh_size; + if (len > MIPS_MIN_PAGE_SIZE) + clen = MIPS_MIN_PAGE_SIZE; + else + clen = len; - if (!(shdr->sh_flags & SHF_ALLOC) || !len) - continue; + remain = PPC32_MIN_PAGE_SIZE; + remain -= (vaddr - (vaddr & PPC32_MIN_PAGE_MASK)); - fseek(bfd,shdr->sh_offset,SEEK_SET); - vaddr = sign_extend(shdr->sh_addr,32); + clen = m_min(clen,remain); - if (cpu->vm->debug_level > 0) { - printf(" * Adding section at virtual address 0x%8.8llx " - "(len=0x%8.8lx)\n",vaddr & 0xFFFFFFFF,(u_long)len); - } + if (fread((u_char *)haddr,clen,1,bfd) < 1) + break; - while(len > 0) - { - haddr = cpu->mem_op_lookup(cpu,vaddr); - - if (!haddr) { - fprintf(stderr,"load_elf_image: invalid load address 0x%llx\n", - vaddr); - return(-1); + vaddr += clen; + len -= clen; } - - if (len > MIPS_MIN_PAGE_SIZE) - clen = MIPS_MIN_PAGE_SIZE; - else - clen = len; - - clen = fread((u_char *)haddr,clen,1,bfd); - - if (clen != 1) - break; - - vaddr += MIPS_MIN_PAGE_SIZE; - len -= clen; } + } else { + printf("ELF loading skipped, using a ghost RAM file.\n"); } printf("ELF entry point: 0x%x\n",ehdr->e_entry); @@ -969,7 +1078,7 @@ FILE *fd; if (!cpu->sym_tree && (mips64_sym_create_tree(cpu) == -1)) { - fprintf(stderr,"CPU%u: Unable to create symbol tree.\n",cpu->id); + fprintf(stderr,"CPU%u: Unable to create symbol tree.\n",cpu->gen->id); return(-1); }