📄 mips64.c
字号:
/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * XXX TODO: proper context save/restore for CPUs. */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/mman.h>#include <fcntl.h>#include <assert.h>#include "rbtree.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"/* MIPS general purpose registers names */char *mips64_gpr_reg_names[MIPS64_GPR_NR] = { "zr", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra",};/* Cacheability and Coherency Attribute */static int cca_cache_status[8] = { 1, 1, 0, 1, 0, 1, 0, 0,};/* Get register index given its name */int mips64_get_reg_index(char *name){ int i; for(i=0;i<MIPS64_GPR_NR;i++) if (!strcmp(mips64_gpr_reg_names[i],name)) return(i); return(-1);}/* Get cacheability info */int mips64_cca_cached(m_uint8_t val){ return(cca_cache_status[val & 0x03]);}/* Reset a MIPS64 CPU */int mips64_reset(cpu_mips_t *cpu){ cpu->pc = MIPS_ROM_PC; cpu->gpr[MIPS_GPR_SP] = MIPS_ROM_SP; cpu->cp0.reg[MIPS_CP0_STATUS] = MIPS_CP0_STATUS_BEV; cpu->cp0.reg[MIPS_CP0_CAUSE] = 0; cpu->cp0.reg[MIPS_CP0_CONFIG] = 0x00c08ff0ULL; /* Clear the complete TLB */ memset(&cpu->cp0.tlb,0,MIPS64_TLB_MAX_ENTRIES*sizeof(tlb_entry_t)); /* Restart the MTS subsystem */ mips64_set_addr_mode(cpu,32/*64*/); /* zzz */ cpu->gen->mts_rebuild(cpu->gen); /* Flush JIT structures */ mips64_jit_flush(cpu,0); return(0);}/* Initialize a MIPS64 processor */int mips64_init(cpu_mips_t *cpu){ 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->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; cpu->timer_irq_freq = 250; /* 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->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); return(0);}/* Delete a MIPS64 processor */void mips64_delete(cpu_mips_t *cpu){ if (cpu) { mips64_mem_shutdown(cpu); mips64_jit_shutdown(cpu); }}/* Set the CPU PRID register */void mips64_set_prid(cpu_mips_t *cpu,m_uint32_t prid){ cpu->cp0.reg[MIPS_CP0_PRID] = prid; if ((prid == MIPS_PRID_R7000) || (prid == MIPS_PRID_BCM1250)) cpu->cp0.tlb_entries = MIPS64_TLB_MAX_ENTRIES;}/* Set idle PC value */void mips64_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr){ CPU_MIPS64(cpu)->idle_pc = addr;}/* Timer IRQ */void *mips64_timer_irq_run(cpu_mips_t *cpu){ pthread_mutex_t umutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t ucond = PTHREAD_COND_INITIALIZER; struct timespec t_spc; m_tmcnt_t expire; u_int interval; u_int threshold; interval = 1000000 / cpu->timer_irq_freq; threshold = cpu->timer_irq_freq * 10; expire = m_gettime_usec() + interval; while(cpu->gen->state != CPU_STATE_HALTED) { pthread_mutex_lock(&umutex); t_spc.tv_sec = expire / 1000000; t_spc.tv_nsec = (expire % 1000000) * 1000; pthread_cond_timedwait(&ucond,&umutex,&t_spc); pthread_mutex_unlock(&umutex); if (likely(!cpu->irq_disable) && likely(cpu->gen->state == CPU_STATE_RUNNING)) { cpu->timer_irq_pending++; if (unlikely(cpu->timer_irq_pending > threshold)) { cpu->timer_irq_pending = 0; cpu->timer_drift++;#if 0 printf("Timer IRQ not accurate (%u pending IRQ): " "reduce the \"--timer-irq-check-itv\" parameter " "(current value: %u)\n", cpu->timer_irq_pending,cpu->timer_irq_check_itv);#endif } } expire += interval; } return NULL;}#define IDLE_HASH_SIZE 8192/* Idle PC hash item */struct mips64_idle_pc_hash { m_uint64_t pc; u_int count; struct mips64_idle_pc_hash *next;};/* Determine an "idling" PC */int mips64_get_idling_pc(cpu_gen_t *cpu){ 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; 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); } printf("\nPlease wait while gathering statistics...\n"); pc_hash = calloc(IDLE_HASH_SIZE,sizeof(struct mips64_idle_pc_hash *)); /* Disable IRQ */ mcpu->irq_disable = TRUE; /* Take 1000 measures, each mesure every 10ms */ for(i=0;i<1000;i++) { cur_pc = mcpu->pc; h_index = (cur_pc >> 2) & (IDLE_HASH_SIZE-1); for(p=pc_hash[h_index];p;p=p->next) if (p->pc == cur_pc) { p->count++; break; } if (!p) { if ((p = malloc(sizeof(*p)))) { p->pc = cur_pc; p->count = 1; p->next = pc_hash[h_index]; pc_hash[h_index] = p; } } usleep(10000); } /* Select PCs */ for(i=0,res_count=0;i<IDLE_HASH_SIZE;i++) { for(p=pc_hash[i];p;p=p->next) if ((p->count >= 20) && (p->count <= 80)) { res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; res->pc = p->pc; res->count = p->count; if (cpu->idle_pc_prop_count >= CPU_IDLE_PC_MAX_RES) goto done; } } done: /* Set idle PC */ if (cpu->idle_pc_prop_count) { printf("Done. Suggested idling PC:\n"); for(i=0;i<cpu->idle_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", cpu->idle_pc_prop[0].pc); } else { printf("Done. No suggestion for idling PC\n"); for(i=0;i<IDLE_HASH_SIZE;i++) for(p=pc_hash[i];p;p=p->next) { 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 */ 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){ mips_cp0_t *cp0 = &cpu->cp0; m_uint32_t imask,sreg_mask; m_uint32_t cause; cpu->irq_pending = FALSE; cause = cp0->reg[MIPS_CP0_CAUSE] & ~MIPS_CP0_CAUSE_IMASK; cp0->reg[MIPS_CP0_CAUSE] = cause | cpu->irq_cause; sreg_mask = MIPS_CP0_STATUS_IE|MIPS_CP0_STATUS_EXL|MIPS_CP0_STATUS_ERL; if ((cp0->reg[MIPS_CP0_STATUS] & sreg_mask) == MIPS_CP0_STATUS_IE) { imask = cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_IMASK; if (unlikely(cp0->reg[MIPS_CP0_CAUSE] & imask)) { cpu->irq_pending = TRUE; return(TRUE); } } return(FALSE);}/* Update the IRQ flag */void mips64_update_irq_flag(cpu_mips_t *cpu){ mips64_update_irq_flag_fast(cpu);}/* Generate an exception */void mips64_trigger_exception(cpu_mips_t *cpu,u_int exc_code,int bd_slot){ mips_cp0_t *cp0 = &cpu->cp0; m_uint64_t cause,vector; /* we don't set EPC if EXL is set */ if (!(cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_EXL)) { cp0->reg[MIPS_CP0_EPC] = cpu->pc; /* keep IM, set exception code and bd slot */ cause = cp0->reg[MIPS_CP0_CAUSE] & MIPS_CP0_CAUSE_IMASK; if (bd_slot) cause |= MIPS_CP0_CAUSE_BD_SLOT; else cause &= ~MIPS_CP0_CAUSE_BD_SLOT; cause |= (exc_code << MIPS_CP0_CAUSE_SHIFT); cp0->reg[MIPS_CP0_CAUSE] = cause; /* XXX properly set vector */ vector = 0x180ULL; } else { /* keep IM and set exception code */ cause = cp0->reg[MIPS_CP0_CAUSE] & MIPS_CP0_CAUSE_IMASK; cause |= (exc_code << MIPS_CP0_CAUSE_SHIFT); cp0->reg[MIPS_CP0_CAUSE] = cause; /* set vector */ vector = 0x180ULL; } /* Set EXL bit in status register */ cp0->reg[MIPS_CP0_STATUS] |= MIPS_CP0_STATUS_EXL; /* Use bootstrap vectors ? */ if (cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_BEV) cpu->pc = 0xffffffffbfc00200ULL + vector; else cpu->pc = 0xffffffff80000000ULL + vector; /* Clear the pending IRQ flag */ cpu->irq_pending = 0;}/* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */fastcall void mips64_exec_inc_cp0_cnt(cpu_mips_t *cpu){ cpu->cp0_virt_cnt_reg++;#if 0 /* TIMER_IRQ */ mips_cp0_t *cp0 = &cpu->cp0; if (unlikely((cpu->cp0_virt_cnt_reg == cpu->cp0_virt_cmp_reg))) { cp0->reg[MIPS_CP0_COUNT] = (m_uint32_t)cp0->reg[MIPS_CP0_COMPARE]; mips64_set_irq(cpu,7); mips64_update_irq_flag_fast(cpu); }#endif}/* Trigger the Timer IRQ */fastcall void mips64_trigger_timer_irq(cpu_mips_t *cpu){ mips_cp0_t *cp0 = &cpu->cp0; cpu->timer_irq_count++; cp0->reg[MIPS_CP0_COUNT] = (m_uint32_t)cp0->reg[MIPS_CP0_COMPARE]; mips64_set_irq(cpu,7); mips64_update_irq_flag_fast(cpu);}/* Execute ERET instruction */fastcall void mips64_exec_eret(cpu_mips_t *cpu){ mips_cp0_t *cp0 = &cpu->cp0; if (cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_ERL) { cp0->reg[MIPS_CP0_STATUS] &= ~MIPS_CP0_STATUS_ERL; cpu->pc = cp0->reg[MIPS_CP0_ERR_EPC]; } else { cp0->reg[MIPS_CP0_STATUS] &= ~MIPS_CP0_STATUS_EXL; cpu->pc = cp0->reg[MIPS_CP0_EPC]; } /* We have to clear the LLbit */ cpu->ll_bit = 0; /* Update the pending IRQ flag */ mips64_update_irq_flag_fast(cpu);}/* Execute SYSCALL instruction */fastcall void mips64_exec_syscall(cpu_mips_t *cpu){#if DEBUG_SYSCALL printf("MIPS64: SYSCALL at PC=0x%llx (RA=0x%llx)\n" " a0=0x%llx, a1=0x%llx, a2=0x%llx, a3=0x%llx\n", cpu->pc, cpu->gpr[MIPS_GPR_RA], 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);}/* Execute BREAK instruction */fastcall void mips64_exec_break(cpu_mips_t *cpu,u_int code){ printf("MIPS64: BREAK instruction (code=%u)\n",code); mips64_dump_regs(cpu->gen); /* XXX TODO: Branch Delay slot */ mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_BP,0);}/* Trigger a Trap Exception */fastcall void mips64_trigger_trap_exception(cpu_mips_t *cpu){ /* XXX TODO: Branch Delay slot */ printf("MIPS64: TRAP exception, CPU=%p\n",cpu); mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_TRAP,0);}/* Trigger IRQs */fastcall void mips64_trigger_irq(cpu_mips_t *cpu){ if (unlikely(cpu->irq_disable)) { cpu->irq_pending = 0; return; } cpu->irq_count++; if (mips64_update_irq_flag_fast(cpu)) mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_INTERRUPT,0); else cpu->irq_fp_count++;}/* DMFC1 */fastcall void mips64_exec_dmfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg){ cpu->gpr[gp_reg] = cpu->fpu.reg[cp1_reg];}/* DMTC1 */fastcall void mips64_exec_dmtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg){ cpu->fpu.reg[cp1_reg] = cpu->gpr[gp_reg];}/* MFC1 */fastcall void mips64_exec_mfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg){ m_int64_t val; val = cpu->fpu.reg[cp1_reg] & 0xffffffff; cpu->gpr[gp_reg] = sign_extend(val,32);}/* MTC1 */fastcall void mips64_exec_mtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg){ cpu->fpu.reg[cp1_reg] = cpu->gpr[gp_reg] & 0xffffffff;}/* Virtual breakpoint */fastcall void mips64_run_breakpoint(cpu_mips_t *cpu){ cpu_log(cpu->gen,"BREAKPOINT", "Virtual breakpoint reached at PC=0x%llx\n",cpu->pc); 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;i<MIPS64_MAX_BREAKPOINTS;i++) if (!mcpu->breakpoints[i]) break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -