aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/debug.c
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-10-15 09:36:40 +0100
committerKeir Fraser <keir.fraser@citrix.com>2009-10-15 09:36:40 +0100
commit61f2a440299cdf5ff809cd330a6400f6a5cdf910 (patch)
tree227d1b51b47a9bddf6dec437371eabad8d346221 /xen/arch/x86/debug.c
parentf8e61527db79a580d9d9d5602059ed4db6c06b2d (diff)
downloadxen-61f2a440299cdf5ff809cd330a6400f6a5cdf910.tar.gz
xen-61f2a440299cdf5ff809cd330a6400f6a5cdf910.tar.bz2
xen-61f2a440299cdf5ff809cd330a6400f6a5cdf910.zip
gdbsx: a gdbserver stub for xen.
It should be run on dom0 on gdbsx enabled hypervisor. For details, please see tools/debugger/gdbsx/README Signed-off-by: Mukesh Rathor <mukesh.rathor@oracle.com> Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
Diffstat (limited to 'xen/arch/x86/debug.c')
-rw-r--r--xen/arch/x86/debug.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/xen/arch/x86/debug.c b/xen/arch/x86/debug.c
new file mode 100644
index 0000000000..51193dd40b
--- /dev/null
+++ b/xen/arch/x86/debug.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <xen/config.h>
+#include <xen/sched.h>
+#include <xen/compile.h>
+#include <xen/mm.h>
+#include <xen/domain_page.h>
+#include <xen/guest_access.h>
+#include <asm/p2m.h>
+
+/*
+ * This file for general routines common to more than one debugger, like kdb,
+ * gdbsx, etc..
+ */
+
+#ifdef XEN_KDB_CONFIG
+extern volatile int kdbdbg;
+extern void kdbp(const char *fmt, ...);
+#define DBGP(...) {(kdbdbg) ? kdbp(__VA_ARGS__):0;}
+#define DBGP1(...) {(kdbdbg>1) ? kdbp(__VA_ARGS__):0;}
+#define DBGP2(...) {(kdbdbg>2) ? kdbp(__VA_ARGS__):0;}
+#else
+#define DBGP1(...) {0;}
+#define DBGP2(...) {0;}
+#endif
+
+typedef unsigned long dbgva_t;
+typedef unsigned char dbgbyte_t;
+
+
+/* Returns: mfn for the given (hvm guest) vaddr */
+static unsigned long
+dbg_hvm_va2mfn(dbgva_t vaddr, struct domain *dp, int toaddr)
+{
+ unsigned long mfn, gfn;
+ uint32_t pfec = PFEC_page_present;
+ p2m_type_t gfntype;
+
+ DBGP2("vaddr:%lx domid:%d\n", vaddr, dp->domain_id);
+
+ gfn = paging_gva_to_gfn(dp->vcpu[0], vaddr, &pfec);
+ if ( gfn == INVALID_GFN )
+ {
+ DBGP2("kdb:bad gfn from gva_to_gfn\n");
+ return INVALID_MFN;
+ }
+
+ mfn = mfn_x(gfn_to_mfn(dp, gfn, &gfntype));
+ if ( p2m_is_readonly(gfntype) && toaddr )
+ {
+ DBGP2("kdb:p2m_is_readonly: gfntype:%x\n", gfntype);
+ return INVALID_MFN;
+ }
+
+ DBGP2("X: vaddr:%lx domid:%d mfn:%lx\n", vaddr, dp->domain_id, mfn);
+ return mfn;
+}
+
+#if defined(__x86_64__)
+
+/*
+ * pgd3val: this is the value of init_mm.pgd[3] in a PV guest. It is optional.
+ * This to assist debug of modules in the guest. The kernel address
+ * space seems is always mapped, but modules are not necessarily
+ * mapped in any arbitraty guest cr3 that we pick if pgd3val is 0.
+ * Modules should always be addressible if we use cr3 from init_mm.
+ * Since pgd3val is already a pgd value, cr3->pgd[3], we just need to
+ * do 2 level lookups.
+ *
+ * NOTE: 4 level paging works for 32 PAE guests also because cpu runs in IA32-e
+ * mode.
+ * Returns: mfn for the given (pv guest) vaddr
+ */
+static unsigned long
+dbg_pv_va2mfn(dbgva_t vaddr, struct domain *dp, uint64_t pgd3val)
+{
+ l4_pgentry_t l4e, *l4t;
+ l3_pgentry_t l3e, *l3t;
+ l2_pgentry_t l2e, *l2t;
+ l1_pgentry_t l1e, *l1t;
+ unsigned long cr3 = (pgd3val ? pgd3val : dp->vcpu[0]->arch.cr3);
+ unsigned long mfn = cr3 >> PAGE_SHIFT;
+
+ DBGP2("vaddr:%lx domid:%d cr3:%lx pgd3:%lx\n", vaddr, dp->domain_id,
+ cr3, pgd3val);
+
+ if ( pgd3val == 0 )
+ {
+ l4t = mfn_to_virt(mfn);
+ l4e = l4t[l4_table_offset(vaddr)];
+ mfn = l4e_get_pfn(l4e);
+ DBGP2("l4t:%p l4to:%lx l4e:%lx mfn:%lx\n", l4t,
+ l4_table_offset(vaddr), l4e, mfn);
+ if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) )
+ {
+ DBGP1("l4 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3);
+ return INVALID_MFN;
+ }
+
+ l3t = mfn_to_virt(mfn);
+ l3e = l3t[l3_table_offset(vaddr)];
+ mfn = l3e_get_pfn(l3e);
+ DBGP2("l3t:%p l3to:%lx l3e:%lx mfn:%lx\n", l3t,
+ l3_table_offset(vaddr), l3e, mfn);
+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) )
+ {
+ DBGP1("l3 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3);
+ return INVALID_MFN;
+ }
+ }
+
+ l2t = mfn_to_virt(mfn);
+ l2e = l2t[l2_table_offset(vaddr)];
+ mfn = l2e_get_pfn(l2e);
+ DBGP2("l2t:%p l2to:%lx l2e:%lx mfn:%lx\n", l2t, l2_table_offset(vaddr),
+ l2e, mfn);
+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ||
+ (l2e_get_flags(l2e) & _PAGE_PSE) )
+ {
+ DBGP1("l2 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3);
+ return INVALID_MFN;
+ }
+ l1t = mfn_to_virt(mfn);
+ l1e = l1t[l1_table_offset(vaddr)];
+ mfn = l1e_get_pfn(l1e);
+ DBGP2("l1t:%p l1to:%lx l1e:%lx mfn:%lx\n", l1t, l1_table_offset(vaddr),
+ l1e, mfn);
+
+ return mfn_valid(mfn) ? mfn : INVALID_MFN;
+}
+
+#else
+
+/* Returns: mfn for the given (pv guest) vaddr */
+static unsigned long
+dbg_pv_va2mfn(dbgva_t vaddr, struct domain *dp, uint64_t pgd3val)
+{
+ l3_pgentry_t l3e, *l3t;
+ l2_pgentry_t l2e, *l2t;
+ l1_pgentry_t l1e, *l1t;
+ unsigned long cr3 = (pgd3val ? pgd3val : dp->vcpu[0]->arch.cr3);
+ unsigned long mfn = cr3 >> PAGE_SHIFT;
+
+ DBGP2("vaddr:%lx domid:%d cr3:%lx pgd3:%lx\n", vaddr, dp->domain_id,
+ cr3, pgd3val);
+
+ if ( pgd3val == 0 )
+ {
+ l3t = map_domain_page(mfn);
+ l3t += (cr3 & 0xFE0UL) >> 3;
+ l3e = l3t[l3_table_offset(vaddr)];
+ mfn = l3e_get_pfn(l3e);
+ unmap_domain_page(l3t);
+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) )
+ return INVALID_MFN;
+ }
+
+ l2t = map_domain_page(mfn);
+ l2e = l2t[l2_table_offset(vaddr)];
+ mfn = l2e_get_pfn(l2e);
+ unmap_domain_page(l2t);
+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ||
+ (l2e_get_flags(l2e) & _PAGE_PSE) )
+ return INVALID_MFN;
+
+ l1t = map_domain_page(mfn);
+ l1e = l1t[l1_table_offset(vaddr)];
+ mfn = l1e_get_pfn(l1e);
+ unmap_domain_page(l1t);
+
+ return mfn_valid(mfn) ? mfn : INVALID_MFN;
+}
+#endif /* defined(__x86_64__) */
+
+/* Returns: number of bytes remaining to be copied */
+static int
+dbg_rw_guest_mem(dbgva_t addr, dbgbyte_t *buf, int len, struct domain *dp,
+ int toaddr, uint64_t pgd3)
+{
+ while ( len > 0 )
+ {
+ char *va;
+ unsigned long mfn, pagecnt;
+
+ pagecnt = min_t(long, PAGE_SIZE - (addr & ~PAGE_MASK), len);
+
+ mfn = (dp->is_hvm
+ ? dbg_hvm_va2mfn(addr, dp, toaddr)
+ : dbg_pv_va2mfn(addr, dp, pgd3));
+ if ( mfn == INVALID_MFN )
+ break;
+
+ va = map_domain_page(mfn);
+ va = va + (addr & (PAGE_SIZE-1));
+
+ if ( toaddr )
+ {
+ memcpy(va, buf, pagecnt); /* va = buf */
+ paging_mark_dirty(dp, mfn);
+ }
+ else
+ {
+ memcpy(buf, va, pagecnt); /* buf = va */
+ }
+
+ unmap_domain_page(va);
+
+ addr += pagecnt;
+ buf += pagecnt;
+ len -= pagecnt;
+ }
+
+ return len;
+}
+
+/*
+ * addr is hypervisor addr if domid == IDLE_DOMAIN_ID, else it's guest addr
+ * buf is debugger buffer.
+ * if toaddr, then addr = buf (write to addr), else buf = addr (rd from guest)
+ * pgd3: value of init_mm.pgd[3] in guest. see above.
+ * Returns: number of bytes remaining to be copied.
+ */
+int
+dbg_rw_mem(dbgva_t addr, dbgbyte_t *buf, int len, domid_t domid, int toaddr,
+ uint64_t pgd3)
+{
+ struct domain *dp = get_domain_by_id(domid);
+ int hyp = (domid == IDLE_DOMAIN_ID);
+
+ DBGP2("gmem:addr:%lx buf:%p len:$%d domid:%x toaddr:%x dp:%p\n",
+ addr, buf, len, domid, toaddr, dp);
+ if ( hyp )
+ {
+ if ( toaddr )
+ len = __copy_to_user((void *)addr, buf, len);
+ else
+ len = __copy_from_user(buf, (void *)addr, len);
+ }
+ else
+ {
+ if ( dp && !dp->is_dying ) /* make sure guest is still there */
+ len= dbg_rw_guest_mem(addr, buf, len, dp, toaddr, pgd3);
+ }
+
+ DBGP2("gmem:exit:len:$%d\n", len);
+ return len;
+}
+