// ========== 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 "SS_SamTracer.h"
#include "SS_VirtualStrand.h"
SS_SamTracer::SS_SamTracer(SS_VirtualStrand
*strand
)
hook_exe_instr
= ss_sam_exe_instr
;
hook_reg_value
= ss_sam_reg_value
;
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
;
irec
.itype
= Sam::VCPU_UNKNOWN_ITYPE
;
// Conditional Branch decoding tables
const int BN
= 2; // special
const int BA
= 3; // case
static uint8_t bpr_taken
[8][4] =
static uint8_t fbr_taken
[16][4] =
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
;
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();
sam_trc
->irec
.pc_type
= Sam::VCPU_VADDR
;
sam_trc
->irec
.pc_type
= Sam::VCPU_RADDR
;
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
// check if it is CALL, BPcc, Bicc, BPr, FBPfcc or FBfcc instruction
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
if(o
.get_cond() == 8) // branch allways
sam_trc
->irec
.npc_va
= pc
;
else // check if conditional branch was taken
uint8_t cc_iw
= o
.get_cc();
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;
taken
= br_taken
[o
.get_cond()][strand
->ccr
.icc()]==1;
else if (op2
==5) // FBPfcc
fcc
= strand
->fsr
.fcc0();
fcc
= strand
->fsr
.fcc1();
fcc
= strand
->fsr
.fcc2();
fcc
= strand
->fsr
.fcc3();
taken
= fbr_taken
[o
.get_cond()][fcc
]==1;
else if (op2
==6) // FBfcc
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
// get current reg[rs1] value
int64_t r1
= sam_trc
->vcpu
->strand
->irf
[o
.get_rs1()];
taken
= bpr_taken
[o
.get_cond()][rv
]==1;
// delay slot instruction is annuled when annul bit is set and
annul
= ((iw
>>29)&1) && !taken
;
// check if it is DONE, RETRY, JPRIV, JMPL, RETURN instruction
uint32_t op3
= (iw
>>19) & 0x3f;
if ((op3
== 0x3e) ||(op3
== 0x38)||(op3
== 0x39))
else if (op3
== 0x3a) // Tcc
sam_trc
->irec
.itype
|= Sam::VCPU_BRANCH_ITYPE
;
// sanity check if "taken" was calculated correctly
if ((npc
!= (strand
->pc() + 4)) && !taken
)
sam_trc
->irec
.annul
= annul
;
sam_trc
->irec
.taken
= taken
;
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
)
hwop
.cpuid
= sam_trc
->vcpu
->id();
hwop
.op_type
= !is_fetch(type
);
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
)
tlb_rec
.format
= 1; // sun4u
tlb_rec
.cpuid
= sam_trc
->vcpu
->id();
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();
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
;
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");
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))
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
;
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
)
sam_trc
->irec
.dcontext
= tte
? tte
->context() : 0;
sam_trc
->irec
.ea_type
= Sam::VCPU_VADDR
;
sam_trc
->irec
.ea_type
= Sam::VCPU_VADDR
;
sam_trc
->irec
.ea_type
= Sam::VCPU_RADDR
;
sam_trc
->irec
.ea_type
= Sam::VCPU_PADDR
;
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
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
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);
if (sam_trc
->irec
.ld_num
< Sam::VCPU_MAX_MEM_ACCESS_NUM
)
sam_trc
->irec
.ld_mem_value
[sam_trc
->irec
.ld_num
++] = *val
;
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];
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];
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);
if (sam_trc
->irec
.st_num
< Sam::VCPU_MAX_MEM_ACCESS_NUM
)
sam_trc
->irec
.st_mem_value
[sam_trc
->irec
.st_num
++] = *val
;
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];
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];
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
)
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
;
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
;
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
;
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
;
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;
sam_trc
->irec
.exception
= 1;
trap
->cpuid
= sam_trc
->vcpu
->id();
trap
->pc_va
= strand
->pc();
trap
->npc_va
= strand
->npc();
sam_trc
->irec
.ea_va
= ea
;
sam_trc
->irec
.ea_pa
= 0x0;
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
));
sam_trc
->vcpu
->sys_intf
.vtrace
->instr(&(sam_trc
->irec
));
if(sam_trc
->irec
.exception
)
sam_trc
->vcpu
->sys_intf
.vtrace
->trap(&(sam_trc
->trec
));
bzero(&sam_trc
->irec
, sizeof(Sam::VCPU_Instruction
));
bzero(&sam_trc
->trec
, sizeof(Sam::VCPU_Trap
));
bzero(&sam_trc
->atrec
, sizeof(Sam::VCPU_Trap
));