Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / ss / api / sam / src / SS_SamTracer.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: SS_SamTracer.cc
// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
//
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
//
// The above named program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
// ========== Copyright Header End ============================================
#include <strings.h>
#include "SS_SamTracer.h"
#include "SS_VirtualStrand.h"
#include "SS_Tlb.h"
#include "SS_Asi.h"
SS_SamTracer::SS_SamTracer(SS_VirtualStrand *strand)
:
SS_Tracer()
{
hook_exe_instr = ss_sam_exe_instr;
hook_reg_value = ss_sam_reg_value;
hook_trap = ss_sam_trap;
hook_end_instr = ss_sam_end_instr;
hook_mem_access = ss_sam_mem_acc;
hook_asi_access = ss_sam_asi_acc;
hook_tlb_update = ss_sam_tlb_update;
hook_hwop = ss_sam_hwop;
irec.nregs = 0;
irec.ld_num = 0;
irec.st_num = 0;
irec.itype = Sam::VCPU_UNKNOWN_ITYPE;
irec.exception = 0;
trec.is_async = 0;
atrec.is_async = 0;
vcpu = strand;
}
// Conditional Branch decoding tables
// special case
const int BN = 2; // special
const int BA = 3; // case
static uint8_t bpr_taken[8][4] =
{
// 0 < 0 > 0
BN, BN, BN, BN,
BN, 1, 0, 0,
BN, 1, 1, 0,
BN, 0, 1, 0,
BN, BN, BN, BN,
BN, 0, 1, 1,
BN, 0, 0, 1,
BN, 1, 0, 1,
};
static uint8_t fbr_taken[16][4] =
{
// E L G U
BN, BN, BN, BN,
0, 1, 1, 1,
0, 1, 1, 0,
0, 1, 0, 1,
0, 1, 0, 0,
0, 0, 1, 1,
0, 0, 1, 0,
0, 0, 0, 1,
BA, BA, BA, BA,
1, 0, 0, 0,
1, 0, 0, 1,
1, 0, 1, 0,
1, 0, 1, 1,
1, 1, 0, 0,
1, 1, 0, 1,
1, 1, 1, 0,
};
static uint8_t br_taken [16][16] =
{
BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN,
0,0,0,0, 1,1,1,1, 0,0,0,0, 1,1,1,1,
0,0,1,1, 1,1,1,1, 1,1,0,0, 1,1,1,1,
0,0,1,1, 0,0,1,1, 1,1,0,0, 1,1,0,0,
0,1,0,1, 1,1,1,1, 0,1,0,1, 1,1,1,1,
0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1,
0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1,
0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1,
BA, BA, BA, BA, BA, BA, BA, BA, BA, BA, BA, BA, BA, BA, BA, BA,
1,1,1,1, 0,0,0,0, 1,1,1,1, 0,0,0,0,
1,1,0,0, 0,0,0,0, 0,0,1,1, 0,0,0,0,
1,1,0,0, 1,1,0,0, 0,0,1,1, 0,0,1,1,
1,0,1,0, 0,0,0,0, 1,0,1,0, 0,0,0,0,
1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0,
1,1,1,1, 1,1,1,1, 0,0,0,0, 0,0,0,0,
1,1,0,0, 1,1,0,0, 1,1,0,0, 1,1,0,0,
};
void SS_SamTracer::ss_sam_exe_instr( SS_Tracer* trc, SS_Vaddr pc, SS_Tte* tte, SS_Instr* i )
{
SS_SamTracer* sam_trc = (SS_SamTracer*)trc;
SS_Strand* strand = sam_trc->vcpu->strand;
uint32_t iw = i->opc();
// instruction record
if(sam_trc->vcpu->config.trace_on &&
sam_trc->vcpu->sys_intf.vtrace)
{
sam_trc->irec.cpuid = strand->strand_id();
sam_trc->irec.pid = tte->pid();
sam_trc->irec.icontext = tte->context();
if (tte->is_virt())
sam_trc->irec.pc_type = Sam::VCPU_VADDR;
else if (tte->is_real())
sam_trc->irec.pc_type = Sam::VCPU_RADDR;
else
sam_trc->irec.pc_type = Sam::VCPU_PADDR;
sam_trc->irec.pc_va = pc;
sam_trc->irec.pc_pa = tte->trans(pc);
int64_t npc = sam_trc->irec.npc_va = strand->npc();
sam_trc->irec.opcode = iw;
sam_trc->irec.pstate = strand->pstate();
sam_trc->irec.hpstate = strand->hpstate();
sam_trc->irec.immu_asi = strand->inst_dft_asi();
sam_trc->irec.dmmu_asi = i->is_lsu() ? i->asi : 0;
sam_trc->irec.ifetch_trap = !tte->valid();
sam_trc->irec.dmmu_trap = 0; // should be detected from trap type
// Check for cti instructions
// simple check for pc diff does not cover all cases
bool taken = false;
bool annul = false;
bool cti = false;
// check if it is CALL, BPcc, Bicc, BPr, FBPfcc or FBfcc instruction
SS_Opcode o;
o = iw;
uint32_t op = o.get_op();
uint32_t op2 = o.get_op2();
bool branch_on_cond = (op==0) && ((op2==1)||(op2==2)||(op2==5)||(op2==6));
bool branch_on_reg = (op==0) && (op2==3);
if (op==1) // CALL instruction
{
taken = true;
cti = true;
}
else if (branch_on_cond)
{
cti = true;
if(o.get_cond() == 8) // branch allways
{
taken = true;
sam_trc->irec.npc_va = pc;
}
else // check if conditional branch was taken
{
if (op2==1) // BPcc
{
uint8_t cc_iw = o.get_cc();
if (cc_iw == 0) // icc
taken = br_taken[o.get_cond()][strand->ccr.icc()]==1;
else if (cc_iw = 2) // xcc
taken = br_taken[o.get_cond()][strand->ccr.xcc()]==1;
}
else if (op2==2) //Bicc
{
taken = br_taken[o.get_cond()][strand->ccr.icc()]==1;
}
else if (op2==5) // FBPfcc
{
uint8_t fcc = 0;
strand->get_fsr();
switch (o.get_cc())
{
case 0: // fcc0
fcc = strand->fsr.fcc0();
break;
case 1: // fcc1
fcc = strand->fsr.fcc1();
break;
case 2: // fcc2
fcc = strand->fsr.fcc2();
break;
case 3: // fcc3
fcc = strand->fsr.fcc3();
break;
}
taken = fbr_taken[o.get_cond()][fcc]==1;
}
else if (op2==6) // FBfcc
{
strand->get_fsr();
uint8_t fcc0 = strand->fsr.fcc0();
taken = fbr_taken[o.get_cond()][fcc0]==1;
}
}
// delay slot instruction is annuled when annul bit is set and
// it is unconditional branch or conditional branch is not taken
annul = ((iw>>29)&1) && ( (o.get_cond() == 8 || o.get_cond() == 0) || !taken );
}
else if (branch_on_reg) // check if branch on reg value was taken
{
cti = true;
// get current reg[rs1] value
int64_t r1 = sam_trc->vcpu->strand->irf[o.get_rs1()];
uint8_t rv = 0;
if (r1 == 0) rv = 1;
else if (r1 < 0) rv = 2;
else if (r1 > 0) rv = 3;
taken = bpr_taken[o.get_cond()][rv]==1;
// delay slot instruction is annuled when annul bit is set and
// branch is not taken
annul = ((iw>>29)&1) && !taken ;
}
if ( !cti && (op == 2))
{
// check if it is DONE, RETRY, JPRIV, JMPL, RETURN instruction
uint32_t op3 = (iw>>19) & 0x3f;
if ((op3 == 0x3e) ||(op3 == 0x38)||(op3 == 0x39))
{
cti = true;
taken = true;
}
else if (op3 == 0x3a) // Tcc
{
cti = true;
}
}
if (cti)
{
sam_trc->irec.itype |= Sam::VCPU_BRANCH_ITYPE;
}
// sanity check if "taken" was calculated correctly
if ((npc != (strand->pc() + 4)) && !taken)
{
taken = true;
}
sam_trc->irec.annul = annul;
sam_trc->irec.taken = taken;
}
return;
}
void SS_SamTracer::ss_sam_hwop( SS_Tracer* trc, MemAccess type, SS_Paddr addr, uint_t size, uint64_t *value )
{
SS_SamTracer* sam_trc = (SS_SamTracer*)trc;
if(sam_trc->vcpu->config.trace_on &&
sam_trc->vcpu->sys_intf.vtrace)
{
Sam::VCPU_HwOp hwop;
hwop.cpuid = sam_trc->vcpu->id();
hwop.op_type = !is_fetch(type);
hwop.addr = addr;
hwop.data[0] = value[0];
hwop.data[1] = value[1];
// call tracer
sam_trc->vcpu->sys_intf.vtrace->hwop(&hwop);
}
}
void SS_SamTracer::ss_sam_tlb_update( SS_Tracer* trc, bool insert, SS_Tlb* tlb, uint_t index, SS_Tte* tte )
{
SS_SamTracer* sam_trc = (SS_SamTracer*)trc;
if( !sam_trc->vcpu->config.trace_on ||
!sam_trc->vcpu->sys_intf.vtrace)
return;
Sam::VCPU_TLB tlb_rec;
tlb_rec.format = 1; // sun4u
tlb_rec.cpuid = sam_trc->vcpu->id();
tlb_rec.demap = !insert;
tlb_rec.tlb_type = tlb->is_data_tlb();
tlb_rec.tlb_index = index;
tlb_rec.tlb_no = tlb->tlb_id();
tlb_rec.tte_tag = tte->tag();
// data: v:63 nfo:62 taddr: 55-13 ie:12 e:11 cp:10 cv:9 p:8 ep:7 w:6 sz:3-0
tlb_rec.tte_data = tte->taddr() & (0x7ffffffffull << 13);
if (tte->valid_bit()) tlb_rec.tte_data |= (1ull<<63);
if (tte->nfo()) tlb_rec.tte_data |= (1ull << 62);
if (tte->ie()) tlb_rec.tte_data |= (1ull << 12);
if (tte->e()) tlb_rec.tte_data |= (1ull << 11);
if (tte->cp()) tlb_rec.tte_data |= (1ull << 10);
if (tte->cv()) tlb_rec.tte_data |= (1ull << 9);
if (tte->p()) tlb_rec.tte_data |= (1ull << 8);
if (tte->x()) tlb_rec.tte_data |= (1ull << 7);
if (tte->w()) tlb_rec.tte_data |= (1ull << 6);
tlb_rec.tte_data |= tte->page_size();
tlb_rec.is_real = tte->real_bit();
tlb_rec.partid = tte->pid();
tlb_rec.tte_page_size = tte->page_size();
tlb_rec.tte_context = tte->context();
// call tracer
sam_trc->vcpu->sys_intf.vtrace->tlb(&tlb_rec);
}
void SS_SamTracer::ss_sam_reg_value( SS_Tracer* trc, SS_Registers::Index index, uint64_t value )
{
SS_SamTracer* sam_trc = (SS_SamTracer*)trc;
// collect reg values
if(sam_trc->vcpu->config.trace_on &&
sam_trc->vcpu->sys_intf.vtrace)
{
int rid = sam_trc->irec.nregs;
if (rid >= (Sam::VCPU_MAX_DEST_REG_NUM-1))
{
//fprintf (stderr, "Tracer: too many dest regs\n");
return;
}
if (SS_Registers::is_irf(index))
{
sam_trc->irec.itype |= Sam::VCPU_ALU_ITYPE;
sam_trc->irec.dreg[rid].r.id = index - SS_Registers::IRF_OFS;
sam_trc->irec.dreg[rid].r.type = Sam::VCPU_INT_RTYPE;
sam_trc->irec.dval[rid] = value;
}
else if (SS_Registers::is_drf(index))
{
sam_trc->irec.itype |= Sam::VCPU_FP_ITYPE;
sam_trc->irec.dreg[rid].r.id = (index - SS_Registers::DRF_OFS)*2; // @@ note *2 !!!
sam_trc->irec.dreg[rid].r.type = Sam::VCPU_FP_DOUBLE_RTYPE;
sam_trc->irec.dval[rid] = value;
}
else if (SS_Registers::is_asr(index))
{
if ((index == SS_Registers::ASR_PC) &&
value == (sam_trc->irec.pc_va + 4))
{
// skip pc update
return;
}
sam_trc->irec.dreg[rid].r.id = index - SS_Registers::ASR_OFS;
sam_trc->irec.dreg[rid].r.type = Sam::VCPU_ASR_RTYPE;
sam_trc->irec.dval[rid] = value;
}
else if (SS_Registers::is_pr(index))
{
sam_trc->irec.dreg[rid].r.id = index - SS_Registers::PR_OFS;
sam_trc->irec.dreg[rid].r.type = Sam::VCPU_PR_RTYPE;
sam_trc->irec.dval[rid] = value;
}
else if (SS_Registers::is_hpr(index))
{
sam_trc->irec.dreg[rid].r.id = index - SS_Registers::HPR_OFS;
sam_trc->irec.dreg[rid].r.type = Sam::VCPU_HPR_RTYPE;
sam_trc->irec.dval[rid] = value;
}
else
{
return; // unknown reg, ignore
}
sam_trc->irec.nregs = rid + 1;
}
}
void SS_SamTracer::ss_sam_mem_acc ( SS_Tracer* trc, MemAccess type, SS_Vaddr va,
SS_Tte* tte, uint_t size, uint64_t* val )
{
SS_SamTracer* sam_trc = (SS_SamTracer*)trc;
if( !sam_trc->vcpu->config.trace_on ||
!sam_trc->vcpu->sys_intf.vtrace)
return;
sam_trc->irec.dcontext = tte ? tte->context() : 0;
if (tte == 0)
sam_trc->irec.ea_type = Sam::VCPU_VADDR;
else if (tte->is_virt())
sam_trc->irec.ea_type = Sam::VCPU_VADDR;
else if (tte->is_real())
sam_trc->irec.ea_type = Sam::VCPU_RADDR;
else
sam_trc->irec.ea_type = Sam::VCPU_PADDR;
switch(type)
{
case PREFETCH:
sam_trc->irec.itype |= Sam::VCPU_LOAD_ITYPE;
sam_trc->irec.ea_va = va;
sam_trc->irec.ea_pa = tte ? tte->trans(va) : 0; // tte == 0 is TLB mis (no trap)
sam_trc->irec.ld_bitmask = 0; // zero size load == prefetch
break;
case FLUSH:
sam_trc->irec.itype |= Sam::VCPU_STORE_ITYPE;
sam_trc->irec.ea_va = va;
sam_trc->irec.ea_pa = tte ? tte->trans(va) : 0; // tte == 0 is TLB mis (no trap)
sam_trc->irec.st_bitmask = 0; // zero size store == flush
break;
case LD_DATA :
case LD_SWAP :
case LD_CAS :
case LD_LDST :
sam_trc->irec.itype |= Sam::VCPU_LOAD_ITYPE;
sam_trc->irec.ea_va = va;
sam_trc->irec.ea_pa = tte->trans(va);
sam_trc->irec.ld_bitmask = (size<8)?(uint64_t(1)<<(size*8))-uint64_t(1):~uint64_t(0);
switch(size)
{
case 1:
case 2:
case 4:
case 8:
if (sam_trc->irec.ld_num < Sam::VCPU_MAX_MEM_ACCESS_NUM)
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = *val;
break;
case 16:
if (sam_trc->irec.ld_num < Sam::VCPU_MAX_MEM_ACCESS_NUM-1)
{
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[0];
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[1];
}
break;
case 64:
if (sam_trc->irec.ld_num < Sam::VCPU_MAX_MEM_ACCESS_NUM-7)
{
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[0];
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[1];
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[2];
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[3];
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[4];
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[5];
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[6];
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = val[7];
}
break;
default:
break;
}
break;
case ST_DATA :
case ST_SWAP :
case ST_CAS :
case ST_LDST :
sam_trc->irec.itype |= Sam::VCPU_STORE_ITYPE;
sam_trc->irec.ea_va = va;
sam_trc->irec.ea_pa = tte->trans(va);
sam_trc->irec.st_bitmask = (size<8)?(uint64_t(1)<<(size*8))-uint64_t(1):~uint64_t(0);
switch(size)
{
case 1:
case 2:
case 4:
case 8:
if (sam_trc->irec.st_num < Sam::VCPU_MAX_MEM_ACCESS_NUM)
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = *val;
break;
case 64:
if (sam_trc->irec.st_num < Sam::VCPU_MAX_MEM_ACCESS_NUM-7)
{
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = val[0];
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = val[1];
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = val[2];
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = val[3];
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = val[4];
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = val[5];
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = val[6];
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = val[7];
}
break;
default:
break;
}
break;
case ST_PART :
sam_trc->irec.itype |= Sam::VCPU_STORE_ITYPE;
sam_trc->irec.ea_va = va;
sam_trc->irec.ea_pa = tte->trans(va);
sam_trc->irec.st_bitmask = val[1];
if (sam_trc->irec.st_num < Sam::VCPU_MAX_MEM_ACCESS_NUM)
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = val[0];
break;
default:
break;
}
}
void SS_SamTracer::ss_sam_asi_acc ( SS_Tracer* trc, MemAccess type, uint8_t asi, SS_Vaddr va, uint64_t* val )
{
SS_SamTracer* sam_trc = (SS_SamTracer*)trc;
if( !sam_trc->vcpu->config.trace_on ||
!sam_trc->vcpu->sys_intf.vtrace)
return;
sam_trc->irec.dmmu_asi = asi;
sam_trc->irec.ea_va = va;
sam_trc->irec.ea_pa = va;
sam_trc->irec.ld_bitmask = ~uint64_t(0);
sam_trc->irec.ea_type = Sam::VCPU_VADDR;
switch(type)
{
case LD_DATA:
sam_trc->irec.itype |= Sam::VCPU_ASI_LOAD_ITYPE;
if (sam_trc->irec.ld_num < Sam::VCPU_MAX_MEM_ACCESS_NUM)
sam_trc->irec.ld_mem_value[sam_trc->irec.ld_num++] = *val;
break;
case ST_DATA :
sam_trc->irec.itype |= Sam::VCPU_ASI_STORE_ITYPE;
if (sam_trc->irec.st_num < Sam::VCPU_MAX_MEM_ACCESS_NUM)
sam_trc->irec.st_mem_value[sam_trc->irec.st_num++] = *val;
break;
default:
break;
}
}
void SS_SamTracer::ss_sam_trap( SS_Tracer* trc, SS_Trap::Type tt, TrapMode mode, SS_Vaddr ea )
{
SS_SamTracer* sam_trc = (SS_SamTracer*)trc;
SS_Strand* strand = sam_trc->vcpu->strand;
// trap record
if(sam_trc->vcpu->config.trace_on &&
sam_trc->vcpu->sys_intf.vtrace)
{
Sam::VCPU_Trap *trap = &sam_trc->trec;
if (SS_Trap::table[tt].disrupting)
{
sam_trc->atrec.is_async = 1;
trap = &sam_trc->atrec;
}
else
{
sam_trc->irec.exception = 1;
}
// collect trap record
trap->cpuid = sam_trc->vcpu->id();
trap->nregs = 0;
uint64_t value=0;
trap->pc_va = strand->pc();
trap->npc_va = strand->npc();
trap->tno = tt;
if (mode == DATA_TRAP) {
sam_trc->irec.ea_va = ea;
sam_trc->irec.ea_pa = 0x0;
}
}
return;
}
void SS_SamTracer::ss_sam_end_instr( SS_Tracer* trc )
{
SS_SamTracer* sam_trc = (SS_SamTracer*)trc;
if(sam_trc->vcpu->config.trace_on &&
sam_trc->vcpu->sys_intf.vtrace)
{
// call tracer - async trap record
if(sam_trc->atrec.is_async)
sam_trc->vcpu->sys_intf.vtrace->trap(&(sam_trc->atrec));
// instruction record
sam_trc->vcpu->sys_intf.vtrace->instr(&(sam_trc->irec));
// trap record
if(sam_trc->irec.exception)
sam_trc->vcpu->sys_intf.vtrace->trap(&(sam_trc->trec));
// reset the counters
bzero(&sam_trc->irec, sizeof(Sam::VCPU_Instruction));
bzero(&sam_trc->trec, sizeof(Sam::VCPU_Trap));
bzero(&sam_trc->atrec, sizeof(Sam::VCPU_Trap));
}
}