mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
1721 lines
45 KiB
C
1721 lines
45 KiB
C
/* tools/debugger/xenitp.c - A low-level debugger.
|
|
|
|
Based on xenctxt.c, but heavily modified.
|
|
Copyright 2007 Tristan Gingold <tgingold@free.fr>
|
|
|
|
Xenitp is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Xenitp 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 program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <sys/mman.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <argp.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
|
|
#include "xenctrl.h"
|
|
#include <xen/sys/privcmd.h>
|
|
#include "dis-asm.h"
|
|
|
|
#ifdef __HYPERVISOR_ia64_debug_op
|
|
#define HAVE_DEBUG_OP
|
|
#include <xen/arch-ia64/debug_op.h>
|
|
#endif
|
|
|
|
static int xc_handle = 0;
|
|
static int domid = 0;
|
|
static vcpu_guest_context_t *cur_ctx;
|
|
static int cur_vcpu;
|
|
|
|
#define PSR_BN (1UL << 44)
|
|
#define PSR_SS (1UL << 40)
|
|
#define PSR_DB (1UL << 24)
|
|
#define PSR_TB (1UL << 26)
|
|
#define PSR_DD (1UL << 39)
|
|
#define PSR_ID (1UL << 37)
|
|
#define PSR_IT (1UL << 36)
|
|
#define PSR_DT (1UL << 17)
|
|
#define PSR_RI_SHIFT 41
|
|
#define CFM_SOF_MASK 0x3f
|
|
|
|
int virt_to_phys (int is_inst, unsigned long vaddr, unsigned long *paddr);
|
|
|
|
/* wrapper for vcpu_gest_context_any_t */
|
|
static int xc_ia64_vcpu_getcontext(int xc_handle,
|
|
uint32_t domid,
|
|
uint32_t vcpu,
|
|
vcpu_guest_context_t *ctxt)
|
|
{
|
|
return xc_vcpu_getcontext(xc_handle, domid, vcpu,
|
|
(vcpu_guest_context_any_t *)ctxt);
|
|
}
|
|
|
|
static inline unsigned int ctx_slot (vcpu_guest_context_t *ctx)
|
|
{
|
|
return (ctx->regs.psr >> PSR_RI_SHIFT) & 3;
|
|
}
|
|
|
|
unsigned char *
|
|
target_map_memory (unsigned long paddr)
|
|
{
|
|
static unsigned long cur_page = (unsigned long)-1;
|
|
static unsigned char *cur_map = NULL;
|
|
|
|
if ((paddr >> XC_PAGE_SHIFT) != cur_page) {
|
|
if (cur_map) {
|
|
munmap (cur_map, XC_PAGE_SIZE);
|
|
cur_map = NULL;
|
|
}
|
|
cur_page = paddr >> XC_PAGE_SHIFT;
|
|
cur_map = xc_map_foreign_range (xc_handle, domid, XC_PAGE_SIZE,
|
|
PROT_READ, cur_page);
|
|
if (cur_map == NULL) {
|
|
perror ("cannot map page");
|
|
cur_page = -1;
|
|
return NULL;
|
|
}
|
|
}
|
|
return cur_map + (paddr & (XC_PAGE_SIZE - 1));
|
|
}
|
|
|
|
/* Get LENGTH bytes from info's buffer, at target address memaddr.
|
|
Transfer them to myaddr. */
|
|
int
|
|
target_read_memory (bfd_vma memaddr, bfd_byte *myaddr,
|
|
unsigned int length, struct disassemble_info *info)
|
|
{
|
|
int i;
|
|
unsigned long paddr;
|
|
|
|
if (cur_ctx->regs.psr & PSR_IT) {
|
|
if (virt_to_phys (1, memaddr, &paddr) != 0)
|
|
return EIO;
|
|
}
|
|
else {
|
|
/* Clear UC. */
|
|
paddr = memaddr & ~(1UL << 63);
|
|
}
|
|
|
|
for (i = 0; i < length; i++) {
|
|
unsigned char *p = target_map_memory (paddr + i);
|
|
|
|
if (p == NULL)
|
|
return EIO;
|
|
myaddr[i] = *p;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Print an error message. We can assume that this is in response to
|
|
an error return from buffer_read_memory. */
|
|
void
|
|
perror_memory (int status, bfd_vma memaddr, struct disassemble_info *info)
|
|
{
|
|
if (status != EIO)
|
|
/* Can't happen. */
|
|
(*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
|
|
else
|
|
/* Actually, address between memaddr and memaddr + len was
|
|
out of bounds. */
|
|
(*info->fprintf_func) (info->stream,
|
|
"Address 0x%" PRIx64 " is out of bounds.\n",
|
|
memaddr);
|
|
}
|
|
|
|
/* This could be in a separate file, to save miniscule amounts of space
|
|
in statically linked executables. */
|
|
|
|
/* Just print the address is hex. This is included for completeness even
|
|
though both GDB and objdump provide their own (to print symbolic
|
|
addresses). */
|
|
|
|
void
|
|
generic_print_address (bfd_vma addr, struct disassemble_info *info)
|
|
{
|
|
(*info->fprintf_func) (info->stream, "0x%" PRIx64, addr);
|
|
}
|
|
|
|
/* Just return the given address. */
|
|
|
|
int
|
|
generic_symbol_at_address (bfd_vma addr, struct disassemble_info * info)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
bfd_boolean
|
|
generic_symbol_is_valid (asymbol * sym ATTRIBUTE_UNUSED,
|
|
struct disassemble_info *info ATTRIBUTE_UNUSED)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
bfd_vma bfd_getl32 (const bfd_byte *addr)
|
|
{
|
|
unsigned long v;
|
|
|
|
v = (unsigned long) addr[0];
|
|
v |= (unsigned long) addr[1] << 8;
|
|
v |= (unsigned long) addr[2] << 16;
|
|
v |= (unsigned long) addr[3] << 24;
|
|
|
|
return (bfd_vma) v;
|
|
}
|
|
|
|
bfd_vma bfd_getl64 (const bfd_byte *addr)
|
|
{
|
|
unsigned long v;
|
|
|
|
v = (unsigned long) addr[0];
|
|
v |= (unsigned long) addr[1] << 8;
|
|
v |= (unsigned long) addr[2] << 16;
|
|
v |= (unsigned long) addr[3] << 24;
|
|
v |= (unsigned long) addr[4] << 32;
|
|
v |= (unsigned long) addr[5] << 40;
|
|
v |= (unsigned long) addr[6] << 48;
|
|
v |= (unsigned long) addr[7] << 56;
|
|
|
|
return (bfd_vma) v;
|
|
}
|
|
|
|
bfd_vma bfd_getb32 (const bfd_byte *addr)
|
|
{
|
|
unsigned long v;
|
|
|
|
v = (unsigned long) addr[0] << 24;
|
|
v |= (unsigned long) addr[1] << 16;
|
|
v |= (unsigned long) addr[2] << 8;
|
|
v |= (unsigned long) addr[3];
|
|
|
|
return (bfd_vma) v;
|
|
}
|
|
|
|
bfd_vma bfd_getl16 (const bfd_byte *addr)
|
|
{
|
|
unsigned long v;
|
|
|
|
v = (unsigned long) addr[0];
|
|
v |= (unsigned long) addr[1] << 8;
|
|
|
|
return (bfd_vma) v;
|
|
}
|
|
|
|
bfd_vma bfd_getb16 (const bfd_byte *addr)
|
|
{
|
|
unsigned long v;
|
|
|
|
v = (unsigned long) addr[0] << 24;
|
|
v |= (unsigned long) addr[1] << 16;
|
|
|
|
return (bfd_vma) v;
|
|
}
|
|
|
|
void
|
|
init_disassemble_info (struct disassemble_info *info, void *stream,
|
|
fprintf_ftype fprintf_func)
|
|
{
|
|
memset (info, 0, sizeof (*info));
|
|
|
|
info->flavour = bfd_target_unknown_flavour;
|
|
info->arch = bfd_arch_unknown;
|
|
info->endian = BFD_ENDIAN_UNKNOWN;
|
|
info->octets_per_byte = 1;
|
|
info->fprintf_func = fprintf_func;
|
|
info->stream = stream;
|
|
info->read_memory_func = target_read_memory;
|
|
info->memory_error_func = perror_memory;
|
|
info->print_address_func = generic_print_address;
|
|
info->symbol_at_address_func = generic_symbol_at_address;
|
|
info->symbol_is_valid = generic_symbol_is_valid;
|
|
info->display_endian = BFD_ENDIAN_UNKNOWN;
|
|
}
|
|
|
|
|
|
void target_disas (FILE *out, unsigned long code, unsigned long size)
|
|
{
|
|
unsigned long pc;
|
|
int count;
|
|
struct disassemble_info disasm_info;
|
|
|
|
INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
|
|
|
|
disasm_info.read_memory_func = target_read_memory;
|
|
#if 0
|
|
disasm_info.buffer = NULL;
|
|
disasm_info.buffer_vma = (unsigned long)code;
|
|
disasm_info.buffer_length = size;
|
|
#endif
|
|
|
|
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
|
disasm_info.mach = 0; //bfd_mach_ia64;
|
|
|
|
for (pc = code; pc < code + size; pc += count) {
|
|
int slot = (pc & 0x0f) / 6;
|
|
fprintf (out, "0x%016lx+%d:%c ", pc & ~0x0fUL, slot,
|
|
((pc & ~0x0fUL) == cur_ctx->regs.ip
|
|
&& slot == ctx_slot (cur_ctx)) ? '*' : ' ');
|
|
|
|
count = print_insn_ia64 (pc, &disasm_info);
|
|
|
|
#if 0
|
|
{
|
|
int i;
|
|
uint8_t b;
|
|
|
|
fprintf (out, " {");
|
|
for (i = 0; i < count; i++) {
|
|
target_read_memory (pc + i, &b, 1, &disasm_info);
|
|
fprintf (out, " %02x", b);
|
|
}
|
|
fprintf (out, " }");
|
|
}
|
|
#endif
|
|
fprintf (out, "\n");
|
|
if (count < 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
#define PTE_ED_SHIFT 52
|
|
#define PTE_ED_MASK 1
|
|
#define PTE_PPN_SHIFT 12
|
|
#define PTE_PPN_MASK 0x3fffffffff
|
|
#define PTE_AR_SHIFT 9
|
|
#define PTE_AR_MASK 7
|
|
#define PTE_PL_SHIFT 7
|
|
#define PTE_PL_MASK 3
|
|
#define PTE_D_SHIFT 6
|
|
#define PTE_D_MASK 1
|
|
#define PTE_A_SHIFT 5
|
|
#define PTE_A_MASK 1
|
|
#define PTE_MA_SHIFT 2
|
|
#define PTE_MA_MASK 7
|
|
#define PTE_P_SHIFT 0
|
|
#define PTE_P_MASK 1
|
|
#define ITIR_KEY_SHIFT 8
|
|
#define ITIR_KEY_MASK 0xffffff
|
|
#define ITIR_PS_SHIFT 2
|
|
#define ITIR_PS_MASK 0x3f
|
|
#define ITIR_PS_MIN 12
|
|
#define ITIR_PS_MAX 28
|
|
#define RR_RID_SHIFT 8
|
|
#define RR_RID_MASK 0xffffff
|
|
#define RR_PS_SHIFT 2
|
|
#define RR_PS_MASK 0x3f
|
|
#define RR_VE_MASK 1
|
|
|
|
static const char *get_ps (int ps_val)
|
|
{
|
|
static const char ps[][5] = {" 4K", " 8K", " 16K", " ",
|
|
" 64K", " ", "256K", " ",
|
|
" 1M", " ", " 4M", " ",
|
|
" 16M", " ", " 64M", " ",
|
|
"256M"};
|
|
return ((ps_val >= ITIR_PS_MIN && ps_val <= ITIR_PS_MAX) ?
|
|
ps[ps_val - ITIR_PS_MIN] : " ");
|
|
|
|
}
|
|
|
|
static void print_a_tr (int i, const struct ia64_tr_entry *tr)
|
|
{
|
|
int ps_val, ma_val;
|
|
unsigned long pa;
|
|
|
|
static const char ma[][4] = {"WB ", " ", " ", " ",
|
|
"UC ", "UCE", "WC ", "Nat"};
|
|
|
|
ps_val = tr->itir >> ITIR_PS_SHIFT & ITIR_PS_MASK;
|
|
ma_val = tr->pte >> PTE_MA_SHIFT & PTE_MA_MASK;
|
|
pa = (tr->pte >> PTE_PPN_SHIFT & PTE_PPN_MASK) << PTE_PPN_SHIFT;
|
|
pa = (pa >> ps_val) << ps_val;
|
|
printf (" [%2d] %ld %06lx %016lx %013lx %02d %s %ld %ld %ld %ld "
|
|
"%ld %d %s %06lx\n", i,
|
|
tr->pte >> PTE_P_SHIFT & PTE_P_MASK,
|
|
tr->rid >> RR_RID_SHIFT & RR_RID_MASK,
|
|
tr->vadr, pa, ps_val, get_ps (ps_val),
|
|
tr->pte >> PTE_ED_SHIFT & PTE_ED_MASK,
|
|
tr->pte >> PTE_PL_SHIFT & PTE_PL_MASK,
|
|
tr->pte >> PTE_AR_SHIFT & PTE_AR_MASK,
|
|
tr->pte >> PTE_A_SHIFT & PTE_A_MASK,
|
|
tr->pte >> PTE_D_SHIFT & PTE_D_MASK,
|
|
ma_val, ma[ma_val],
|
|
tr->itir >> ITIR_KEY_SHIFT & ITIR_KEY_MASK);
|
|
}
|
|
|
|
void print_ctx (vcpu_guest_context_t *ctx)
|
|
{
|
|
struct vcpu_guest_context_regs *regs = &ctx->regs;
|
|
int i;
|
|
unsigned int rbs_size, cfm_sof;
|
|
|
|
rbs_size = (regs->ar.bsp - regs->ar.bspstore) / 8;
|
|
cfm_sof = (regs->cfm & CFM_SOF_MASK);
|
|
printf ("bspstore: %016lx bsp: %016lx rbs_size=%d, sof=%d\n",
|
|
regs->ar.bspstore, regs->ar.bsp, rbs_size, cfm_sof);
|
|
|
|
for (i = 0; i < cfm_sof; i++) {
|
|
int off = cfm_sof - i;
|
|
unsigned int rbs_off =
|
|
(((62 - ((rbs_size + regs->rbs_voff) % 64) + off)) / 63) + off;
|
|
if (rbs_off > rbs_size)
|
|
break;
|
|
printf (" r%02d: %016lx%s", 32 + i,
|
|
regs->rbs[rbs_size - rbs_off],
|
|
(i % 3) != 2 ? " " : "\n");
|
|
}
|
|
if ((i % 3) != 0)
|
|
printf ("\n");
|
|
|
|
printf ("\n");
|
|
printf (" r1: %016lx ", regs->r[1]);
|
|
printf (" r2: %016lx ", regs->r[2]);
|
|
printf (" r3: %016lx\n", regs->r[3]);
|
|
printf (" r4: %016lx ", regs->r[4]);
|
|
printf (" r5: %016lx ", regs->r[5]);
|
|
printf (" r6: %016lx\n", regs->r[6]);
|
|
printf (" r7: %016lx ", regs->r[7]);
|
|
printf (" r8: %016lx ", regs->r[8]);
|
|
printf (" r9: %016lx\n", regs->r[9]);
|
|
printf (" r10: %016lx ", regs->r[10]);
|
|
printf (" r11: %016lx ", regs->r[11]);
|
|
printf (" sp: %016lx\n", regs->r[12]);
|
|
printf (" tp: %016lx ", regs->r[13]);
|
|
printf (" r14: %016lx ", regs->r[14]);
|
|
printf (" r15: %016lx\n", regs->r[15]);
|
|
printf (" r16: %016lx ", regs->r[16]);
|
|
printf (" r17: %016lx ", regs->r[17]);
|
|
printf (" r18: %016lx\n", regs->r[18]);
|
|
printf (" r19: %016lx ", regs->r[19]);
|
|
printf (" r20: %016lx ", regs->r[20]);
|
|
printf (" r21: %016lx\n", regs->r[21]);
|
|
printf (" r22: %016lx ", regs->r[22]);
|
|
printf (" r23: %016lx ", regs->r[23]);
|
|
printf (" r24: %016lx\n", regs->r[24]);
|
|
printf (" r25: %016lx ", regs->r[25]);
|
|
printf (" r26: %016lx ", regs->r[26]);
|
|
printf (" r27: %016lx\n", regs->r[27]);
|
|
printf (" r28: %016lx ", regs->r[28]);
|
|
printf (" r29: %016lx ", regs->r[29]);
|
|
printf (" r30: %016lx\n", regs->r[30]);
|
|
printf (" r31: %016lx ", regs->r[31]);
|
|
printf (" ");
|
|
printf (" b0: %016lx\n", regs->b[0]);
|
|
|
|
printf ("\n");
|
|
printf (" psr: %016lx ", regs->psr);
|
|
printf (" cfm: %016lx ", regs->cfm);
|
|
printf (" pr: %016lx\n", regs->pr);
|
|
|
|
printf ("\n");
|
|
printf (" ip: %016lx+%d", regs->ip, (int)(regs->psr >> PSR_RI_SHIFT) & 3);
|
|
printf ("\n");
|
|
target_disas (stdout, regs->ip, 16);
|
|
}
|
|
|
|
void print_br (vcpu_guest_context_t *ctx)
|
|
{
|
|
struct vcpu_guest_context_regs *regs = &ctx->regs;
|
|
|
|
printf (" b0: %016lx ", regs->b[0]);
|
|
printf (" b1: %016lx ", regs->b[1]);
|
|
printf (" b2: %016lx\n", regs->b[2]);
|
|
printf (" b3: %016lx ", regs->b[3]);
|
|
printf (" b4: %016lx ", regs->b[4]);
|
|
printf (" b5: %016lx\n", regs->b[5]);
|
|
printf (" b6: %016lx ", regs->b[6]);
|
|
printf (" b7: %016lx\n", regs->b[7]);
|
|
}
|
|
|
|
void print_regs (vcpu_guest_context_t *ctx)
|
|
{
|
|
struct vcpu_guest_context_regs *regs = &ctx->regs;
|
|
|
|
printf (" r1: %016lx ", regs->r[1]);
|
|
printf (" r2: %016lx ", regs->r[2]);
|
|
printf (" r3: %016lx\n", regs->r[3]);
|
|
printf (" r4: %016lx ", regs->r[4]);
|
|
printf (" r5: %016lx ", regs->r[5]);
|
|
printf (" r6: %016lx\n", regs->r[6]);
|
|
printf (" r7: %016lx ", regs->r[7]);
|
|
printf (" r8: %016lx ", regs->r[8]);
|
|
printf (" r9: %016lx\n", regs->r[9]);
|
|
printf (" r10: %016lx ", regs->r[10]);
|
|
printf (" r11: %016lx ", regs->r[11]);
|
|
printf (" sp: %016lx\n", regs->r[12]);
|
|
printf (" tp: %016lx ", regs->r[13]);
|
|
printf (" r14: %016lx ", regs->r[14]);
|
|
printf (" r15: %016lx\n", regs->r[15]);
|
|
|
|
printf (" Bank %d (current) Bank %d\n",
|
|
(regs->psr & PSR_BN) ? 1 : 0, (regs->psr & PSR_BN) ? 0 : 1);
|
|
printf ("16:%016lx ", regs->r[16]);
|
|
printf ("17:%016lx ", regs->r[17]);
|
|
printf ("16:%016lx ", regs->bank[0]);
|
|
printf ("17:%016lx\n", regs->bank[1]);
|
|
printf ("18:%016lx ", regs->r[18]);
|
|
printf ("19:%016lx ", regs->r[19]);
|
|
printf ("18:%016lx ", regs->bank[2]);
|
|
printf ("19:%016lx\n", regs->bank[3]);
|
|
printf ("20:%016lx ", regs->r[20]);
|
|
printf ("21:%016lx ", regs->r[21]);
|
|
printf ("20:%016lx ", regs->bank[4]);
|
|
printf ("21:%016lx\n", regs->bank[5]);
|
|
printf ("22:%016lx ", regs->r[22]);
|
|
printf ("23:%016lx ", regs->r[23]);
|
|
printf ("22:%016lx ", regs->bank[6]);
|
|
printf ("23:%016lx\n", regs->bank[7]);
|
|
printf ("24:%016lx ", regs->r[24]);
|
|
printf ("25:%016lx ", regs->r[25]);
|
|
printf ("24:%016lx ", regs->bank[8]);
|
|
printf ("25:%016lx\n", regs->bank[9]);
|
|
printf ("26:%016lx ", regs->r[26]);
|
|
printf ("27:%016lx ", regs->r[27]);
|
|
printf ("26:%016lx ", regs->bank[10]);
|
|
printf ("27:%016lx\n", regs->bank[11]);
|
|
printf ("28:%016lx ", regs->r[28]);
|
|
printf ("29:%016lx ", regs->r[29]);
|
|
printf ("28:%016lx ", regs->bank[12]);
|
|
printf ("29:%016lx\n", regs->bank[13]);
|
|
printf ("30:%016lx ", regs->r[30]);
|
|
printf ("31:%016lx ", regs->r[31]);
|
|
printf ("30:%016lx ", regs->bank[14]);
|
|
printf ("31:%016lx\n", regs->bank[15]);
|
|
printf ("\n");
|
|
}
|
|
|
|
void print_cr (vcpu_guest_context_t *ctx)
|
|
{
|
|
struct vcpu_guest_context_regs *regs = &ctx->regs;
|
|
|
|
printf (" dcr: %016lx ", regs->cr.dcr);
|
|
printf (" itm: %016lx ", regs->cr.itm);
|
|
printf (" iva: %016lx\n", regs->cr.iva);
|
|
printf (" pta: %016lx ", regs->cr.pta);
|
|
printf (" ipsr: %016lx ", regs->cr.ipsr);
|
|
printf (" isr: %016lx\n", regs->cr.isr);
|
|
printf (" iip: %016lx ", regs->cr.iip);
|
|
printf (" ifa: %016lx ", regs->cr.ifa);
|
|
printf (" itir: %016lx\n", regs->cr.itir);
|
|
printf (" iipa: %016lx ", regs->cr.iipa);
|
|
printf (" ifs: %016lx ", regs->cr.ifs);
|
|
printf (" iim: %016lx\n", regs->cr.iim);
|
|
printf (" iha: %016lx ", regs->cr.iha);
|
|
printf (" lid: %016lx ", regs->cr.lid);
|
|
printf (" ivr: %016lx\n", regs->cr.ivr);
|
|
printf (" tpr: %016lx ", regs->cr.tpr);
|
|
printf (" eoi: %016lx ", regs->cr.eoi);
|
|
printf (" irr0: %016lx\n", regs->cr.irr[0]);
|
|
printf (" irr1: %016lx ", regs->cr.irr[1]);
|
|
printf (" irr2: %016lx ", regs->cr.irr[2]);
|
|
printf (" irr3: %016lx\n", regs->cr.irr[3]);
|
|
printf (" itv: %016lx ", regs->cr.itv);
|
|
printf (" pmv: %016lx ", regs->cr.pmv);
|
|
printf (" cmcv: %016lx\n", regs->cr.cmcv);
|
|
printf (" lrr0: %016lx ", regs->cr.lrr0);
|
|
printf (" lrr1: %016lx ", regs->cr.lrr1);
|
|
printf (" ev_cb:%016lx\n", ctx->event_callback_ip);
|
|
}
|
|
|
|
void print_ar (vcpu_guest_context_t *ctx)
|
|
{
|
|
struct vcpu_guest_context_regs *regs = &ctx->regs;
|
|
|
|
printf (" kr0: %016lx ", regs->ar.kr[0]);
|
|
printf (" kr1: %016lx ", regs->ar.kr[1]);
|
|
printf (" kr2: %016lx\n", regs->ar.kr[2]);
|
|
printf (" kr3: %016lx ", regs->ar.kr[3]);
|
|
printf (" kr4: %016lx ", regs->ar.kr[4]);
|
|
printf (" kr5: %016lx\n", regs->ar.kr[5]);
|
|
printf (" kr6: %016lx ", regs->ar.kr[6]);
|
|
printf (" kr7: %016lx ", regs->ar.kr[7]);
|
|
printf (" rsc: %016lx\n", regs->ar.rsc);
|
|
printf (" bsp: %016lx ", regs->ar.bsp);
|
|
printf (" bsps: %016lx ", regs->ar.bspstore);
|
|
printf (" rnat: %016lx\n", regs->ar.rnat);
|
|
printf (" csd: %016lx ", regs->ar.csd);
|
|
printf (" ccv: %016lx ", regs->ar.ccv);
|
|
printf (" unat: %016lx\n", regs->ar.unat);
|
|
printf (" fpsr: %016lx ", regs->ar.fpsr);
|
|
printf (" itc: %016lx\n", regs->ar.itc);
|
|
printf (" pfs: %016lx ", regs->ar.pfs);
|
|
printf (" lc: %016lx ", regs->ar.lc);
|
|
printf (" ec: %016lx\n", regs->ar.ec);
|
|
}
|
|
|
|
void print_a_rr (int num, unsigned long rr)
|
|
{
|
|
int ps_val = (rr >> RR_PS_SHIFT) & RR_PS_MASK;
|
|
|
|
printf (" [%d] %06lx %02x %s %ld\n",
|
|
num, (rr >> RR_RID_SHIFT) & RR_RID_MASK,
|
|
ps_val, get_ps (ps_val), rr & RR_VE_MASK);
|
|
}
|
|
|
|
void print_rr (vcpu_guest_context_t *ctx)
|
|
{
|
|
struct vcpu_guest_context_regs *regs = &ctx->regs;
|
|
int i;
|
|
|
|
printf (" rr: rid ps ve\n");
|
|
for (i = 0; i < 8; i++)
|
|
print_a_rr (i, regs->rr[i]);
|
|
}
|
|
|
|
void print_db (vcpu_guest_context_t *ctx)
|
|
{
|
|
struct vcpu_guest_context_regs *regs = &ctx->regs;
|
|
int i;
|
|
|
|
for (i = 0; i < 7; i += 2)
|
|
printf (" ibr[%d]: %016lx ibr[%d]: %016lx\n",
|
|
i, regs->ibr[i], i + 1, regs->ibr[i + 1]);
|
|
printf ("\n");
|
|
for (i = 0; i < 7; i += 2)
|
|
printf (" dbr[%d]: %016lx dbr[%d]: %016lx\n",
|
|
i, regs->dbr[i], i + 1, regs->dbr[i + 1]);
|
|
}
|
|
|
|
struct bit_descr {
|
|
const char *name;
|
|
unsigned char sz;
|
|
};
|
|
|
|
const struct bit_descr psr_bits[] =
|
|
{
|
|
{"", 1 }, {"be", 1 }, {"up", 1 }, {"ac", 1 },
|
|
{"mfl", 1 }, {"mfh", 1 }, {"", 7 },
|
|
{"ic", 1 }, {"i", 1 }, {"pk", 1 },
|
|
{"", 1 }, {"dt", 1 }, {"dfl", 1 }, {"dfh", 1 },
|
|
{"sp", 1 }, {"pp", 1 }, {"di", 1 }, {"si", 1 },
|
|
{"db", 1 }, {"lp", 1 }, {"tb", 1 }, {"rt", 1 },
|
|
{"", 4 },
|
|
{"cpl", 2 }, {"is", 1 }, {"mc", 1 },
|
|
{"it", 1 }, {"id", 1 }, {"da", 1 }, {"dd", 1 },
|
|
{"ss", 1 }, {"ri", 2 }, {"ed", 1 },
|
|
{"bn", 1 }, {"ia", 1 }, {"vm", 1 },
|
|
{NULL, 0 }
|
|
};
|
|
|
|
void print_bits (const struct bit_descr *desc, unsigned long val)
|
|
{
|
|
const struct bit_descr *d;
|
|
unsigned int off;
|
|
|
|
/* Reverse order. */
|
|
for (d = desc, off = 0; d->name; d++)
|
|
off += d->sz;
|
|
|
|
d--;
|
|
|
|
while (1) {
|
|
off -= d->sz;
|
|
if (*d->name) {
|
|
if (d->sz != 1 || ((val >> off) & 1))
|
|
printf (" %s", d->name);
|
|
if (d->sz != 1)
|
|
printf ("=%lx", (val >> off) & ((1 << d->sz) - 1));
|
|
}
|
|
if (d == desc)
|
|
break;
|
|
d--;
|
|
}
|
|
}
|
|
|
|
void print_tr (vcpu_guest_context_t *ctx)
|
|
{
|
|
struct vcpu_tr_regs *tr = &ctx->regs.tr;
|
|
int i;
|
|
|
|
printf ("\n itr: P rid va pa ps ed pl "
|
|
"ar a d ma key\n");
|
|
|
|
for (i = 0; i < sizeof (tr->itrs) / sizeof (tr->itrs[0]); i++)
|
|
print_a_tr (i, &tr->itrs[i]);
|
|
|
|
printf ("\n dtr: P rid va pa ps ed pl "
|
|
"ar a d ma key\n");
|
|
|
|
for (i = 0; i < sizeof (tr->dtrs) / sizeof (tr->dtrs[0]); i++)
|
|
print_a_tr (i, &tr->dtrs[i]);
|
|
}
|
|
|
|
int lock_pages (void *addr, size_t len);
|
|
void unlock_pages (void *addr, size_t len);
|
|
int do_xen_hypercall (int xc_handle, privcmd_hypercall_t *hypercall);
|
|
|
|
#ifdef HAVE_DEBUG_OP
|
|
static int do_ia64_debug_op (int xc_handle,
|
|
unsigned long cmd, unsigned long domain,
|
|
xen_ia64_debug_op_t *op)
|
|
{
|
|
int ret = -1;
|
|
privcmd_hypercall_t hypercall;
|
|
|
|
hypercall.op = __HYPERVISOR_ia64_debug_op;
|
|
hypercall.arg[0] = cmd;
|
|
hypercall.arg[1] = domain;
|
|
hypercall.arg[2] = (unsigned long)op;
|
|
|
|
if (lock_pages (op, sizeof (*op)) != 0) {
|
|
perror ("Could not lock memory for Xen hypercall");
|
|
goto out1;
|
|
}
|
|
|
|
ret = do_xen_hypercall (xc_handle, &hypercall);
|
|
if (ret < 0) {
|
|
if (errno == EACCES)
|
|
fprintf (stderr,"domctl operation failed -- need to "
|
|
"rebuild the user-space tool set?\n");
|
|
}
|
|
|
|
unlock_pages (op, sizeof (*op));
|
|
|
|
out1:
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static volatile int ctrl_c_hit;
|
|
|
|
void ctrl_c_handler (int sig)
|
|
{
|
|
ctrl_c_hit = 1;
|
|
}
|
|
|
|
int wait_domain (int vcpu, vcpu_guest_context_t *ctx)
|
|
{
|
|
struct timespec ts;
|
|
xc_dominfo_t dominfo;
|
|
int ret;
|
|
int cnt = 0;
|
|
|
|
ts.tv_sec = 0;
|
|
ts.tv_nsec = 10*1000*1000;
|
|
|
|
ret = xc_domain_unpause (xc_handle, domid);
|
|
if (ret < 0)
|
|
perror ("xc_domain_unpause");
|
|
|
|
ctrl_c_hit = 0;
|
|
|
|
while (1) {
|
|
ret = xc_domain_getinfo (xc_handle, domid, 1, &dominfo);
|
|
if (ret < 0)
|
|
perror ("xc_domain_getinfo");
|
|
|
|
if (dominfo.paused)
|
|
break;
|
|
|
|
if (ctrl_c_hit) {
|
|
fflush (stdout);
|
|
/* Force pause. */
|
|
ret = xc_domain_pause (xc_handle, domid);
|
|
if (ret < 0)
|
|
perror ("xc_domain_pause");
|
|
|
|
break;
|
|
}
|
|
|
|
printf ("%c\b", "/-\\|"[(cnt++) % 4]);
|
|
fflush (stdout);
|
|
nanosleep (&ts, NULL);
|
|
}
|
|
return xc_ia64_vcpu_getcontext (xc_handle, domid, vcpu, ctx);
|
|
}
|
|
|
|
int virt_to_phys (int is_inst, unsigned long vaddr, unsigned long *paddr)
|
|
{
|
|
struct vcpu_tr_regs *trs = &cur_ctx->regs.tr;
|
|
struct ia64_tr_entry *tr;
|
|
int i;
|
|
int num;
|
|
|
|
/* Search in tr. */
|
|
if (is_inst) {
|
|
tr = trs->itrs;
|
|
num = sizeof (trs->itrs) / sizeof (trs->itrs[0]);
|
|
}
|
|
else {
|
|
tr = trs->dtrs;
|
|
num = sizeof (trs->dtrs) / sizeof (trs->dtrs[0]);
|
|
}
|
|
for (i = 0; i < num; i++, tr++) {
|
|
int ps_val = (tr->itir >> ITIR_PS_SHIFT) & ITIR_PS_MASK;
|
|
unsigned long ps_mask = (-1L) << ps_val;
|
|
|
|
if ((tr->vadr & ps_mask) == (vaddr & ps_mask)) {
|
|
*paddr = ((tr->pte & (PTE_PPN_MASK << PTE_PPN_SHIFT)) & ps_mask) |
|
|
(vaddr & ~ps_mask);
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
unsigned long *
|
|
get_reg_addr (const char *name)
|
|
{
|
|
if (strcmp (name, "ip") == 0)
|
|
return &cur_ctx->regs.ip;
|
|
else if (strcmp (name, "psr") == 0)
|
|
return &cur_ctx->regs.psr;
|
|
else if (strcmp (name, "iip") == 0)
|
|
return &cur_ctx->regs.cr.iip;
|
|
else if (strcmp (name, "b0") == 0)
|
|
return &cur_ctx->regs.b[0];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
enum prio_expr {EXPR_BASE, EXPR_SUM, EXPR_LOGIC, EXPR_PROD};
|
|
|
|
int parse_expr (char **buf, unsigned long *res, enum prio_expr prio);
|
|
|
|
int next_char (char **buf)
|
|
{
|
|
char *b;
|
|
|
|
b = *buf;
|
|
while (isspace ((unsigned char)*b))
|
|
b++;
|
|
*buf = b;
|
|
return *b;
|
|
}
|
|
|
|
int parse_unary (char **buf, unsigned long *res)
|
|
{
|
|
char c;
|
|
|
|
c = next_char (buf);
|
|
switch (c) {
|
|
case '0' ... '9':
|
|
{
|
|
char *e;
|
|
*res = strtoul (*buf, &e, 0);
|
|
if (e == *buf) {
|
|
printf ("bad literal\n");
|
|
return -1;
|
|
}
|
|
*buf = e;
|
|
}
|
|
break;
|
|
case '+':
|
|
(*buf)++;
|
|
return parse_unary (buf, res);
|
|
case '$':
|
|
{
|
|
char *b;
|
|
char *e;
|
|
char c;
|
|
unsigned long *reg;
|
|
int len;
|
|
|
|
b = *buf;
|
|
e = b + 1;
|
|
|
|
while ((*e >= 'a' && *e <= 'z') ||
|
|
(*e >= 'A' && *e <= 'Z') ||
|
|
(*e >= '0' && *e <= '9') ||
|
|
(*e == '_' || *e == '.'))
|
|
e++;
|
|
|
|
if (b == e) {
|
|
printf ("identifier missing after '$'\n");
|
|
return -1;
|
|
}
|
|
|
|
b++;
|
|
len = e - b;
|
|
|
|
c = b[len];
|
|
b[len] = 0;
|
|
reg = get_reg_addr (b);
|
|
b[len] = c;
|
|
|
|
if (reg != NULL)
|
|
*res = *reg;
|
|
else if (strncmp (b, "d2p", len) == 0 ||
|
|
strncmp (b, "i2p", len) == 0) {
|
|
unsigned long vaddr;
|
|
|
|
*buf = e;
|
|
if (parse_unary (buf, &vaddr) != 0)
|
|
return -1;
|
|
if (virt_to_phys (*b == 'i', vaddr, res) != 0) {
|
|
printf ("cannot find vaddr %016lx in tr\n", vaddr);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
else {
|
|
printf ("unknown symbol\n");
|
|
return -1;
|
|
}
|
|
*buf = e;
|
|
}
|
|
break;
|
|
case '(':
|
|
(*buf)++;
|
|
if (parse_expr (buf, res, EXPR_BASE) != 0)
|
|
return -1;
|
|
|
|
if (next_char (buf) != ')') {
|
|
printf ("missing ')'\n");
|
|
return -1;
|
|
}
|
|
else
|
|
(*buf)++;
|
|
break;
|
|
default:
|
|
printf ("unknown operand '%c' in expression\n", c);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int parse_expr (char **buf, unsigned long *res, enum prio_expr prio)
|
|
{
|
|
unsigned long val = 0;
|
|
unsigned long val1;
|
|
char c;
|
|
|
|
if (parse_unary (buf, &val) != 0)
|
|
return -1;
|
|
|
|
while (1) {
|
|
c = next_char (buf);
|
|
switch (c) {
|
|
case '+':
|
|
case '-':
|
|
if (prio > EXPR_SUM)
|
|
return 0;
|
|
(*buf)++;
|
|
if (parse_expr (buf, &val1, EXPR_SUM) < 0)
|
|
return -1;
|
|
if (c == '+')
|
|
val += val1;
|
|
else
|
|
val -= val1;
|
|
break;
|
|
case '*':
|
|
if (prio > EXPR_PROD)
|
|
return 0;
|
|
|
|
(*buf)++;
|
|
if (parse_expr (buf, &val1, EXPR_SUM) < 0)
|
|
return -1;
|
|
|
|
val *= val1;
|
|
break;
|
|
default:
|
|
*res = val;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
char *parse_arg (char **buf)
|
|
{
|
|
char *res;
|
|
char *b = *buf;
|
|
|
|
/* Eat leading spaces. */
|
|
while (isspace ((unsigned char)*b))
|
|
b++;
|
|
|
|
res = b;
|
|
while (*b && !isspace ((unsigned char)*b))
|
|
b++;
|
|
|
|
/* Set the NUL terminator. */
|
|
if (*b)
|
|
*b++ = 0;
|
|
|
|
*buf = b;
|
|
return res;
|
|
}
|
|
|
|
vcpu_guest_context_any_t vcpu_ctx_any[MAX_VIRT_CPUS];
|
|
|
|
int vcpu_setcontext (int vcpu)
|
|
{
|
|
int ret;
|
|
|
|
ret = xc_vcpu_setcontext (xc_handle, domid, vcpu, &vcpu_ctx_any[vcpu]);
|
|
if (ret < 0)
|
|
perror ("xc_vcpu_setcontext");
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum cmd_status { CMD_ERROR, CMD_OK, CMD_REPEAT, CMD_QUIT };
|
|
|
|
struct command_desc
|
|
{
|
|
const char *name;
|
|
const char *help;
|
|
enum cmd_status (*cmd)(char *line);
|
|
};
|
|
|
|
static enum cmd_status
|
|
cmd_registers (char *line)
|
|
{
|
|
print_ctx (cur_ctx);
|
|
return CMD_OK;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_sstep (char *line)
|
|
{
|
|
/* Set psr.dd and psr.id to skip over current breakpoint. */
|
|
cur_ctx->regs.psr |= PSR_SS | PSR_DD | PSR_ID;
|
|
cur_ctx->regs.psr &= ~PSR_TB;
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
|
|
if (wait_domain (cur_vcpu, cur_ctx) < 0) {
|
|
perror ("wait_domain");
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
print_ctx (cur_ctx);
|
|
|
|
return CMD_REPEAT;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_go (char *line)
|
|
{
|
|
unsigned long n = 1;
|
|
|
|
if (*line != 0) {
|
|
if (parse_expr (&line, &n, 0) < 0)
|
|
return CMD_ERROR;
|
|
}
|
|
while (n > 0) {
|
|
/* Set psr.dd and psr.id to skip over current breakpoint. */
|
|
if ((cur_ctx->regs.psr & (PSR_SS | PSR_TB | PSR_DB)) != 0) {
|
|
cur_ctx->regs.psr &= ~(PSR_SS | PSR_TB);
|
|
cur_ctx->regs.psr |= PSR_DD | PSR_ID;
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
if (wait_domain (cur_vcpu, cur_ctx) < 0) {
|
|
perror ("wait_domain");
|
|
return CMD_ERROR;
|
|
}
|
|
print_ctx (cur_ctx);
|
|
n--;
|
|
}
|
|
|
|
return CMD_REPEAT;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_cb (char *line)
|
|
{
|
|
if ((cur_ctx->regs.psr & (PSR_SS | PSR_TB)) != PSR_TB) {
|
|
cur_ctx->regs.psr &= ~PSR_SS;
|
|
cur_ctx->regs.psr |= PSR_TB;
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
if (wait_domain (cur_vcpu, cur_ctx) < 0) {
|
|
perror ("wait_domain");
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
print_ctx (cur_ctx);
|
|
|
|
return CMD_REPEAT;
|
|
}
|
|
|
|
static int quit_paused;
|
|
|
|
static enum cmd_status
|
|
cmd_quit (char *line)
|
|
{
|
|
if (!strcmp (line, "paused"))
|
|
quit_paused = 1;
|
|
return CMD_QUIT;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_echo (char *line)
|
|
{
|
|
printf ("%s", line);
|
|
return CMD_OK;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_disassemble (char *args)
|
|
{
|
|
static unsigned long addr;
|
|
unsigned long end_addr = addr + 16;
|
|
|
|
if (*args != 0) {
|
|
if (parse_expr (&args, &addr, 0) < 0)
|
|
return CMD_ERROR;
|
|
if (*args != 0) {
|
|
if (parse_expr (&args, &end_addr, 0) < 0)
|
|
return CMD_ERROR;
|
|
}
|
|
else
|
|
end_addr = addr + 16;
|
|
}
|
|
target_disas (stdout, addr, end_addr - addr);
|
|
addr = end_addr;
|
|
return CMD_REPEAT;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_dump (char *args)
|
|
{
|
|
static unsigned long addr;
|
|
unsigned long end_addr = addr + 256;
|
|
unsigned long p;
|
|
|
|
if (*args != 0) {
|
|
if (parse_expr (&args, &addr, 0) < 0)
|
|
return CMD_ERROR;
|
|
if (*args != 0) {
|
|
if (parse_expr (&args, &end_addr, 0) < 0)
|
|
return CMD_ERROR;
|
|
}
|
|
else
|
|
end_addr = addr + 256;
|
|
}
|
|
for (p = addr; p < end_addr; p += 16) {
|
|
int i;
|
|
printf ("%016lx:", p);
|
|
for (i = 0; i < 16; i++) {
|
|
unsigned char *m = target_map_memory (p + i);
|
|
printf ("%c%02x", i == 8 ? '-' : ' ', *m);
|
|
}
|
|
printf ("\n");
|
|
}
|
|
addr = end_addr;
|
|
return CMD_REPEAT;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_break (char *args)
|
|
{
|
|
unsigned long addr;
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
if (cur_ctx->regs.ibr[2 * i] == 0 && cur_ctx->regs.ibr[2 * i + 1] == 0)
|
|
break;
|
|
|
|
if (i == 4) {
|
|
printf ("no availabe break points\n");
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
if (parse_expr (&args, &addr, 0) < 0)
|
|
return CMD_ERROR;
|
|
|
|
cur_ctx->regs.ibr[2 * i] = addr;
|
|
cur_ctx->regs.ibr[2 * i + 1] = 0x87fffffffffffff0UL;
|
|
cur_ctx->regs.psr |= PSR_DB;
|
|
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
else
|
|
return CMD_OK;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_watch (char *args)
|
|
{
|
|
unsigned long addr;
|
|
unsigned long mask;
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
if (cur_ctx->regs.dbr[2 * i] == 0 && cur_ctx->regs.dbr[2 * i + 1] == 0)
|
|
break;
|
|
|
|
if (i == 4) {
|
|
printf ("no availabe watch points\n");
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
if (parse_expr (&args, &addr, 0) < 0)
|
|
return CMD_ERROR;
|
|
|
|
if (*args == 0)
|
|
mask = 3;
|
|
else if (parse_expr (&args, &mask, 0) < 0)
|
|
return CMD_ERROR;
|
|
|
|
cur_ctx->regs.dbr[2 * i] = addr;
|
|
cur_ctx->regs.dbr[2 * i + 1] = ~((1UL << mask) - 1) | (0xc7UL << 56);
|
|
cur_ctx->regs.psr |= PSR_DB;
|
|
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
else {
|
|
printf ("Watchpoint %d set\n", i);
|
|
return CMD_OK;
|
|
}
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_delete (char *args)
|
|
{
|
|
unsigned long num;
|
|
|
|
if (parse_expr (&args, &num, 0) < 0)
|
|
return CMD_ERROR;
|
|
|
|
if (num < 4) {
|
|
cur_ctx->regs.ibr[2 * num] = 0;
|
|
cur_ctx->regs.ibr[2 * num + 1] = 0;
|
|
}
|
|
else if (num < 8) {
|
|
num -= 4;
|
|
cur_ctx->regs.dbr[2 * num] = 0;
|
|
cur_ctx->regs.dbr[2 * num + 1] = 0;
|
|
}
|
|
else {
|
|
printf ("breakpoint out of range\n");
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
cur_ctx->regs.psr |= PSR_DB;
|
|
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
else
|
|
return CMD_OK;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_disable (char *args)
|
|
{
|
|
unsigned long num;
|
|
|
|
if (parse_expr (&args, &num, 0) < 0)
|
|
return CMD_ERROR;
|
|
|
|
if (num >= 4) {
|
|
printf ("breakpoint out of range\n");
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
cur_ctx->regs.ibr[2 * num + 1] &= ~(1UL << 63);
|
|
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
else
|
|
return CMD_OK;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_enable (char *args)
|
|
{
|
|
unsigned long num;
|
|
|
|
if (parse_expr (&args, &num, 0) < 0)
|
|
return CMD_ERROR;
|
|
|
|
if (num >= 4) {
|
|
printf ("breakpoint out of range\n");
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
cur_ctx->regs.ibr[2 * num + 1] |= 1UL << 63;
|
|
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
else
|
|
return CMD_OK;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_print (char *args)
|
|
{
|
|
unsigned long addr;
|
|
|
|
if (parse_expr (&args, &addr, 0) < 0)
|
|
return CMD_ERROR;
|
|
|
|
printf ("res: 0x%016lx = %ld\n", addr, addr);
|
|
|
|
return CMD_OK;
|
|
}
|
|
|
|
struct bit_xlat {
|
|
unsigned int bit;
|
|
const char *name;
|
|
};
|
|
|
|
static const struct bit_xlat debug_flags[] = {
|
|
{ XEN_IA64_DEBUG_ON_KERN_SSTEP, "sstep" },
|
|
{ XEN_IA64_DEBUG_ON_KERN_DEBUG, "debug" },
|
|
{ XEN_IA64_DEBUG_ON_KERN_TBRANCH, "tbranch" },
|
|
{ XEN_IA64_DEBUG_ON_EXTINT, "extint" },
|
|
{ XEN_IA64_DEBUG_ON_EXCEPT, "except" },
|
|
{ XEN_IA64_DEBUG_ON_EVENT, "event" },
|
|
{ XEN_IA64_DEBUG_ON_PRIVOP, "privop" },
|
|
{ XEN_IA64_DEBUG_ON_PAL, "pal" },
|
|
{ XEN_IA64_DEBUG_ON_SAL, "sal" },
|
|
{ XEN_IA64_DEBUG_ON_EFI, "efi" },
|
|
{ XEN_IA64_DEBUG_ON_RFI, "rfi" },
|
|
{ XEN_IA64_DEBUG_ON_MMU, "mmu" },
|
|
{ XEN_IA64_DEBUG_ON_BAD_MPA, "mpa" },
|
|
{ XEN_IA64_DEBUG_FORCE_SS, "ss" },
|
|
{ XEN_IA64_DEBUG_FORCE_DB, "db" },
|
|
{ XEN_IA64_DEBUG_ON_TR, "tr" },
|
|
{ XEN_IA64_DEBUG_ON_TC, "tc" },
|
|
#if 0
|
|
{ XEN_IA64_DEBUG_ON_KEYS, "keys" },
|
|
{ XEN_IA64_DEBUG_ON_MOV_TO_CR, "mov_to_cr" },
|
|
{ XEN_IA64_DEBUG_ON_VHPT, "vhpt" },
|
|
{ XEN_IA64_DEBUG_ON_IOSAPIC, "iosapic" },
|
|
#endif
|
|
{ 0, NULL }
|
|
};
|
|
|
|
static enum cmd_status
|
|
cmd_disp (char *arg)
|
|
{
|
|
if (strcmp (arg, "br") == 0)
|
|
print_br (cur_ctx);
|
|
else if (strcmp (arg, "regs") == 0)
|
|
print_regs (cur_ctx);
|
|
else if (strcmp (arg, "cr") == 0)
|
|
print_cr (cur_ctx);
|
|
else if (strcmp (arg, "ar") == 0)
|
|
print_ar (cur_ctx);
|
|
else if (strcmp (arg, "tr") == 0)
|
|
print_tr (cur_ctx);
|
|
else if (strcmp (arg, "rr") == 0)
|
|
print_rr (cur_ctx);
|
|
else if (strcmp (arg, "db") == 0)
|
|
print_db (cur_ctx);
|
|
else if (strcmp (arg, "psr") == 0) {
|
|
printf ("psr:");
|
|
print_bits (psr_bits, cur_ctx->regs.psr);
|
|
printf ("\n");
|
|
}
|
|
else if (strcmp (arg, "ipsr") == 0) {
|
|
printf ("ipsr:");
|
|
print_bits (psr_bits, cur_ctx->regs.cr.ipsr);
|
|
printf ("\n");
|
|
}
|
|
else if (strcmp (arg, "break") == 0) {
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
if (cur_ctx->regs.ibr[2 * i + 1])
|
|
printf ("%d: 0x%016lx %s\n", i, cur_ctx->regs.ibr[2 * i],
|
|
(cur_ctx->regs.ibr[2 * i + 1] & (1UL << 63)) ?
|
|
"enabled" : "disabled");
|
|
for (i = 0; i < 4; i++)
|
|
if (cur_ctx->regs.dbr[2 * i + 1])
|
|
printf ("%d: 0x%016lx %s\n", i, cur_ctx->regs.dbr[2 * i],
|
|
(cur_ctx->regs.dbr[2 * i + 1] & (1UL << 63)) ?
|
|
"enabled" : "disabled");
|
|
}
|
|
else if (strcmp (arg, "domain") == 0) {
|
|
xc_dominfo_t dominfo;
|
|
#ifdef HAVE_DEBUG_OP
|
|
xen_ia64_debug_op_t debug_op;
|
|
int i;
|
|
#endif
|
|
if (xc_domain_getinfo (xc_handle, domid, 1, &dominfo) < 0) {
|
|
perror ("xc_domain_getinfo");
|
|
return 0;
|
|
}
|
|
|
|
printf ("id=%d nr_pages=%lu shared_info_frame=%lu max_mem=%luKB\n",
|
|
dominfo.domid, dominfo.nr_pages, dominfo.shared_info_frame,
|
|
dominfo.max_memkb);
|
|
printf (" nr_online_vcpu=%u max_vcpu_id=%u\n",
|
|
dominfo.nr_online_vcpus, dominfo.max_vcpu_id);
|
|
printf (" status:");
|
|
if (dominfo.dying)
|
|
printf (" dying");
|
|
if (dominfo.crashed)
|
|
printf (" crashed");
|
|
if (dominfo.shutdown)
|
|
printf (" shutdown(%u)", dominfo.shutdown_reason);
|
|
if (dominfo.paused)
|
|
printf (" paused");
|
|
if (dominfo.blocked)
|
|
printf (" blocked");
|
|
if (dominfo.running)
|
|
printf (" running");
|
|
if (dominfo.hvm)
|
|
printf (" hvm");
|
|
if (dominfo.debugged)
|
|
printf (" debug");
|
|
printf ("\n");
|
|
|
|
#ifdef HAVE_DEBUG_OP
|
|
if (do_ia64_debug_op (xc_handle, XEN_IA64_DEBUG_OP_GET_FLAGS,
|
|
domid, &debug_op) < 0) {
|
|
perror ("xc_domain_getinfo");
|
|
return 0;
|
|
}
|
|
printf ("debug flags: %08lx: ", debug_op.flags);
|
|
for (i = 0; debug_flags[i].name; i++)
|
|
if (debug_flags[i].bit & debug_op.flags)
|
|
printf (" %s", debug_flags[i].name);
|
|
printf ("\n");
|
|
#endif
|
|
}
|
|
else if (*arg == 0)
|
|
printf ("choose among br, regs, cr, ar, tr, rr, db\n");
|
|
else {
|
|
printf ("cannot disp '%s'\n", arg);
|
|
return CMD_ERROR;
|
|
}
|
|
return CMD_OK;
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_bev (char *arg)
|
|
{
|
|
xen_ia64_debug_op_t debug_op;
|
|
int i;
|
|
|
|
if (do_ia64_debug_op (xc_handle, XEN_IA64_DEBUG_OP_GET_FLAGS,
|
|
domid, &debug_op) < 0) {
|
|
perror ("get debug flags");
|
|
return CMD_ERROR;
|
|
}
|
|
if (arg == NULL || arg[0] == 0) {
|
|
printf ("debug flags: %08lx:\n", debug_op.flags);
|
|
for (i = 0; debug_flags[i].name; i++)
|
|
printf (" %c%s\n",
|
|
(debug_flags[i].bit & debug_op.flags) ? '+' : '-',
|
|
debug_flags[i].name);
|
|
return CMD_OK;
|
|
}
|
|
else {
|
|
char *p = strtok ((char *)arg, " ");
|
|
|
|
while (p != NULL) {
|
|
unsigned int flag = 0;
|
|
|
|
for (i = 0; debug_flags[i].name; i++)
|
|
if (strcmp (p, debug_flags[i].name) == 0
|
|
|| ((p[0] == '-' || p[0] == '+')
|
|
&& strcmp (p + 1, debug_flags[i].name) == 0)) {
|
|
flag = debug_flags[i].bit;
|
|
break;
|
|
}
|
|
if (flag == 0) {
|
|
printf ("unknown event %s\n", p);
|
|
return CMD_ERROR;
|
|
}
|
|
if (p[0] == '-')
|
|
debug_op.flags &= ~flag;
|
|
else
|
|
debug_op.flags |= flag;
|
|
|
|
p = strtok (NULL, " ");
|
|
}
|
|
if (do_ia64_debug_op (xc_handle, XEN_IA64_DEBUG_OP_SET_FLAGS,
|
|
domid, &debug_op) < 0) {
|
|
perror ("set debug flags");
|
|
return CMD_ERROR;
|
|
}
|
|
/* Disabling force_SS and force_DB requires setting psr. */
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
else
|
|
return CMD_OK;
|
|
}
|
|
}
|
|
|
|
static enum cmd_status
|
|
cmd_set (char *line)
|
|
{
|
|
char *reg;
|
|
unsigned long *addr;
|
|
unsigned long val;
|
|
|
|
reg = parse_arg (&line);
|
|
|
|
addr = get_reg_addr (reg);
|
|
if (addr == NULL) {
|
|
printf ("unknown register %s\n", reg);
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
if (parse_expr (&line, &val, 0) < 0)
|
|
return CMD_ERROR;
|
|
|
|
*addr = val;
|
|
|
|
if (vcpu_setcontext (cur_vcpu) < 0)
|
|
return CMD_ERROR;
|
|
else
|
|
return CMD_OK;
|
|
}
|
|
|
|
const struct command_desc commands[];
|
|
|
|
static enum cmd_status
|
|
cmd_help (char *line)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; commands[i].name; i++)
|
|
printf ("%s -- %s\n", commands[i].name, commands[i].help);
|
|
|
|
return CMD_OK;
|
|
}
|
|
|
|
const struct command_desc commands[] = {
|
|
{ "registers", "display current registers", cmd_registers },
|
|
{ "sstep", "single step", cmd_sstep },
|
|
{ "go", "resume execution", cmd_go },
|
|
{ "quit", "quit debugger", cmd_quit },
|
|
{ "echo", "display parameters", cmd_echo },
|
|
{ "disassemble", "disassemble memory", cmd_disassemble },
|
|
{ "dump", "dump memory", cmd_dump },
|
|
{ "break", "set a break point", cmd_break },
|
|
{ "watch", "set a watch point", cmd_watch },
|
|
{ "cb", "resume until branch", cmd_cb },
|
|
{ "delete", "delete a break point", cmd_delete },
|
|
{ "disable", "disable a break point", cmd_disable },
|
|
{ "enable", "enable a break point", cmd_enable },
|
|
{ "print", "print an expression", cmd_print },
|
|
{ "disp", "disp br/regs/cr/ar/tr/rr/db/psr/break/domain", cmd_disp},
|
|
{ "bev", "break on event", cmd_bev},
|
|
{ "set", "set reg val", cmd_set},
|
|
{ "help", "disp help", cmd_help },
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
|
|
enum cmd_status do_command (int vcpu, char *line)
|
|
{
|
|
char *cmd;
|
|
char *args;
|
|
int i;
|
|
const struct command_desc *desc;
|
|
static const struct command_desc *last_desc;
|
|
enum cmd_status status;
|
|
int flag_ambiguous;
|
|
|
|
cur_vcpu = vcpu;
|
|
cur_ctx = &vcpu_ctx_any[vcpu].c;
|
|
|
|
/* Handle repeat last-command. */
|
|
if (*line == 0) {
|
|
if (last_desc != NULL)
|
|
return (*last_desc->cmd)("");
|
|
else
|
|
return CMD_OK;
|
|
}
|
|
last_desc = NULL;
|
|
|
|
cmd = parse_arg (&line);
|
|
args = line;
|
|
|
|
desc = NULL;
|
|
flag_ambiguous = 0;
|
|
|
|
for (i = 0; commands[i].name; i++) {
|
|
const char *n = commands[i].name;
|
|
char *c = cmd;
|
|
|
|
while (*n == *c && *n)
|
|
n++, c++;
|
|
|
|
if (*c == 0) {
|
|
/* Match. */
|
|
if (desc != NULL) {
|
|
if (!flag_ambiguous)
|
|
printf ("ambiguous command: %s", desc->name);
|
|
printf (", %s", commands[i].name);
|
|
flag_ambiguous = 1;
|
|
}
|
|
else
|
|
desc = &commands[i];
|
|
}
|
|
}
|
|
|
|
if (flag_ambiguous) {
|
|
printf ("\n");
|
|
return CMD_ERROR;
|
|
}
|
|
else if (!desc) {
|
|
printf ("command not found, try help\n");
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
status = (*desc->cmd)(args);
|
|
if (status == CMD_REPEAT)
|
|
last_desc = desc;
|
|
return status;
|
|
}
|
|
|
|
void xenitp (int vcpu)
|
|
{
|
|
int ret;
|
|
struct sigaction sa;
|
|
|
|
cur_ctx = &vcpu_ctx_any[vcpu].c;
|
|
|
|
xc_handle = xc_interface_open (); /* for accessing control interface */
|
|
|
|
if (xc_domain_setdebugging (xc_handle, domid, 1) != 0)
|
|
perror ("setdebugging");
|
|
|
|
ret = xc_domain_pause (xc_handle, domid);
|
|
if (ret < 0) {
|
|
perror ("xc_domain_pause");
|
|
exit (-1);
|
|
}
|
|
|
|
ret = xc_ia64_vcpu_getcontext (xc_handle, domid, vcpu, cur_ctx);
|
|
if (ret < 0) {
|
|
perror ("xc_ia64_vcpu_getcontext");
|
|
exit (-1);
|
|
}
|
|
|
|
print_ctx (cur_ctx);
|
|
|
|
/* Catch ctrl-c. */
|
|
sa.sa_handler = &ctrl_c_handler;
|
|
sa.sa_flags = 0;
|
|
sigemptyset (&sa.sa_mask);
|
|
if (sigaction (SIGINT, &sa, NULL) != 0)
|
|
perror ("sigaction");
|
|
|
|
while (1) {
|
|
char buf[128];
|
|
int len;
|
|
|
|
printf ("XenITP> ");
|
|
fflush (stdout);
|
|
|
|
if (fgets (buf, sizeof (buf), stdin) == NULL)
|
|
break;
|
|
|
|
len = strlen ((char *)buf);
|
|
if (len >= 1 && buf[len - 1] == '\n')
|
|
buf[len - 1] = 0;
|
|
|
|
ret = do_command (vcpu, buf);
|
|
if (ret == CMD_QUIT)
|
|
break;
|
|
}
|
|
|
|
/* Clear debug bits. */
|
|
if ((cur_ctx->regs.psr & (PSR_SS | PSR_TB | PSR_DB)) != 0) {
|
|
cur_ctx->regs.psr &= ~(PSR_SS | PSR_TB | PSR_DB);
|
|
cur_ctx->regs.psr |= PSR_DD | PSR_ID;
|
|
vcpu_setcontext (cur_vcpu);
|
|
}
|
|
|
|
/* Disable debugging. */
|
|
if (xc_domain_setdebugging (xc_handle, domid, 0) != 0)
|
|
perror ("setdebugging");
|
|
|
|
if (!quit_paused) {
|
|
ret = xc_domain_unpause (xc_handle, domid);
|
|
if (ret < 0) {
|
|
perror ("xc_domain_unpause");
|
|
exit (-1);
|
|
}
|
|
}
|
|
|
|
xc_interface_close (xc_handle);
|
|
if (ret < 0) {
|
|
perror ("xc_interface_close");
|
|
exit (-1);
|
|
}
|
|
}
|
|
|
|
static void usage (void)
|
|
{
|
|
printf ("usage:\n");
|
|
printf (" xenitp <DOMAIN> [VCPU]\n");
|
|
|
|
}
|
|
|
|
int main (int argc, char **argv)
|
|
{
|
|
int ch;
|
|
static const char *sopts = "h"
|
|
;
|
|
static const struct option lopts[] = {
|
|
{"help", 0, NULL, 'h'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
int vcpu = 0;
|
|
|
|
while ((ch = getopt_long (argc, argv, sopts, lopts, NULL)) != -1) {
|
|
switch (ch) {
|
|
case 'h':
|
|
usage ();
|
|
exit (-1);
|
|
case '?':
|
|
fprintf (stderr, "%s --help for more options\n", argv[0]);
|
|
exit (-1);
|
|
}
|
|
}
|
|
|
|
argv += optind;
|
|
argc -= optind;
|
|
|
|
if (argc < 1 || argc > 2) {
|
|
usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
domid = atoi (argv[0]);
|
|
if (domid == 0) {
|
|
fprintf (stderr, "cannot trace dom0\n");
|
|
exit (-1);
|
|
}
|
|
|
|
if (argc == 2)
|
|
vcpu = atoi (argv[1]);
|
|
|
|
xenitp (vcpu);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-set-style: "BSD"
|
|
* c-basic-offset: 4
|
|
* tab-width: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|