* ========== Copyright Header Begin ==========================================
* OpenSPARC T2 Processor File: debug.c
* 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 ============================================
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
#pragma ident "@(#)debug.c 1.29 07/05/30 SMI"
void debug_log(simcpu_t
*sp
, uint32_t rawi
);
#define DEBUG_LOG_SIZE 16384 /* XXX FIXME: parse from conf file */
struct debug_log log
[DEBUG_LOG_SIZE
];
} *debug_spe_head
= NULL
, *debug_spe_focus
= NULL
;
print_spe_id(struct debug_spe
*spe
, char *pre
, char *post
)
nsp
= v9p
->impl_specificp
;
printf("%s%u (proc %u strand %u [core %u thread %u])%s",
sp
->config_procp
->proc_id
,
nsp
->vcore_id
, nsp
->core
, nsp
->vthread
,
print_spe_id(debug_spe_focus
, "focus is ", "\n");
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
printf("unable to set focus to %d\n", id
);
print_spe_id(debug_spe_focus
, "focus is now ", "\n");
debug_clear_breakpoint(tvaddr_t bpaddr
)
if (debug_spe_head
!= NULL
) {
(sparcv9_cpu_t
*)debug_spe_head
->sp
->specificp
, bpaddr
);
debug_print_breakpoints(void)
if (debug_spe_head
!= NULL
) {
(sparcv9_cpu_t
*)debug_spe_head
->sp
->specificp
);
debug_set_breakpoint(tvaddr_t bpaddr
)
if (debug_spe_head
!= NULL
) {
(sparcv9_cpu_t
*)debug_spe_head
->sp
->specificp
, bpaddr
);
debug_set_breakpoint_next(void)
sparcv9_cpu_t
*v9p
= (sparcv9_cpu_t
*)debug_spe_focus
->sp
->specificp
;
sparcv9_set_break_next(v9p
);
debug_breakpoint_cb(void *arg
)
/* try to set focus appropriately */
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
sparcv9_cpu_t
*v9p
= (sparcv9_cpu_t
*)spe
->sp
->specificp
;
if (sparcv9_hit_break(v9p
, spe
->sp
->pc
))
if (spe
!= NULL
&& spe
!= debug_spe_focus
)
debug_focus_set(spe
->sp
->gid
);
v9p
= (sparcv9_cpu_t
*)(debug_spe_focus
->sp
->specificp
);
sparcv9_clear_break_next(v9p
);
#if ERROR_TRAP_GEN /* { */
check_if_error_event_pending();
/* if we're single stepping enable only single cpu */
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
if (spe
!= debug_spe_focus
) {
simcore_cpu_disable(spe
->sp
);
/* otherwise we're not single stepping enable all cpus */
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
simcore_cpu_enable(spe
->sp
);
* this is a bit of a hack, but we assume that if auto_start is set
* then gdb is in use and therefore assume someone else will restart
* the simulation so don't bother.
if (options
.flag_auto_start
)
struct debug_spe
*spe
, *prev
, *tmp
;
if (debug_spe_head
== NULL
)
callback_register(CB_Breakpoint
, debug_breakpoint_cb
, NULL
);
spe
= Xcalloc(1, struct debug_spe
);
for (prev
= NULL
, tmp
= debug_spe_head
;
prev
= tmp
, tmp
= tmp
->next
)
debug_spe_focus
= debug_spe_head
;
debug_dump_pc_one(struct debug_spe
*spe
)
xidx
= (xpc
>> 2) & XICACHE_NUM_INSTR_MASK
;
xip
= &spe
->sp
->xicachep
->instn
[xidx
];
sparcv9_idis(debug_buf
, sizeof (debug_buf
),
FE_INSTN(xip
->rawi
), spe
->sp
->pc
);
printf("pc = 0x%llx:\t%08x\t%s\n", spe
->sp
->pc
, FE_INSTN(xip
->rawi
),
debug_dump_pc_one(debug_spe_focus
);
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
print_spe_id(spe
, "--- ", " ---\n");
#define CHUNKSZ (sizeof (uint64_t))
debug_x(uint64_t addr
, int chunks
)
config_proc_t
*config_procp
;
config_procp
= debug_spe_focus
->sp
->config_procp
;
domainp
= config_procp
->domainp
;
addr
&= ~(CHUNKSZ
- 1); /* force 8-byte alignment */
cap
= find_domain_address(domainp
, addr
);
printf("0x%llx not mapped in domain\n", addr
);
extent
= cap
->config_devp
->dev_typep
->dev_cacheable(cap
, DA_Load
,
addr
- cap
->baseaddr
, (uint8_t **)&bufp
);
if (extent
< chunks
* CHUNKSZ
) {
printf("0x%llx not cacheable memory in domain\n", addr
);
printf("0x%llx less than %d bytes to print\n",
chunks
= extent
/ CHUNKSZ
;
printf("0x%llx\t0x%016llx\n", addr
, *bufp
);
* Since the context is not specified for the translate command,
* check all entries that match the partition ID and print any
debug_search_tlb(ss_strand_t
*nsp
, ss_tlb_t
*tlbp
, uint64_t va
, char *nam
)
for (i
= tlbp
->nentries
, tep
= &(tlbp
->tlb_entryp
[0]);
if (tep
->hashidx
== -1) continue;
if (((tep
->tag_pfn
^ va
) >> tep
->match_shift
) == 0 &&
uint64_t pa
= va
+ tep
->pa_offset
;
printf("va:0x%016llx real -> pa:0x%016llx %s\n",
printf("va:0x%016llx context:0x%x -> "
va
, tep
->tag_context
, pa
, nam
);
simcpu_t
*sp
= debug_spe_focus
->sp
;
lprintf(sp
->gid
, "icount = 0x%lld\n", ICOUNT(sp
));
debug_initiate_save_state_now(void)
simcpu_t
*sp
= debug_spe_focus
->sp
;
options
.save_restore
.trigger_icount
= now
;
lprintf(sp
->gid
, "Debugger requested save_state. Current"
" icount=0x%llx\n", now
);
debug_print_scratch(int id
)
* simscratch called with no id so we
* use the current cpu in focus
/* search for the specific gid */
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
printf("cpuid%d does not exist\n", id
);
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= v9p
->impl_specificp
;
print_spe_id(spe
, "Scratchpad registers for ", "\n");
for (i
= 0; i
< SSR_Num_Regs
; i
++) {
printf("scratch[%d] = 0x%llx\n", i
, nsp
->strand_reg
[i
]);
sp
= debug_spe_focus
->sp
;
v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
nsp
= v9p
->impl_specificp
;
* First search itlb, if not found, search the dtlb.
* For Rock, if still not found, also search the
found
= debug_search_tlb(nsp
, tlbp
, va
, "ITLB");
found
+= debug_search_tlb(nsp
, tlbp
, va
, "DTLB");
found
+= debug_search_tlb(nsp
, tlbp
, va
, "CTLB");
printf("va 0x%llx not found in TLBs\n", va
);
debug_dis(uint64_t addr
, int chunks
)
config_proc_t
*config_procp
;
config_procp
= debug_spe_focus
->sp
->config_procp
;
domainp
= config_procp
->domainp
;
addr
&= ~3; /* force 4-byte alignment */
cap
= find_domain_address(domainp
, addr
);
extent
= cap
->config_devp
->dev_typep
->dev_cacheable(cap
, DA_Load
,
addr
- cap
->baseaddr
, (uint8_t **)&bufp
);
sparcv9_idis(debug_buf
, sizeof (debug_buf
),
printf("0x%llx\t%08x\t%s\n", addr
, FE_INSTN(*bufp
), debug_buf
);
debug_regs_one(struct debug_spe
*spe
)
v9p
= (sparcv9_cpu_t
*)spe
->sp
->specificp
;
printf(" %%g (gl=%d) %%o (cwp=%d) "
"%%l %%i\n", v9p
->gl
, v9p
->cwp
);
for (i
= 0; i
< 8; i
++) {
for (j
= 0; j
< 4; j
++) {
printf(" 0x%016llx", spe
->sp
->intreg
[i
+j
*8]);
printf(" 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n",
spe
->sp
->pc
, spe
->sp
->npc
, v9p
->tba
, v9p
->htba
);
printf(" %%tl %%pil %%pstate\n");
printf(" 0x%08x 0x%08x 0x%08x\n", v9p
->tl
, v9p
->pil
,
for (i
= 1; i
<= v9p
->maxtl
; i
++) {
printf("%d 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n",
i
, N_TPC(v9p
, i
), N_TNPC(v9p
, i
), N_TSTATE(v9p
, i
),
debug_regs_one(debug_spe_focus
);
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
print_spe_id(spe
, "--- ", " ---\n");
debug_cmpregs_one(struct debug_spe
*spe
)
simcpu_t
*sp
= (simcpu_t
*)spe
->sp
;
ss_proc_t
*npp
= (ss_proc_t
*)sp
->config_procp
->procp
;
printf("N2 CMP regs:\n");
printf("core_enable_status xir_steering core_run_status"
printf("0x%016llx 0x%016llx 0x%016llx 0x%x\n",
npp
->cmp_regs
.core_enable_status
, npp
->cmp_regs
.xir_steering
,
npp
->cmp_regs
.core_running_status
, npp
->cmp_regs
.tick_enable
);
printf("Rock CMP regs:\n");
printf("strand_available strand_enable strand_running"
" str_running_status\n");
printf("0x%016llx 0x%016llx 0x%016llx 0x%016llx\n",
npp
->cmp_regs
.strand_available
, npp
->cmp_regs
.strand_enable
,
npp
->cmp_regs
.strand_running
, npp
->cmp_regs
.strand_running_status
);
printf("No CMP regs defined\n");
debug_cmpregs_one(debug_spe_focus
);
debug_mmuregs_one(struct debug_spe
*spe
)
v9p
= (sparcv9_cpu_t
*)spe
->sp
->specificp
;
strandp
= v9p
->impl_specificp
;
sp
= (simcpu_t
*)spe
->sp
;
pnp
= (ss_proc_t
*)sp
->config_procp
->procp
;
printf("Context registers:\n");
printf(" Primary ctxt: 0x%04x Secondary ctxt: 0x%04x\n",
strandp
->pri_context
, strandp
->sec_context
);
#if defined(NIAGARA1) || defined(NIAGARA2)
printf("LSU control register: 0x%016llx\n",
strandp
->lsu_control_raw
);
printf("DCUCR: 0x%016llx\n", pnp
->dcucr
[strandp
->core
]);
printf("IFUCR: 0x%016llx\n", strandp
->ifucr
);
#error "No valid processor defined."
#if defined(NIAGARA1) || defined(NIAGARA2)
printf("D-MMU tag access register: 0x%016llx\n",
strandp
->dmmu
.tag_access_reg
);
printf("I-MMU tag access register: 0x%016llx\n",
strandp
->immu
.tag_access_reg
);
printf("MMU fault address: 0x%016llx\n", strandp
->mmu
.fault_addr
);
printf("MMU fault context type: %d\n",
(strandp
->mmu
.dsfsr
& MASK64(6, 5)) >> 5);
#error "No valid processor defined."
printf("MMU fault TL : %d\n", v9p
->tl
);
printf("MMU fault TPC: 0x%016llx\n", N_TPC(v9p
, v9p
->tl
));
debug_mmuregs_one(debug_spe_focus
);
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
print_spe_id(spe
, "--- ", " ---\n");
debug_dump_dtlb_one(struct debug_spe
*spe
)
#if defined(NIAGARA1) || defined(NIAGARA2)
ss_dump_tlbs_nolock(spe
->sp
->config_procp
, true);
rock_dump_tlbs_nolock(spe
->sp
->config_procp
, true);
#error "No valid processor defined."
debug_dump_dtlb_one(debug_spe_focus
);
debug_dump_dtlb_all(void)
config_proc_t
*cpp
= NULL
;
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
* Crude way of going per proc using the spe list.
if (spe
->sp
->config_procp
!= cpp
) {
debug_dump_dtlb_one(spe
);
cpp
= spe
->sp
->config_procp
;
debug_dump_itlb_one(struct debug_spe
*spe
)
#if defined(NIAGARA1) || defined(NIAGARA2)
ss_dump_tlbs(spe
->sp
->config_procp
, false);
rock_dump_tlbs(spe
->sp
->config_procp
, false);
#error "No valid processor defined."
debug_dump_itlb_one(debug_spe_focus
);
debug_dump_itlb_all(void)
config_proc_t
*cpp
= NULL
;
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
* Crude way of going per proc using the spe list.
if (spe
->sp
->config_procp
!= cpp
) {
debug_dump_itlb_one(spe
);
cpp
= spe
->sp
->config_procp
;
debug_log(simcpu_t
*sp
, uint32_t rawi
)
spe
= (struct debug_spe
*)sp
->debug
;
v9p
= (sparcv9_cpu_t
*)spe
->sp
->specificp
;
idx
= spe
->log_count
% DEBUG_LOG_SIZE
;
spe
->log
[idx
].count
= spe
->log_count
;
spe
->log
[idx
].pc
= sp
->pc
;
spe
->log
[idx
].rawi
= rawi
;
spe
->log
[idx
].tl
= v9p
->tl
;
spe
->log
[idx
].tt
= (v9p
->tl
== 0) ? 0 : N_TT(v9p
, v9p
->tl
);
spe
->log
[idx
].gl
= v9p
->gl
;
spe
->log
[idx
].state
= v9p
->state
;
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
print_spe_id(spe
, "--- ", " ---\n");
for (i
= 0; i
< DEBUG_LOG_SIZE
; i
++) {
int idx
= (spe
->log_count
+ i
) % DEBUG_LOG_SIZE
;
sparcv9_idis(debug_buf
, sizeof (debug_buf
),
FE_INSTN(spe
->log
[idx
].rawi
), spe
->log
[idx
].pc
);
printf("%s%llu (%s)%x:%x/%x 0x%llx:\t%08x\t%s\n",
spe
->log
[idx
].count
== (spe
->log_count
- 1) ? "-> " : " ",
sparcv9_state_name
[spe
->log
[idx
].state
],
FE_INSTN(spe
->log
[idx
].rawi
),
#if ERROR_TRAP_GEN /* { */
* We need to check if we are here because of a debugger breakpoint
* or because of an ERROR_TRAP_GEN breakpoint.
* If we got here because the ERROR_TRAP_GEN framework set
* a breakpoint, we need to check all simcpus to see which
* one has a pending error to inject, we then notify that
* simcpu to inject it's error as we have hit the %pc
* that it was waiting for. Once all the search criteria
* have been met, we trigger the error and remove the breakpoint.
check_if_error_event_pending()
for (spe
= debug_spe_head
; spe
!= NULL
; spe
= spe
->next
) {
error_event_t
*eep
= (error_event_t
*)(sp
->eep
);
sparcv9_cpu_t
*v9p
= (sparcv9_cpu_t
*)(sp
->specificp
);
/* Make sure there is an error pending for this CPU */
if (sp
->error_pending
== false) {
* Make sure if an instn_cnt was specified, that we have
if (sp
->error_cycle_reached
== false) {
/* Check if error event has a priv level specified */
if ((eep
->options
.bits
.priv
) && (eep
->priv
!= v9p
->state
)) {
/* Check if error event has a trap level specified */
if ((eep
->options
.bits
.tl
) && (eep
->tl
!= v9p
->tl
)) {
* We get here because all constraints have been met,
lprintf(sp
->gid
, "ERROR_TRAP_GEN: HIT BREAKPOINT: pc=0x%llx @ instn_cnt=0x%llx priv=%d tl=%d\n",
sp
->pc
, sp
->cycle
, v9p
->state
, v9p
->tl
);
* Only clear the breakpoint when trigger_cnt is at 1, otherwise
* leave it in place so it will be triggered again.
if (eep
->trigger_cnt
== 1)
debug_clear_breakpoint(eep
->pc
);
lprintf(sp
->gid
, "ERROR_TRAP_GEN: TRIGGER: @ pc=0x%llx priv=%d tl=%d trigger_cnt=%d\n",
sp
->pc
, v9p
->state
, v9p
->tl
, eep
->trigger_cnt
);
eep
->ee_status
= EE_TRIGGERED
;
sp
->error_pending
= false;
sp
->config_procp
->proc_typep
->trigger_error_trap(sp
);
#endif /* } ERROR_TRAP_GEN */
#define BARS "#############################################################"
#define MSG1 "## Legion Breakpoint File, Do NOT Edit ##"
#define MSG2 "## Legion session started as: ##"
debug_dump_breakpoints(char *fname
)
extern clibuf_t inputCliLine
;
if ((fp
= fopen(fname
, "w")) == NULL
) {
perror("debug_dump_breakpoints:fopen");
if (debug_spe_head
!= NULL
) {
int cliend
= strlen(inputCliLine
);
int delta
= strlen(BARS
) - (cliend
+ TARE
);
fprintf(fp
, "%s\n", BARS
);
fprintf(fp
, "%s\n", MSG1
);
fprintf(fp
, "%s\n", MSG2
);
for (; delta
> 0 && cliend
< sizeof (inputCliLine
);
inputCliLine
[cliend
++] = ' ';
inputCliLine
[cliend
] = '\0';
fprintf(fp
, "## %s ##\n", inputCliLine
);
fprintf(fp
, "%s\n", BARS
);
(sparcv9_cpu_t
*)debug_spe_head
->sp
->specificp
, fp
);
debug_restore_breakpoints(char *fname
)
if ((fp
= fopen(fname
, "r")) == NULL
) {
perror("debug_restore_breakpoints:fopen");
sparcv9_restore_break(fp
);