mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
Since the shared info layout is fixed, guests are required to use VCPUOP_register_vcpu_info prior to booting any vCPU beyond the traditional limit of 32. MAX_VIRT_CPUS, being an implemetation detail of the hypervisor, is no longer being exposed in the public headers. The tools changes are clearly incomplete (and done only so things would build again), and the current state of the tools (using scalar variables all over the place to represent vCPU bitmaps) very likely doesn't permit booting DomU-s with more than the traditional number of vCPU-s. Testing of the extended functionality was done with Dom0 (96 vCPU-s, as well as 128 vCPU-s out of which the kernel elected - by way of a simple kernel side patch - to use only some, resulting in a sparse bitmap). ia64 changes only to make things build, and build-tested only (and the tools part only as far as the build would go without encountering unrelated problems in the blktap code). Signed-off-by: Jan Beulich <jbeulich@novell.com>
1733 lines
46 KiB
C
1733 lines
46 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;
|
|
|
|
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;
|
|
xc_dominfo_t dominfo;
|
|
|
|
xc_handle = xc_interface_open (); /* for accessing control interface */
|
|
|
|
ret = xc_domain_getinfo (xc_handle, domid, 1, &dominfo);
|
|
if (ret < 0) {
|
|
perror ("xc_domain_getinfo");
|
|
exit (-1);
|
|
}
|
|
|
|
vcpu_ctx_any = calloc (sizeof(vcpu_ctx_any), dominfo.max_vcpu_id + 1);
|
|
if (!vcpu_ctx_any) {
|
|
perror ("vcpu context array alloc");
|
|
exit (-1);
|
|
}
|
|
cur_ctx = &vcpu_ctx_any[vcpu].c;
|
|
|
|
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:
|
|
*/
|