// SPDX-License-Identifier: GPL-2.0-or-later /* * Routines providing a simple monitor for use on the PowerMac. * * Copyright (C) 1996-2005 Paul Mackerras. * Copyright (C) 2001 PPC64 Team, IBM Corp * Copyrignt (C) 2006 Michael Ellerman, IBM Corp
*/
staticconstchar *xmon_ro_msg = "Operation disabled: xmon in read-only mode\n";
staticchar *help_string = "\
Commands:\n\
b show breakpoints\n\
bd set data breakpoint\n\
bi set instruction breakpoint\n\
bc clear breakpoint\n" #ifdef CONFIG_SMP "\
c print cpus stopped in xmon\n\
c# try to switch to cpu number h (in hex)\n\
c# $ run command '$' (one of 'r','S'or't') on all cpus in xmon\n" #endif "\
C checksum\n\
d dump bytes\n\
d1 dump 1 byte values\n\
d2 dump 2 byte values\n\
d4 dump 4 byte values\n\
d8 dump 8 byte values\n\
di dump instructions\n\
df dump float values\n\
dd dump double values\n\
dl dump the kernel log buffer\n" #ifdef CONFIG_PPC_POWERNV "\ do dump the OPAL message log\n" #endif #ifdef CONFIG_PPC64 "\
dp[#] dump paca for current cpu, or cpu #\n\
dpa dump paca for all possible cpus\n" #endif "\
dr dump stream of raw bytes\n\
dv dump virtual address translation \n\
dt dump the tracing buffers (uses printk)\n\
dtc dump the tracing buffers for current CPU (uses printk)\n\ " #ifdef CONFIG_PPC_POWERNV " dx# dump xive on CPU #\n\
dxi# dump xive irq state #\n\
dxa dump xive on all CPUs\n" #endif " e print exception information\n\
f flush cache\n\
la lookup symbol+offset of specified address\n\
ls lookup address of specified symbol\n\
lp s [#] lookup address of percpu symbol s for current cpu, or cpu #\n\
m examine/change memory\n\
mm move a block of memory\n\
ms set a block of memory\n\
md compare two blocks of memory\n\
ml locate a block of memory\n\
mz zero a block of memory\n\
mi show information about memory allocation\n\
p call a procedure\n\
P list processes/tasks\n\
r print registers\n\
s single step\n" " S print special registers\n\
Sa print all SPRs\n\
Sr # read SPR #\n\
Sw #v write v to SPR #\n\
t print backtrace\n\
x exit monitor and recover\n\
X exit monitor and don't recover\n" #ifdefined(CONFIG_PPC_BOOK3S_64) " u dump segment table or SLB\n" #elifdefined(CONFIG_PPC_BOOK3S_32) " u dump segment registers\n" #elifdefined(CONFIG_44x) || defined(CONFIG_PPC_BOOK3E_64) " u dump TLB\n" #endif " U show uptime information\n" " ? help\n" " # n limit output to n lines per page (for dp, dpa, dl)\n" " zr reboot\n" " zh halt\n"
;
if (!lockdown) {
lockdown = !!security_locked_down(LOCKDOWN_XMON_RW); if (lockdown) {
printf("xmon: Disabled due to kernel lockdown\n");
xmon_is_ro = true;
}
}
if (!xmon_is_ro) {
xmon_is_ro = !!security_locked_down(LOCKDOWN_XMON_WR); if (xmon_is_ro)
printf("xmon: Read-only due to kernel lockdown\n");
}
/** * write_ciabr() - write the CIABR SPR * @ciabr: The value to write. * * This function writes a value to the CIARB register either directly * through mtspr instruction if the kernel is in HV privilege mode or * call a hypervisor function to achieve the same in case the kernel * is in supervisor privilege mode.
*/ staticvoid write_ciabr(unsignedlong ciabr)
{ if (!cpu_has_feature(CPU_FTR_ARCH_207S)) return;
if (cpu_has_feature(CPU_FTR_HVMODE)) {
mtspr(SPRN_CIABR, ciabr); return;
}
plpar_set_ciabr(ciabr);
}
/** * set_ciabr() - set the CIABR * @addr: The value to set. * * This function sets the correct privilege value into the HW * breakpoint address before writing it up in the CIABR register.
*/ staticvoid set_ciabr(unsignedlong addr)
{
addr &= ~CIABR_PRIV;
/* * Disable surveillance (the service processor watchdog function) * while we are in xmon. * XXX we should re-enable it when we leave. :)
*/ #define SURVEILLANCE_TOKEN 9000
staticinlinevoid disable_surveillance(void)
{ #ifdef CONFIG_PPC_PSERIES /* Since this can't be a module, args should end up below 4GB. */ staticstruct rtas_args args; const s32 token = rtas_function_token(RTAS_FN_SET_INDICATOR);
/* * At this point we have got all the cpus we can into * xmon, so there is hopefully no other cpu calling RTAS * at the moment, even though we don't take rtas.lock. * If we did try to take rtas.lock there would be a * real possibility of deadlock.
*/ if (token == RTAS_UNKNOWN_SERVICE) return;
staticvoid get_output_lock(void)
{ int me = smp_processor_id() + 0x100; int last_speaker = 0, prev; long timeout;
if (xmon_speaker == me) return;
for (;;) {
last_speaker = cmpxchg(&xmon_speaker, 0, me); if (last_speaker == 0) return;
/* * Wait a full second for the lock, we might be on a slow * console, but check every 100us.
*/
timeout = 10000; while (xmon_speaker == last_speaker) { if (--timeout > 0) {
udelay(100); continue;
}
/* We wait for 2s, which is a metric "little while" */ for (timeout = 20000; timeout != 0; --timeout) { if (cpumask_weight(&cpus_in_xmon) >= ncpus) returntrue;
udelay(100);
barrier();
}
staticint xmon_core(struct pt_regs *regs, volatileint fromipi)
{ volatileint cmd = 0; struct bpt *volatile bp; long recurse_jmp[JMP_BUF_LEN]; bool locked_down; unsignedlong offset; unsignedlong flags; #ifdef CONFIG_SMP int cpu; int secondary; #endif
local_irq_save(flags);
hard_irq_disable();
locked_down = xmon_is_locked_down();
if (!fromipi) {
tracing_enabled = tracing_is_on();
tracing_off();
}
bp = in_breakpoint_table(regs->nip, &offset); if (bp != NULL) {
regs_set_return_ip(regs, bp->address + offset);
atomic_dec(&bp->ref_count);
}
remove_cpu_bpts();
#ifdef CONFIG_SMP
cpu = smp_processor_id(); if (cpumask_test_cpu(cpu, &cpus_in_xmon)) { /* * We catch SPR read/write faults here because the 0x700, 0xf60 * etc. handlers don't call debugger_fault_handler().
*/ if (catch_spr_faults)
longjmp(bus_error_jmp, 1);
get_output_lock();
excprint(regs);
printf("cpu 0x%x: Exception %lx %s in xmon, " "returning to main loop\n",
cpu, regs->trap, getvecname(TRAP(regs)));
release_output_lock();
longjmp(xmon_fault_jmp[cpu], 1);
}
if (setjmp(recurse_jmp) != 0) { if (!in_xmon || !xmon_gate) {
get_output_lock();
printf("xmon: WARNING: bad recursive fault " "on cpu 0x%x\n", cpu);
release_output_lock(); goto waiting;
}
secondary = !(xmon_taken && cpu == xmon_owner); goto cmdloop;
}
xmon_fault_jmp[cpu] = recurse_jmp;
bp = NULL; if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) == (MSR_IR|MSR_64BIT))
bp = at_breakpoint(regs->nip); if (bp || regs_is_unrecoverable(regs))
fromipi = 0;
if (!fromipi) {
get_output_lock(); if (!locked_down)
excprint(regs); if (bp) {
printf("cpu 0x%x stopped at breakpoint 0x%tx (",
cpu, BP_NUM(bp));
xmon_print_symbol(regs->nip, " ", ")\n");
} if (regs_is_unrecoverable(regs))
printf("WARNING: exception is not recoverable, " "can't continue\n");
release_output_lock();
}
if (!secondary && !xmon_gate) { /* we are the first cpu to come in */ /* interrupt other cpu(s) */ int ncpus = num_online_cpus();
xmon_owner = cpu;
mb(); if (ncpus > 1) { /* * A system reset (trap == 0x100) can be triggered on * all CPUs, so when we come in via 0x100 try waiting * for the other CPUs to come in before we send the * debugger break (IPI). This is similar to * crash_kexec_secondary().
*/ if (TRAP(regs) != INTERRUPT_SYSTEM_RESET || !wait_for_other_cpus(ncpus))
smp_send_debugger_break();
if (!locked_down) { /* for breakpoint or single step, print curr insn */ if (bp || TRAP(regs) == INTERRUPT_TRACE)
ppc_inst_dump(regs->nip, 1, 0);
printf("enter ? for help\n");
}
cmdloop: while (in_xmon) { if (secondary) {
spin_begin(); if (cpu == xmon_owner) { if (!test_and_set_bit(0, &xmon_taken)) {
secondary = 0;
spin_end(); continue;
} /* missed it */ while (cpu == xmon_owner)
spin_cpu_relax();
}
spin_cpu_relax();
touch_nmi_watchdog();
} else {
cmd = 1; if (xmon_batch)
cmd = batch_cmds(regs); if (!locked_down && cmd)
cmd = cmds(regs); if (locked_down || cmd != 0) { /* exiting xmon */
insert_bpts();
xmon_gate = 0;
wmb();
in_xmon = 0; break;
} /* have switched to some other cpu */
secondary = 1;
}
}
leave:
cpumask_clear_cpu(cpu, &cpus_in_xmon);
xmon_fault_jmp[cpu] = NULL; #else /* UP is simple... */ if (in_xmon) {
printf("Exception %lx %s in xmon, returning to main loop\n",
regs->trap, getvecname(TRAP(regs)));
longjmp(xmon_fault_jmp[0], 1);
} if (setjmp(recurse_jmp) == 0) {
xmon_fault_jmp[0] = recurse_jmp;
in_xmon = 1;
excprint(regs);
bp = at_breakpoint(regs->nip); if (bp) {
printf("Stopped at breakpoint %tx (", BP_NUM(bp));
xmon_print_symbol(regs->nip, " ", ")\n");
} if (regs_is_unrecoverable(regs))
printf("WARNING: exception is not recoverable, " "can't continue\n");
remove_bpts();
disable_surveillance(); if (!locked_down) { /* for breakpoint or single step, print current insn */ if (bp || TRAP(regs) == INTERRUPT_TRACE)
ppc_inst_dump(regs->nip, 1, 0);
printf("enter ? for help\n");
}
}
if (!locked_down)
cmd = cmds(regs);
insert_bpts();
in_xmon = 0; #endif
#ifdef CONFIG_BOOKE if (regs->msr & MSR_DE) {
bp = at_breakpoint(regs->nip); if (bp != NULL) {
regs_set_return_ip(regs, (unsignedlong) &bp->instr[0]);
atomic_inc(&bp->ref_count);
}
} #else if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) == (MSR_IR|MSR_64BIT)) {
bp = at_breakpoint(regs->nip); if (bp != NULL) { int stepped = emulate_step(regs, ppc_inst_read(bp->instr)); if (stepped == 0) {
regs_set_return_ip(regs, (unsignedlong) &bp->instr[0]);
atomic_inc(&bp->ref_count);
} elseif (stepped < 0) {
printf("Couldn't single-step %s instruction\n",
IS_RFID(ppc_inst_read(bp->instr))? "rfid": "mtmsrd");
}
}
} #endif if (locked_down)
clear_all_bpt(); else
insert_cpu_bpts();
xmon_touch_watchdogs();
local_irq_restore(flags);
return cmd != 'X' && cmd != EOF;
}
int xmon(struct pt_regs *excp)
{ struct pt_regs regs;
if (excp == NULL) {
ppc_save_regs(®s);
excp = ®s;
}
if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT)) return 0;
/* Are we at the trap at bp->instr[1] for some bp? */
bp = in_breakpoint_table(regs->nip, &offset); if (bp != NULL && (offset == 4 || offset == 8)) {
regs_set_return_ip(regs, bp->address + offset);
atomic_dec(&bp->ref_count); return 1;
}
/* Are we at a breakpoint? */
bp = at_breakpoint(regs->nip); if (!bp) return 0;
staticint xmon_break_match(struct pt_regs *regs)
{ int i;
if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT)) return 0; for (i = 0; i < nr_wp_slots(); i++) { if (dabr[i].enabled) goto found;
} return 0;
bp = bpts; for (i = 0; i < NBPTS; ++i, ++bp) { if ((bp->enabled & (BP_TRAP|BP_CIABR)) == 0) continue; if (!mread_instr(bp->address, &instr)) {
printf("Couldn't read instruction at %lx, " "disabling breakpoint there\n", bp->address);
bp->enabled = 0; continue;
} if (!can_single_step(ppc_inst_val(instr))) {
printf("Breakpoint at %lx is on an instruction that can't be single stepped, disabling it\n",
bp->address);
bp->enabled = 0; continue;
} /* * Check the address is not a suffix by looking for a prefix in * front of it.
*/ if (mread_instr(bp->address - 4, &instr2) == 8) {
printf("Breakpoint at %lx is on the second word of a prefixed instruction, disabling it\n",
bp->address);
bp->enabled = 0; continue;
} /* * We might still be a suffix - if the prefix has already been * replaced by a breakpoint we won't catch it with the above * test.
*/
bp2 = at_breakpoint(bp->address - 4); if (bp2 && ppc_inst_prefixed(ppc_inst_read(bp2->instr))) {
printf("Breakpoint at %lx is on the second word of a prefixed instruction, disabling it\n",
bp->address);
bp->enabled = 0; continue;
}
if (!scanhex(&adrs)) return; if (!scanhex(&ncsum)) return;
fcs = 0xffff; for (i = 0; i < ncsum; ++i) { if (mread(adrs+i, &v, 1) == 0) {
printf("csum stopped at "REG"\n", adrs+i); break;
}
fcs = FCS(fcs, v);
}
printf("%x\n", fcs);
}
/* * Check if this is a suitable place to put a breakpoint.
*/ staticlong check_bp_loc(unsignedlong addr)
{
ppc_inst_t instr;
addr &= ~3; if (!is_kernel_addr(addr)) {
printf("Breakpoints may only be placed at kernel addresses\n"); return 0;
} if (!mread_instr(addr, &instr)) {
printf("Can't read instruction at address %lx\n", addr); return 0;
} if (!can_single_step(ppc_inst_val(instr))) {
printf("Breakpoints may not be placed on instructions that can't be single stepped\n"); return 0;
} return 1;
}
staticint find_free_data_bpt(void)
{ int i;
for (i = 0; i < nr_wp_slots(); i++) { if (!dabr[i].enabled) return i;
}
printf("Couldn't find free breakpoint register\n"); return -1;
}
staticvoid print_data_bpts(void)
{ int i;
for (i = 0; i < nr_wp_slots(); i++) { if (!dabr[i].enabled) continue;
printf(" data "REG" [", dabr[i].address); if (dabr[i].enabled & 1)
printf("r"); if (dabr[i].enabled & 2)
printf("w");
printf("]\n");
}
}
staticchar *breakpoint_help_string = "Breakpoint command usage:\n" "b show breakpoints\n" "b <addr> [cnt] set breakpoint at given instr addr\n" "bc clear all breakpoints\n" "bc <n/addr> clear breakpoint number n or at addr\n" "bi <addr> [cnt] set hardware instr breakpoint (POWER8 only)\n" "bd <addr> [cnt] set hardware data breakpoint\n" "";
staticvoid
bpt_cmds(void)
{ int cmd; unsignedlong a; int i; struct bpt *bp;
cmd = inchar();
switch (cmd) { case'd': { /* bd - hardware data breakpoint */ staticconstchar badaddr[] = "Only kernel addresses are permitted for breakpoints\n"; int mode; if (xmon_is_ro) {
printf(xmon_ro_msg); break;
} if (!ppc_breakpoint_available()) {
printf("Hardware data breakpoint not supported on this cpu\n"); break;
}
i = find_free_data_bpt(); if (i < 0) break;
mode = 7;
cmd = inchar(); if (cmd == 'r')
mode = 5; elseif (cmd == 'w')
mode = 6; else
termch = cmd;
dabr[i].address = 0;
dabr[i].enabled = 0; if (scanhex(&dabr[i].address)) { if (!is_kernel_addr(dabr[i].address)) {
printf(badaddr); break;
}
dabr[i].address &= ~HW_BRK_TYPE_DABR;
dabr[i].enabled = mode | BP_DABR;
}
force_enable_xmon(); break;
}
case'i': /* bi - hardware instr breakpoint */ if (xmon_is_ro) {
printf(xmon_ro_msg); break;
} if (!cpu_has_feature(CPU_FTR_ARCH_207S)) {
printf("Hardware instruction breakpoint " "not supported on this cpu\n"); break;
} if (iabr) {
iabr->enabled &= ~BP_CIABR;
iabr = NULL;
} if (!scanhex(&a)) break; if (!check_bp_loc(a)) break;
bp = new_breakpoint(a); if (bp != NULL) {
bp->enabled |= BP_CIABR;
iabr = bp;
force_enable_xmon();
} break;
case'c': if (!scanhex(&a)) { /* clear all breakpoints */ for (i = 0; i < NBPTS; ++i)
bpts[i].enabled = 0;
iabr = NULL; for (i = 0; i < nr_wp_slots(); i++)
dabr[i].enabled = 0;
printf("All breakpoints cleared\n"); break;
}
if (a <= NBPTS && a >= 1) { /* assume a breakpoint number */
bp = &bpts[a-1]; /* bp nums are 1 based */
} else { /* assume a breakpoint address */
bp = at_breakpoint(a); if (bp == NULL) {
printf("No breakpoint at %lx\n", a); break;
}
}
if (xmon_is_ro || !scanhex(&a)) { /* print all breakpoints */
printf(" type address\n");
print_data_bpts(); for (bp = bpts; bp < &bpts[NBPTS]; ++bp) { if (!bp->enabled) continue;
printf("%tx %s ", BP_NUM(bp),
(bp->enabled & BP_CIABR) ? "inst": "trap");
xmon_print_symbol(bp->address, " ", "\n");
} break;
}
if (!check_bp_loc(a)) break;
bp = new_breakpoint(a); if (bp != NULL) {
bp->enabled |= BP_TRAP;
force_enable_xmon();
} break;
}
}
/* Very cheap human name for vector lookup. */ static constchar *getvecname(unsignedlong vec)
{ char *ret;
switch (vec) { case 0x100: ret = "(System Reset)"; break; case 0x200: ret = "(Machine Check)"; break; case 0x300: ret = "(Data Access)"; break; case 0x380: if (radix_enabled())
ret = "(Data Access Out of Range)"; else
ret = "(Data SLB Access)"; break; case 0x400: ret = "(Instruction Access)"; break; case 0x480: if (radix_enabled())
ret = "(Instruction Access Out of Range)"; else
ret = "(Instruction SLB Access)"; break; case 0x500: ret = "(Hardware Interrupt)"; break; case 0x600: ret = "(Alignment)"; break; case 0x700: ret = "(Program Check)"; break; case 0x800: ret = "(FPU Unavailable)"; break; case 0x900: ret = "(Decrementer)"; break; case 0x980: ret = "(Hypervisor Decrementer)"; break; case 0xa00: ret = "(Doorbell)"; break; case 0xc00: ret = "(System Call)"; break; case 0xd00: ret = "(Single Step)"; break; case 0xe40: ret = "(Emulation Assist)"; break; case 0xe60: ret = "(HMI)"; break; case 0xe80: ret = "(Hypervisor Doorbell)"; break; case 0xf00: ret = "(Performance Monitor)"; break; case 0xf20: ret = "(Altivec Unavailable)"; break; case 0x1300: ret = "(Instruction Breakpoint)"; break; case 0x1500: ret = "(Denormalisation)"; break; case 0x1700: ret = "(Altivec Assist)"; break; case 0x3000: ret = "(System Call Vectored)"; break; default: ret = "";
} return ret;
}
while (max_to_print--) { if (!is_kernel_addr(sp)) { if (sp != 0)
printf("SP (%lx) is in userspace\n", sp); break;
}
if (!mread(sp + LRSAVE_OFFSET, &ip, sizeof(unsignedlong))
|| !mread(sp, &newsp, sizeof(unsignedlong))) {
printf("Couldn't read stack frame at %lx\n", sp); break;
}
/* * For the first stack frame, try to work out if * LR and/or the saved LR value in the bottommost * stack frame are valid.
*/ if ((pc | lr) != 0) { unsignedlong fnstart, fnend; unsignedlong nextip; int printip = 1;
get_function_bounds(pc, &fnstart, &fnend);
nextip = 0; if (newsp > sp)
mread(newsp + LRSAVE_OFFSET, &nextip, sizeof(unsignedlong)); if (lr == ip) { if (!is_kernel_addr(lr)
|| (fnstart <= lr && lr < fnend))
printip = 0;
} elseif (lr == nextip) {
printip = 0;
} elseif (is_kernel_addr(lr)
&& !(fnstart <= lr && lr < fnend)) {
printf("[link register ] ");
xmon_print_symbol(lr, " ", "\n");
} if (printip) {
printf("["REG"] ", sp);
xmon_print_symbol(ip, " ", " (unreliable)\n");
}
pc = lr = 0;
/* Look for "regs" marker to see if this is
an exception frame. */ if (mread(sp + STACK_INT_FRAME_MARKER, &marker, sizeof(unsignedlong))
&& marker == STACK_FRAME_REGS_MARKER) { if (mread(sp + STACK_INT_FRAME_REGS, ®s, sizeof(regs)) != sizeof(regs)) {
printf("Couldn't read registers at %lx\n",
sp + STACK_INT_FRAME_REGS); break;
}
printf("---- Exception: %lx %s at ", regs.trap,
getvecname(TRAP(®s)));
pc = regs.nip;
lr = regs.link;
xmon_print_symbol(pc, " ", "\n");
}
if (cmd != 'i' || IS_ENABLED(CONFIG_PPC_BOOK3S_64)) { for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
cflush((void *) adrs);
} else { for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
cinval((void *) adrs);
}
sync(); /* wait a little while to see if we get a machine check */
__delay(200);
}
catch_memory_errors = 0;
}
msr = mfmsr(); if (msr & MSR_TM) { /* Only if TM has been enabled in the kernel */
printf("tfhar = %.16lx tfiar = %.16lx texasr = %.16lx\n",
mfspr(SPRN_TFHAR), mfspr(SPRN_TFIAR),
mfspr(SPRN_TEXASR));
}
val = 0xdeadbeef; if (!read_spr(spr, &val)) {
printf("SPR 0x%03x (%4d) Faulted during read\n", spr, spr); return;
}
if (val == 0xdeadbeef) { /* Looks like read was a nop, confirm */
val = 0x0badcafe; if (!read_spr(spr, &val)) {
printf("SPR 0x%03x (%4d) Faulted during read\n", spr, spr); return;
}
if (val == 0x0badcafe) { if (show_unimplemented)
printf("SPR 0x%03x (%4d) Unimplemented\n", spr, spr); return;
}
}
n = 0; if (setjmp(bus_error_jmp) == 0) {
catch_memory_errors = 1;
sync();
*instr = ppc_inst_read((u32 *)adrs);
sync(); /* wait a little while to see if we get a machine check */
__delay(200);
n = ppc_inst_len(*instr);
}
catch_memory_errors = 0; return n;
}
staticvoid
byterev(unsignedchar *val, int size)
{ int t;
switch (size) { case 2:
SWAP(val[0], val[1], t); break; case 4:
SWAP(val[0], val[3], t);
SWAP(val[1], val[2], t); break; case 8: /* is there really any use for this? */
SWAP(val[0], val[7], t);
SWAP(val[1], val[6], t);
SWAP(val[2], val[5], t);
SWAP(val[3], val[4], t); break;
}
}
staticint brev; staticint mnoread;
staticchar *memex_help_string = "Memory examine command usage:\n" "m [addr] [flags] examine/change memory\n" " addr is optional. will start where left off.\n" " flags may include chars from this set:\n" " b modify by bytes (default)\n" " w modify by words (2 byte)\n" " l modify by longs (4 byte)\n" " d modify by doubleword (8 byte)\n" " r toggle reverse byte order mode\n" " n do not read memory (for i/o spaces)\n" " . ok to read (default)\n" "NOTE: flags are saved as defaults\n" "";
staticchar *memex_subcmd_help_string = "Memory examine subcommands:\n" " hexval write this val to current location\n" " 'string' write chars from string to this location\n" " ' increment address\n" " ^ decrement address\n" " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n" " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n" " ` clear no-read flag\n" " ; stay at this addr\n" " v change to byte mode\n" " w change to word (2 byte) mode\n" " l change to long (4 byte) mode\n" " u change to doubleword (8 byte) mode\n" " m addr change current addr\n" " n toggle no-read flag\n" " r toggle byte reverse flag\n" " < count back up count bytes\n" " > count skip forward count bytes\n" " x exit this mode\n" "";
staticvoid
memex(void)
{ int cmd, inc, i, nslash; unsignedlong n; unsignedchar val[16];
if (!early_cpu_has_feature(CPU_FTR_ARCH_300)) {
DUMP(p, slb_cache_ptr, "%#-*x"); for (i = 0; i < SLB_CACHE_ENTRIES; i++)
printf(" %-*s[%d] = 0x%016x\n",
22, "slb_cache", i, p->slb_cache[i]);
}
} #endif
if (num_online_cpus() == 0) {
printf("No possible cpus, use 'dx #' to dump individual cpus\n"); return;
}
for_each_online_cpu(cpu)
dump_one_xive(cpu);
}
staticvoid dump_xives(void)
{ unsignedlong num; int c;
if (!xive_enabled()) {
printf("Xive disabled on this system\n"); return;
}
c = inchar(); if (c == 'a') {
dump_all_xives(); return;
} elseif (c == 'i') { if (scanhex(&num))
xmon_xive_get_irq_config(num, NULL); else
xmon_xive_get_irq_all(); return;
}
termch = c; /* Put c back, it wasn't 'a' */
if (scanhex(&num) && num < num_possible_cpus())
dump_one_xive(num); else
dump_one_xive(xmon_owner);
} #endif/* CONFIG_PPC_POWERNV */
staticvoid dump_by_size(unsignedlong addr, long count, int size)
{ unsignedchar temp[16]; int i, j;
u64 val;
count = ALIGN(count, 16);
for (i = 0; i < count; i += 16, addr += 16) {
printf(REG, addr);
if (mread(addr, temp, 16) != 16) {
printf("\nFaulted reading %d bytes from 0x"REG"\n", 16, addr); return;
}
for (j = 0; j < 16; j += size) {
putchar(' '); switch (size) { case 1: val = temp[j]; break; case 2: val = *(u16 *)&temp[j]; break; case 4: val = *(u32 *)&temp[j]; break; case 8: val = *(u64 *)&temp[j]; break; default: val = 0;
}
printf("%0*llx", size * 2, val);
}
printf(" |"); for (j = 0; j < 16; ++j) {
val = temp[j];
putchar(' ' <= val && val <= '~' ? val : '.');
}
printf("|\n");
}
}
staticvoid
memzcan(void)
{ unsignedchar v; unsigned a; int ok, ook;
scanhex(&mdest); if (termch != '\n') termch = 0;
scanhex(&mskip); if (termch != '\n') termch = 0;
scanhex(&mlim);
ook = 0; for (a = mdest; a < mlim; a += mskip) {
ok = mread(a, &v, 1); if (ok && !ook) {
printf("%.8x .. ", a);
} elseif (!ok && ook)
printf("%.8lx\n", a - mskip);
ook = ok; if (a + mskip < a) break;
} if (ook)
printf("%.8lx\n", a - mskip);
}
/* * Cloned from kdb_task_state_char(), which is not entirely * appropriate for calling from xmon. This could be moved * to a common, generic, routine used by both.
*/
state = (p_state == TASK_RUNNING) ? 'R' :
(p_state & TASK_UNINTERRUPTIBLE) ? 'D' :
(p_state & TASK_STOPPED) ? 'T' :
(p_state & TASK_TRACED) ? 'C' :
(tsk->exit_state & EXIT_ZOMBIE) ? 'Z' :
(tsk->exit_state & EXIT_DEAD) ? 'E' :
(p_state & TASK_INTERRUPTIBLE) ? 'S' : '?';
int
scanhex(unsignedlong *vp)
{ int c, d; unsignedlong v;
c = skipbl(); if (c == '%') { /* parse register name */ char regname[8]; int i;
for (i = 0; i < sizeof(regname) - 1; ++i) {
c = inchar(); if (!isalnum(c)) {
termch = c; break;
}
regname[i] = c;
}
regname[i] = 0;
i = match_string(regnames, N_PTREGS, regname); if (i < 0) {
printf("invalid register name '%%%s'\n", regname); return 0;
} if (xmon_regs == NULL) {
printf("regs not available\n"); return 0;
}
*vp = ((unsignedlong *)xmon_regs)[i]; return 1;
}
/* skip leading "0x" if any */
if (c == '0') {
c = inchar(); if (c == 'x') {
c = inchar();
} else {
d = hexdigit(c); if (d == EOF) {
termch = c;
*vp = 0; return 1;
}
}
} elseif (c == '$') { int i; for (i = 0; i < (KSYM_NAME_LEN - 1); i++) {
c = inchar(); if (isspace(c) || c == '\0') {
termch = c; break;
}
tmpstr[i] = c;
}
tmpstr[i++] = 0;
*vp = 0; if (setjmp(bus_error_jmp) == 0) {
catch_memory_errors = 1;
sync();
*vp = kallsyms_lookup_name(tmpstr);
sync();
}
catch_memory_errors = 0; if (!(*vp)) {
printf("unknown symbol '%s'\n", tmpstr); return 0;
} return 1;
}
d = hexdigit(c); if (d == EOF) {
termch = c; return 0;
}
v = 0; do {
v = (v << 4) + d;
c = inchar();
d = hexdigit(c);
} while (d != EOF);
termch = c;
*vp = v; return 1;
}
staticvoid
scannl(void)
{ int c;
c = termch;
termch = 0; while( c != '\n' )
c = inchar();
}
staticint hexdigit(int c)
{ if( '0' <= c && c <= '9' ) return c - '0'; if( 'A' <= c && c <= 'F' ) return c - ('A' - 10); if( 'a' <= c && c <= 'f' ) return c - ('a' - 10); return EOF;
}
void
getstring(char *s, int size)
{ int c;
c = skipbl(); if (c == '\n') {
*s = 0; return;
}
do { if( size > 1 ){
*s++ = c;
--size;
}
c = inchar();
} while( c != ' ' && c != '\t' && c != '\n' );
termch = c;
*s = 0;
}
if (ptr &&
ptr >= (void __percpu *)__per_cpu_start &&
ptr < (void __percpu *)__per_cpu_end)
{ if (scanhex(&cpu) && cpu < num_possible_cpus()) {
addr = (unsignedlong)per_cpu_ptr(ptr, cpu);
} else {
cpu = raw_smp_processor_id();
addr = (unsignedlong)this_cpu_ptr(ptr);
}
printf("%s for cpu 0x%lx: %lx\n", tmp, cpu, addr);
} else {
printf("Percpu symbol '%s' not found.\n", tmp);
}
catch_memory_errors = 0;
termch = 0; break;
}
}
/* Print an address in numeric and symbolic form (if possible) */ staticvoid xmon_print_symbol(unsignedlong address, constchar *mid, constchar *after)
{ char *modname; constchar *volatile name = NULL; unsignedlong offset, size;
printf(REG, address); if (setjmp(bus_error_jmp) == 0) {
catch_memory_errors = 1;
sync();
name = kallsyms_lookup(address, &size, &offset, &modname,
tmpstr);
sync(); /* wait a little while to see if we get a machine check */
__delay(200);
}
catch_memory_errors = 0;
if (name) {
printf("%s%s+%#lx/%#lx", mid, name, offset, size); if (modname)
printf(" [%s]", modname);
}
printf("%s", after);
}
/* make sure all breakpoints removed when disabling */ if (!xmon_on) {
clear_all_bpt();
get_output_lock();
printf("xmon: All breakpoints cleared\n");
release_output_lock();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.