Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / procs / sunsparc / debug / debug.c
/*
* ========== 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"
#include <stdio.h>
#include "ss_common.h"
#include "options.h"
#ifdef ERROR_TRAP_GEN
#include "ss_err_trap.h"
#endif
#ifdef NIAGARA1
#include "niagara.h"
#endif
#ifdef NIAGARA2
#include "niagara2.h"
#endif
#ifdef ROCK
#include "rock.h"
#endif
void debug_log(simcpu_t *sp, uint32_t rawi);
#define DEBUG_LOG_SIZE 16384 /* XXX FIXME: parse from conf file */
struct debug_log {
uint64_t count;
uint64_t pc;
uint32_t rawi;
uint32_t tl;
uint32_t tt;
uint32_t gl;
sparcv9_state_t state;
};
struct debug_spe {
simcpu_t *sp;
struct debug_log log[DEBUG_LOG_SIZE];
uint64_t log_count;
struct debug_spe *next;
} *debug_spe_head = NULL, *debug_spe_focus = NULL;
char debug_buf[160];
int do_next = false;
void
debug_hook()
{
}
static void
print_spe_id(struct debug_spe *spe, char *pre, char *post)
{
simcpu_t *sp;
sparcv9_cpu_t *v9p;
ss_strand_t *nsp;
sp = spe->sp;
v9p = sp->specificp;
nsp = v9p->impl_specificp;
printf("%s%u (proc %u strand %u [core %u thread %u])%s",
pre,
sp->gid,
sp->config_procp->proc_id,
nsp->vcore_id, nsp->core, nsp->vthread,
post);
}
void
debug_focus_show(void)
{
print_spe_id(debug_spe_focus, "focus is ", "\n");
}
void
debug_focus_set(int id)
{
struct debug_spe *spe;
for (spe = debug_spe_head; spe != NULL; spe = spe->next) {
if (spe->sp->gid == id)
break;
}
if (spe == NULL) {
printf("unable to set focus to %d\n", id);
} else {
debug_spe_focus = spe;
print_spe_id(debug_spe_focus, "focus is now ", "\n");
}
}
void
debug_clear_breakpoint(tvaddr_t bpaddr)
{
if (debug_spe_head != NULL) {
sparcv9_clear_break(
(sparcv9_cpu_t *)debug_spe_head->sp->specificp, bpaddr);
}
}
void
debug_print_breakpoints(void)
{
if (debug_spe_head != NULL) {
sparcv9_print_break(
(sparcv9_cpu_t *)debug_spe_head->sp->specificp);
}
}
void
debug_set_breakpoint(tvaddr_t bpaddr)
{
if (debug_spe_head != NULL) {
sparcv9_set_break(
(sparcv9_cpu_t *)debug_spe_head->sp->specificp, bpaddr);
}
}
void
debug_set_breakpoint_next(void)
{
sparcv9_cpu_t *v9p = (sparcv9_cpu_t *)debug_spe_focus->sp->specificp;
sparcv9_set_break_next(v9p);
do_next = true;
}
void
debug_breakpoint_cb(void *arg)
{
struct debug_spe *spe;
/* 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))
break;
}
if (spe != NULL && spe != debug_spe_focus)
debug_focus_set(spe->sp->gid);
if (do_next) {
sparcv9_cpu_t *v9p;
v9p = (sparcv9_cpu_t *)(debug_spe_focus->sp->specificp);
sparcv9_clear_break_next(v9p);
do_next = false;
}
debug_hook();
#if ERROR_TRAP_GEN /* { */
check_if_error_event_pending();
#endif
if (do_next) {
/* if we're single stepping enable only single cpu */
for (spe = debug_spe_head; spe != NULL; spe = spe->next) {
if (spe != debug_spe_focus) {
if (!DISABLED(spe->sp))
simcore_cpu_disable(spe->sp);
}
}
} else {
/* otherwise we're not single stepping enable all cpus */
for (spe = debug_spe_head; spe != NULL; spe = spe->next) {
if (DISABLED(spe->sp))
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)
simcore_start();
}
void
debug_init(simcpu_t *sp)
{
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);
spe->sp = sp;
for (prev = NULL, tmp = debug_spe_head;
tmp != NULL;
prev = tmp, tmp = tmp->next)
;
if (prev == NULL) {
debug_spe_head = spe;
debug_spe_focus = debug_spe_head;
} else {
prev->next = spe;
}
sp->debug = (void *)spe;
}
void
debug_dump_pc_one(struct debug_spe *spe)
{
tvaddr_t xpc;
uint64_t xidx;
xicache_instn_t *xip;
xpc = spe->sp->pc;
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_buf);
}
void
debug_dump_pc(void)
{
debug_dump_pc_one(debug_spe_focus);
}
void
debug_dump_pc_all(void)
{
struct debug_spe *spe;
for (spe = debug_spe_head; spe != NULL; spe = spe->next) {
print_spe_id(spe, "--- ", " ---\n");
debug_dump_pc_one(spe);
}
}
#define CHUNKSZ (sizeof (uint64_t))
int
debug_x(uint64_t addr, int chunks)
{
domain_t *domainp;
config_proc_t *config_procp;
config_addr_t *cap;
uint64_t extent;
uint64_t *bufp;
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);
if (cap == NULL) {
printf("0x%llx not mapped in domain\n", addr);
return (false);
}
extent = cap->config_devp->dev_typep->dev_cacheable(cap, DA_Load,
addr - cap->baseaddr, (uint8_t **)&bufp);
if (extent < chunks * CHUNKSZ) {
if (extent == 0) {
printf("0x%llx not cacheable memory in domain\n", addr);
return (false);
}
if (extent < CHUNKSZ) {
printf("0x%llx less than %d bytes to print\n",
addr, CHUNKSZ);
return (false);
}
chunks = extent / CHUNKSZ;
}
while (chunks--) {
printf("0x%llx\t0x%016llx\n", addr, *bufp);
addr += CHUNKSZ;
bufp++;
}
return (true);
}
/*
* Since the context is not specified for the translate command,
* check all entries that match the partition ID and print any
* that match.
*/
uint_t
debug_search_tlb(ss_strand_t *nsp, ss_tlb_t *tlbp, uint64_t va, char *nam)
{
tlb_entry_t *tep;
ulong_t partid, i;
uint_t count = 0;
partid = nsp->partid;
for (i = tlbp->nentries, tep = &(tlbp->tlb_entryp[0]);
i > 0; i--, tep++) {
if (tep->hashidx == -1) continue;
if (((tep->tag_pfn ^ va) >> tep->match_shift) == 0 &&
tep->partid == partid) {
uint64_t pa = va + tep->pa_offset;
if (tep->is_real)
printf("va:0x%016llx real -> pa:0x%016llx %s\n",
va, pa, nam);
else
printf("va:0x%016llx context:0x%x -> "
"pa:0x%016llx %s\n",
va, tep->tag_context, pa, nam);
count++;
}
}
return (count);
}
void
debug_print_icount(void)
{
simcpu_t *sp = debug_spe_focus->sp;
lprintf(sp->gid, "icount = 0x%lld\n", ICOUNT(sp));
}
void
debug_initiate_save_state_now(void)
{
simcpu_t *sp = debug_spe_focus->sp;
uint64_t now;
now = ICOUNT(sp);
options.save_restore.trigger_icount = now;
lprintf(sp->gid, "Debugger requested save_state. Current"
" icount=0x%llx\n", now);
}
void
debug_print_scratch(int id)
{
simcpu_t *sp;
sparcv9_cpu_t *v9p;
ss_strand_t *nsp;
int i;
struct debug_spe *spe;
if (id == -1) {
/*
* simscratch called with no id so we
* use the current cpu in focus
*/
spe = debug_spe_focus;
} else {
/* search for the specific gid */
for (spe = debug_spe_head; spe != NULL; spe = spe->next) {
if (spe->sp->gid == id)
break;
}
}
if (spe == NULL) {
printf("cpuid%d does not exist\n", id);
} else {
sp = spe->sp;
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]);
}
}
}
int
debug_trans(uint64_t va)
{
simcpu_t *sp;
sparcv9_cpu_t *v9p;
ss_strand_t *nsp;
ss_tlb_t *tlbp;
uint_t found;
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
* level 2 unified TLB.
*/
tlbp = nsp->itlbp;
found = debug_search_tlb(nsp, tlbp, va, "ITLB");
tlbp = nsp->dtlbp;
found += debug_search_tlb(nsp, tlbp, va, "DTLB");
#if defined(ROCK)
tlbp = nsp->tlbp;
found += debug_search_tlb(nsp, tlbp, va, "CTLB");
#endif
if (found == 0)
printf("va 0x%llx not found in TLBs\n", va);
return (true);
}
int
debug_dis(uint64_t addr, int chunks)
{
domain_t *domainp;
config_proc_t *config_procp;
config_addr_t *cap;
uint64_t extent;
uint32_t *bufp;
config_procp = debug_spe_focus->sp->config_procp;
domainp = config_procp->domainp;
addr &= ~3; /* force 4-byte alignment */
cap = find_domain_address(domainp, addr);
if (cap == NULL) {
return (false);
}
extent = cap->config_devp->dev_typep->dev_cacheable(cap, DA_Load,
addr - cap->baseaddr, (uint8_t **)&bufp);
if (extent < chunks * 4)
return (false);
while (chunks--) {
sparcv9_idis(debug_buf, sizeof (debug_buf),
FE_INSTN(*bufp), addr);
printf("0x%llx\t%08x\t%s\n", addr, FE_INSTN(*bufp), debug_buf);
addr += 4;
bufp++;
}
return (true);
}
void
debug_regs_one(struct debug_spe *spe)
{
sparcv9_cpu_t *v9p;
int i, j;
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++) {
printf("%d", i);
for (j = 0; j < 4; j++) {
printf(" 0x%016llx", spe->sp->intreg[i+j*8]);
}
printf("\n");
}
printf(" %%pc %%npc "
"%%tba %%htba\n");
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,
ss_read_pstate(v9p));
printf(" %%tpc %%tnpc "
"%%tstate %%tt\n");
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),
N_TT(v9p, i));
}
}
void
debug_regs(void)
{
debug_regs_one(debug_spe_focus);
}
void
debug_regs_all(void)
{
struct debug_spe *spe;
for (spe = debug_spe_head; spe != NULL; spe = spe->next) {
print_spe_id(spe, "--- ", " ---\n");
debug_regs_one(spe);
}
}
void
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;
#if defined(NIAGARA2)
printf("N2 CMP regs:\n");
printf("core_enable_status xir_steering core_run_status"
" tick_enable\n");
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);
#elif defined(ROCK)
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);
#else
printf("No CMP regs defined\n");
#endif
}
void
debug_cmpregs(void)
{
debug_cmpregs_one(debug_spe_focus);
}
void
debug_mmuregs_one(struct debug_spe *spe)
{
sparcv9_cpu_t *v9p;
ss_strand_t *strandp;
simcpu_t *sp;
ss_proc_t *pnp;
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);
#elif defined(ROCK)
printf("DCUCR: 0x%016llx\n", pnp->dcucr[strandp->core]);
printf("IFUCR: 0x%016llx\n", strandp->ifucr);
#else
#error "No valid processor defined."
#endif
#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);
#elif defined(ROCK)
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);
#else
#error "No valid processor defined."
#endif
printf("MMU fault TL : %d\n", v9p->tl);
if (v9p->tl != 0)
printf("MMU fault TPC: 0x%016llx\n", N_TPC(v9p, v9p->tl));
}
void
debug_mmuregs(void)
{
debug_mmuregs_one(debug_spe_focus);
}
void
debug_mmuregs_all(void)
{
struct debug_spe *spe;
for (spe = debug_spe_head; spe != NULL; spe = spe->next) {
print_spe_id(spe, "--- ", " ---\n");
debug_mmuregs_one(spe);
}
}
void
debug_dump_dtlb_one(struct debug_spe *spe)
{
#if defined(NIAGARA1) || defined(NIAGARA2)
ss_dump_tlbs_nolock(spe->sp->config_procp, true);
#elif defined(ROCK)
rock_dump_tlbs_nolock(spe->sp->config_procp, true);
#else
#error "No valid processor defined."
#endif
}
void
debug_dump_dtlb(void)
{
debug_dump_dtlb_one(debug_spe_focus);
}
void
debug_dump_dtlb_all(void)
{
struct debug_spe *spe;
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;
}
}
}
void
debug_dump_itlb_one(struct debug_spe *spe)
{
#if defined(NIAGARA1) || defined(NIAGARA2)
ss_dump_tlbs(spe->sp->config_procp, false);
#elif defined(ROCK)
rock_dump_tlbs(spe->sp->config_procp, false);
#else
#error "No valid processor defined."
#endif
}
void
debug_dump_itlb(void)
{
debug_dump_itlb_one(debug_spe_focus);
}
void
debug_dump_itlb_all(void)
{
struct debug_spe *spe;
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;
}
}
}
void
debug_log(simcpu_t *sp, uint32_t rawi)
{
struct debug_spe *spe;
sparcv9_cpu_t *v9p;
int idx;
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;
spe->log_count++;
}
void
debug_log_print(void)
{
struct debug_spe *spe;
for (spe = debug_spe_head; spe != NULL; spe = spe->next) {
int i;
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",
/* CSTYLED */
spe->log[idx].count == (spe->log_count - 1) ? "-> " : " ",
spe->log[idx].count,
sparcv9_state_name[spe->log[idx].state],
spe->log[idx].tl,
spe->log[idx].tt,
spe->log[idx].gl,
spe->log[idx].pc,
FE_INSTN(spe->log[idx].rawi),
debug_buf);
}
printf("\n");
}
}
#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.
*/
void
check_if_error_event_pending()
{
struct debug_spe *spe;
for (spe = debug_spe_head; spe != NULL; spe = spe->next) {
simcpu_t *sp = spe->sp;
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) {
continue;
}
/*
* Make sure if an instn_cnt was specified, that we have
* reached that
*/
if (sp->error_cycle_reached == false) {
continue;
}
/* Check if error event has a priv level specified */
if ((eep->options.bits.priv) && (eep->priv != v9p->state)) {
continue;
}
/* Check if error event has a trap level specified */
if ((eep->options.bits.tl) && (eep->tl != v9p->tl)) {
continue;
}
/*
* We get here because all constraints have been met,
* so we trigger the trap
*/
/* CSTYLED */
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);
/* CSTYLED */
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: ##"
#define TARE 7
void
debug_dump_breakpoints(char *fname)
{
extern clibuf_t inputCliLine;
FILE *fp;
if ((fp = fopen(fname, "w")) == NULL) {
perror("debug_dump_breakpoints:fopen");
return;
}
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);
if (delta > 0) {
for (; delta > 0 && cliend < sizeof (inputCliLine);
delta--) {
inputCliLine[cliend++] = ' ';
}
inputCliLine[cliend] = '\0';
}
fprintf(fp, "## %s ##\n", inputCliLine);
fprintf(fp, "%s\n", BARS);
sparcv9_dump_break(
(sparcv9_cpu_t *)debug_spe_head->sp->specificp, fp);
}
fclose(fp);
}
void
debug_restore_breakpoints(char *fname)
{
FILE *fp;
if ((fp = fopen(fname, "r")) == NULL) {
perror("debug_restore_breakpoints:fopen");
return;
}
sparcv9_restore_break(fp);
fclose(fp);
}