diff options
Diffstat (limited to 'tools/debugger/gdbsx/xg/xg_main.c')
-rw-r--r-- | tools/debugger/gdbsx/xg/xg_main.c | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/tools/debugger/gdbsx/xg/xg_main.c b/tools/debugger/gdbsx/xg/xg_main.c new file mode 100644 index 0000000000..9cd9fc3a6d --- /dev/null +++ b/tools/debugger/gdbsx/xg/xg_main.c @@ -0,0 +1,806 @@ +/* + * 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. + */ + +/* This is the main module to interface with xen. This module exports APIs that + * allow for creating any remote debugger plugin. The APIs are: + * + * xg_init() : initialize + * xg_attach(): attach to the given guest, preparing it for debug + * xg_detach_deinit(): exit debugging + * + * xg_step() : single step the the given vcpu + * xg_resume_n_wait(): resume the target guest and wait for any debug event + * xg_regs_read(): read context of given vcpu + * xg_regs_write(): write context of given vcpu + * xg_read_mem(): read memory of guest at given VA + * xg_write_mem(): write memory of guest at given VA + * + * XGERR(): generic print error utility + * XGTRC(): generic trace utility + */ + +#include <stdio.h> +#include <stddef.h> +#include <stdarg.h> +#include <stdint.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/mman.h> +#include <string.h> +#include <time.h> +#include <assert.h> + +#include "xg_public.h" +#include <xen/version.h> +#include <xen/domctl.h> +#include <xen/sys/privcmd.h> +#include <xen/foreign/x86_32.h> +#include <xen/foreign/x86_64.h> + +#define XGMIN(x,y) (((x)<(y))?(x):(y)) + +#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */ + + +/* + * Contexts returned by xen: (gdbsx : dom 0 : hypervisor) + * + * 32 : 32 : 32 => 64bit context never returned. can't run 64bit guests + * 32 : 32 : 64 => 32bit ctxt for 32bit PV guest. 64bit ctxt for 64 PV guests. + * HVM always 64bit ctxt. + * 32 : 64 : 64 => N/A + * 64 : 64 : 64 => Same as 32:32:64 (CONFIG_COMPAT is almost always defined) + * 64 : 64 : 64 => !CONFIG_COMPAT : not supported. + */ +typedef union vcpu_guest_context_any { + vcpu_guest_context_x86_64_t ctxt64; + vcpu_guest_context_x86_32_t ctxt32; + vcpu_guest_context_t ctxt; +} vcpu_guest_context_any_t; + + +int xgtrc_on = 0; +struct xen_domctl domctl; /* just use a global domctl */ + +static int _hvm_guest; /* hvm guest? 32bit HVMs have 64bit context */ +static domid_t _dom_id; /* guest domid */ +static int _max_vcpu_id; /* thus max_vcpu_id+1 VCPUs */ +static int _dom0_fd; /* fd of /dev/privcmd */ +static int _32bit_hyp; /* hyp is 32bit */ + + +/* print trace info with function name pre-pended */ +void +xgtrc(const char *fn, const char *fmt, ...) +{ + char buf[2048]; + va_list args; + + fprintf(stderr, "%s:", fn); + va_start(args, fmt); + (void)vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + fprintf(stderr, "%s", buf); + fflush (stderr); +} + +/* print error msg with function name pre-pended */ +void +xgprt(const char *fn, const char *fmt, ...) +{ + char buf[2048]; + va_list args; + + fprintf(stderr, "ERROR:%s:", fn); + va_start(args, fmt); + (void)vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + fprintf (stderr, "%s", buf); + fflush (stderr); +} + + +/* + * Returns: 0 success + * -1 failure, errno set. + */ +int +xg_init() +{ + int flags, saved_errno; + + XGTRC("E\n"); + if ((_dom0_fd=open("/proc/xen/privcmd", O_RDWR)) == -1) { + perror("Failed to open /proc/xen/privcmd\n"); + return -1; + } + /* Although we return the file handle as the 'xc handle' the API + * does not specify / guarentee that this integer is in fact + * a file handle. Thus we must take responsiblity to ensure + * it doesn't propagate (ie leak) outside the process (copied comment)*/ + if ( (flags=fcntl(_dom0_fd, F_GETFD)) < 0 ) { + perror("Could not get file handle flags (F_GETFD)"); + goto error; + } + flags |= FD_CLOEXEC; + if (fcntl(_dom0_fd, F_SETFD, flags) < 0) { + perror("Could not set file handle flags"); + goto error; + } + + XGTRC("X:fd:%d\n", _dom0_fd); + return _dom0_fd; + + error: + XGTRC("X:Error: errno:%d\n", errno); + saved_errno = errno; + close(_dom0_fd); + errno = saved_errno; + return -1; +} + + +/* + * Returns : 0 Success, failure otherwise with errno set + */ +static int +_domctl_hcall(uint32_t cmd, /* which domctl hypercall */ + void *domctlarg, /* arg/buf to domctl to pin in mem */ + int sz) /* size of *domctlarg */ +{ + privcmd_hypercall_t hypercall; + int rc; + + if (domctlarg && sz && mlock(domctlarg, sz)) { + XGERR("Unable to pin domctl arg. p:%p sz:%d errno:%d\n", + domctlarg, sz, errno); + return 1; + } + domctl.cmd = cmd; + hypercall.op = __HYPERVISOR_domctl; + hypercall.arg[0] = (unsigned long)&domctl; + + rc = ioctl(_dom0_fd, IOCTL_PRIVCMD_HYPERCALL, (ulong)&hypercall); + if (domctlarg && sz) + munlock(domctlarg, sz); + return rc; +} + +/* + * Make sure we are running on hyp enabled for gdbsx. Also, note whether + * its 32bit. Fail if user typed 64bit for guest in case of 32bit hyp. + * + * RETURNS: 0 : everything OK. + */ +static int +_check_hyp(int guest_bitness) +{ + xen_capabilities_info_t xen_caps = ""; + privcmd_hypercall_t hypercall; + int rc; + + /* + * Try to unpause an invalid vcpu. If hypervisor supports gdbsx then + * this should fail with an error other than ENOSYS. + */ + domctl.u.gdbsx_pauseunp_vcpu.vcpu = ~0u; + (void)_domctl_hcall(XEN_DOMCTL_gdbsx_unpausevcpu, NULL, 0); + if (errno == ENOSYS) { + XGERR("Hyp is NOT enabled for gdbsx\n"); + return -1; + } + + if (mlock(&xen_caps, sizeof(xen_caps))) { + XGERR("Unable to pin xen_caps in memory. errno:%d\n", errno); + return -1; + } + memset(&xen_caps, 0, sizeof(xen_caps)); + + hypercall.op = __HYPERVISOR_xen_version; + hypercall.arg[0] = (unsigned long)XENVER_capabilities; + hypercall.arg[1] = (unsigned long)&xen_caps; + + rc = ioctl(_dom0_fd, IOCTL_PRIVCMD_HYPERCALL, (ulong)&hypercall); + munlock(&xen_caps, sizeof(xen_caps)); + XGTRC("XENCAPS:%s\n", xen_caps); + + if (rc != 0) { + XGERR("Failed xen_version hcall. errno:%d\n", errno); + return -1; + } + + _32bit_hyp = (strstr(xen_caps, "x86_64") == NULL); + if (_32bit_hyp && guest_bitness !=32) { + XGERR("32bit hyp can only run 32bit guests\n"); + return -1; + } + return 0; +} + +/* check if domain is alive and well + * returns : 0 if domain is not alive and well + */ +static int +_domain_ok(struct xen_domctl_getdomaininfo *domp) +{ + int rc = 0; + if (domp->flags & XEN_DOMINF_dying) + XGERR("Invalid domain (state dying)...\n"); + else + rc = 1; + return rc; +} + +/* Returns: 0 : success */ +static int +_unpause_domain(void) +{ + memset(&domctl.u, 0, sizeof(domctl.u)); + if (_domctl_hcall(XEN_DOMCTL_unpausedomain, NULL, 0)) { + XGERR("Unable to unpause domain:%d errno:%d\n", _dom_id, errno); + return -1; + } + return 0; +} + +/* + * Attach to the given domid for debugging. + * Returns: max vcpu id : Success + * -1 : Failure + */ +int +xg_attach(int domid, int guest_bitness) +{ + XGTRC("E:domid:%d\n", domid); + + _dom_id = domctl.domain = domid; + domctl.interface_version = XEN_DOMCTL_INTERFACE_VERSION; + + if (mlock(&domctl, sizeof(domctl))) { + XGERR("Unable to pin domctl in memory. errno:%d\n", errno); + return -1; + } + if (_check_hyp(guest_bitness)) + return -1; + + if (_domctl_hcall(XEN_DOMCTL_pausedomain, NULL, 0)) { + XGERR("Unable to pause domain:%d\n", _dom_id); + return -1; + } + + memset(&domctl.u, 0, sizeof(domctl.u)); + domctl.u.setdebugging.enable = 1; + if (_domctl_hcall(XEN_DOMCTL_setdebugging, NULL, 0)) { + XGERR("Unable to set domain to debug mode: errno:%d\n", errno); + _unpause_domain(); + return -1; + } + + memset(&domctl.u, 0, sizeof(domctl.u)); + if (_domctl_hcall(XEN_DOMCTL_getdomaininfo, NULL, 0)) { + XGERR("Unable to get domain info: domid:%d errno:%d\n", + domid, errno); + _unpause_domain(); + return -1; + } + if (!_domain_ok(&domctl.u.getdomaininfo)) { + _unpause_domain(); + return -1; + } + + _max_vcpu_id = domctl.u.getdomaininfo.max_vcpu_id; + _hvm_guest = (domctl.u.getdomaininfo.flags & XEN_DOMINF_hvm_guest); + return _max_vcpu_id; +} + + +/* Returns: 1 : domain is paused. 0 otherwise */ +static int +_domain_is_paused(void) +{ + memset(&domctl.u, 0, sizeof(domctl.u)); + if (_domctl_hcall(XEN_DOMCTL_getdomaininfo, NULL, 0)) { + XGERR("ERROR: Unable to get domain paused info:%d\n", _dom_id); + return 0; + } + return (domctl.u.getdomaininfo.flags & XEN_DOMINF_paused); +} + +/* Detach from guest for debugger exit */ +void +xg_detach_deinit(void) +{ + memset(&domctl.u, 0, sizeof(domctl.u)); + domctl.u.setdebugging.enable = 0; + if (_domctl_hcall(XEN_DOMCTL_setdebugging, NULL, 0)) { + XGERR("Unable to reset domain debug mode: errno:%d\n", errno); + } + if (_domain_is_paused()) + _unpause_domain(); + + close(_dom0_fd); +} + +/* + * Returns : 0 success. + * 1 error, with errno set (hopefully :)) + */ +static int +_wait_domain_pause(void) +{ + int dom_paused; + struct timespec ts={0, 10*1000*1000}; + + XGTRC("E:\n"); + do { + dom_paused = _domain_is_paused(); + nanosleep(&ts, NULL); + } while(!dom_paused); + return 0; +} + +/* + * Change the TF flag for single step. TF = (setit ? 1 : 0); + * Returns: 0 Success + */ +static int +_change_TF(vcpuid_t which_vcpu, int guest_bitness, int setit) +{ + union vcpu_guest_context_any anyc; + int sz = sizeof(anyc); + + memset(&anyc, 0, sz); + domctl.u.vcpucontext.vcpu = (uint16_t)which_vcpu; + set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &anyc.ctxt); + + if (_domctl_hcall(XEN_DOMCTL_getvcpucontext, &anyc, sz)) { + XGERR("Failed hcall to get vcpu ctxt for TF. errno:%d\n",errno); + return 1; + } + if (_32bit_hyp || (guest_bitness == 32 && !_hvm_guest)) { + if (setit) + anyc.ctxt32.user_regs.eflags |= X86_EFLAGS_TF; + else + anyc.ctxt32.user_regs.eflags &= ~X86_EFLAGS_TF; + } else { + if (setit) + anyc.ctxt64.user_regs.rflags |= X86_EFLAGS_TF; + else + anyc.ctxt64.user_regs.rflags &= ~X86_EFLAGS_TF; + } + + if (_domctl_hcall(XEN_DOMCTL_setvcpucontext, &anyc, sz)) { + XGERR("Failed hcall to set vcpu ctxt for TF. errno:%d\n",errno); + return 1; + } + return 0; +} + +/* Do the given DOMCTL hcall action(pause or unpause) on all but the given vcpu + * Returns: 0 success */ +static int +_allbutone_vcpu(uint32_t hcall, vcpuid_t which_vcpu) +{ + int i; + for (i=0; i <= _max_vcpu_id; i++) { + if (i == which_vcpu) + continue; + + memset(&domctl.u, 0, sizeof(domctl.u)); + domctl.u.gdbsx_pauseunp_vcpu.vcpu = i; + if (_domctl_hcall(hcall, NULL, 0)) { + XGERR("Unable to do:%d vcpu:%d errno:%d\n", + hcall, i, errno); + return 1; + } + } + return 0; +} + +/* + * Single step the given vcpu. This is achieved by pausing all but given vcpus, + * setting the TF flag, let the domain run and pause, unpause all vcpus, and + * clear TF flag on given vcpu. + * Returns: 0 success + */ +int +xg_step(vcpuid_t which_vcpu, int guest_bitness) +{ + int rc; + + XGTRC("E:vcpu:%d\n", (int)which_vcpu); + + if (_allbutone_vcpu(XEN_DOMCTL_gdbsx_pausevcpu, which_vcpu)) + return 1; + + if ((rc=_change_TF(which_vcpu, guest_bitness, 1))) + return rc; + + XGTRC("unpausing domain\n"); + + /* now unpause the domain so our vcpu can execute */ + if (_unpause_domain()) + return 1; + + /* wait for our vcpu to finish step */ + _wait_domain_pause(); + + _allbutone_vcpu(XEN_DOMCTL_gdbsx_unpausevcpu, which_vcpu); + rc = _change_TF(which_vcpu, guest_bitness, 0); + + return rc; +} + +/* + * check if any one of the vcpus is in a breakpoint + * Returns: vcpuid : if a vcpu found in a bp + * -1 : otherwise + */ +static vcpuid_t +_vcpu_in_bp(void) +{ + memset(&domctl.u, 0, sizeof(domctl.u)); + if (_domctl_hcall(XEN_DOMCTL_gdbsx_domstatus, NULL, 0)) { + XGERR("ERROR: Unable to check vcpu bp status:%d errno:%d\n", + _dom_id, errno); + return -1; + } + return domctl.u.gdbsx_domstatus.vcpu_id; +} + +/* + * Resume the domain if no pending events. If there are pending events, like + * another vcpu in a BP, report it. Otherwise, continue, and wait till an + * event, like bp or user doing xm pause, occurs. + * + * Returns: vcpuid : if a vcpu hits a breakpoint or end of step + * -1 : either an error (msg printed on terminal), or non-bp + * event, like "xm pause domid", to enter debugger + */ +vcpuid_t +xg_resume_n_wait(int guest_bitness) +{ + vcpuid_t vcpu; + + XGTRC("E:\n"); + assert(_domain_is_paused()); + + if ((vcpu=_vcpu_in_bp()) != -1) { + /* another vcpu in breakpoint. return it's id */ + return vcpu; + } + XGTRC("unpausing domain\n"); + if (_unpause_domain()) + return -1; + + /* now wait for domain to pause */ + _wait_domain_pause(); + + /* check again if any vcpu in BP, or user thru "xm pause" */ + vcpu = _vcpu_in_bp(); + + XGTRC("X:vcpu:%d\n", vcpu); + return vcpu; +} + +static void +_cp_32ctxt_to_32gdb(struct cpu_user_regs_x86_32 *cp, struct xg_gdb_regs32 *rp) +{ + memset(rp, 0, sizeof(struct xg_gdb_regs32)); + rp->ebx = cp->ebx; + rp->ecx = cp->ecx; + rp->edx = cp->edx; + rp->esi = cp->esi; + rp->edi = cp->edi; + rp->ebp = cp->ebp; + rp->eax = cp->eax; + rp->eip = cp->eip; + rp->cs = cp->cs; + rp->eflags = cp->eflags; + rp->esp = cp->esp; + rp->ss = cp->ss; + rp->es = cp->es; + rp->ds = cp->ds; + rp->fs = cp->fs; + rp->gs = cp->gs; +} + +static void +_cp_64ctxt_to_32gdb(struct cpu_user_regs_x86_64 *cp, struct xg_gdb_regs32 *rp) +{ + memset(rp, 0, sizeof(struct xg_gdb_regs32)); + rp->ebx = cp->rbx; + rp->ecx = cp->rcx; + rp->edx = cp->rdx; + rp->esi = cp->rsi; + rp->edi = cp->rdi; + rp->ebp = cp->rbp; + rp->eax = cp->rax; + rp->eip = cp->rip; + rp->cs = cp->cs; + rp->eflags = cp->rflags; + rp->esp = cp->rsp; + rp->ss = cp->ss; + rp->es = cp->es; + rp->ds = cp->ds; + rp->fs = cp->fs; + rp->gs = cp->gs; +} + +static void +_cp_64ctxt_to_64gdb(struct cpu_user_regs_x86_64 *cp, struct xg_gdb_regs64 *rp) +{ + memset(rp, 0, sizeof(struct xg_gdb_regs64)); + rp->r8 = cp->r8; + rp->r9 = cp->r9; + rp->r10 = cp->r10; + rp->r11 = cp->r11; + rp->r12 = cp->r12; + rp->r13 = cp->r13; + rp->r14 = cp->r14; + rp->r15 = cp->r15; + rp->rbx = cp->rbx; + rp->rcx = cp->rcx; + rp->rdx = cp->rdx; + rp->rsi = cp->rsi; + rp->rdi = cp->rdi; + rp->rbp = cp->rbp; + rp->rax = cp->rax; + rp->rip = cp->rip; + rp->rsp = cp->rsp; + rp->rflags = cp->rflags; + + rp->cs = (uint64_t)cp->cs; + rp->ss = (uint64_t)cp->ss; + rp->es = (uint64_t)cp->es; + rp->ds = (uint64_t)cp->ds; + rp->fs = (uint64_t)cp->fs; + rp->gs = (uint64_t)cp->gs; +#if 0 + printf("cp:%llx bp:%llx rip:%llx\n", rp->rsp, rp->rbp, rp->rip); + printf("rax:%llx rbx:%llx\n", rp->rax, rp->rbx); + printf("cs:%04x ss:%04x ds:%04x\n", (int)rp->cs, (int)rp->ss, + (int)rp->ds); +#endif +} + +static void +_cp_32gdb_to_32ctxt(struct xg_gdb_regs32 *rp, struct cpu_user_regs_x86_32 *cp) +{ + cp->ebx = rp->ebx; + cp->ecx = rp->ecx; + cp->edx = rp->edx; + cp->esi = rp->esi; + cp->edi = rp->edi; + cp->ebp = rp->ebp; + cp->eax = rp->eax; + cp->eip = rp->eip; + cp->esp = rp->esp; + cp->cs = rp->cs; + cp->ss = rp->ss; + cp->es = rp->es; + cp->ds = rp->ds; + cp->fs = rp->fs; + cp->gs = rp->gs; + cp->eflags = rp->eflags; +} + +static void +_cp_32gdb_to_64ctxt(struct xg_gdb_regs32 *rp, struct cpu_user_regs_x86_64 *cp) +{ + cp->rbx = rp->ebx; + cp->rcx = rp->ecx; + cp->rdx = rp->edx; + cp->rsi = rp->esi; + cp->rdi = rp->edi; + cp->rbp = rp->ebp; + cp->rax = rp->eax; + cp->rip = rp->eip; + cp->rsp = rp->esp; + cp->cs = rp->cs; + cp->ss = rp->ss; + cp->es = rp->es; + cp->ds = rp->ds; + cp->fs = rp->fs; + cp->gs = rp->gs; + cp->rflags = rp->eflags; +} + +static void +_cp_64gdb_to_64ctxt(struct xg_gdb_regs64 *rp, struct cpu_user_regs_x86_64 *cp) +{ + cp->r8 = rp->r8; + cp->r9 = rp->r9; + cp->r10 = rp->r10; + cp->r11 = rp->r11; + cp->r12 = rp->r12; + cp->r13 = rp->r13; + cp->r14 = rp->r14; + cp->r15 = rp->r15; + cp->rbx = rp->rbx; + cp->rcx = rp->rcx; + cp->rdx = rp->rdx; + cp->rsi = rp->rsi; + cp->rdi = rp->rdi; + cp->rbp = rp->rbp; + cp->rax = rp->rax; + cp->rip = rp->rip; + cp->rsp = rp->rsp; + cp->rflags = rp->rflags; + + cp->cs = (uint16_t)rp->cs; + cp->ss = (uint16_t)rp->ss; + cp->es = (uint16_t)rp->es; + cp->ds = (uint16_t)rp->ds; + cp->fs = (uint16_t)rp->fs; + cp->gs = (uint16_t)rp->gs; +} + + +/* get vcpu context from xen and return it in *ctxtp + * RETURNS: 0 for success + */ +static int +_get_vcpu_ctxt(vcpuid_t vcpu_id, union vcpu_guest_context_any *anycp) +{ + int sz = sizeof(union vcpu_guest_context_any); + + memset(anycp, 0, sz); + domctl.u.vcpucontext.vcpu = (uint16_t)vcpu_id; + set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &anycp->ctxt); + + if (_domctl_hcall(XEN_DOMCTL_getvcpucontext, anycp, sz)) { + XGERR("Failed hcall to get vcpu ctxt. errno:%d\n", errno); + return 1; + } + return 0; +} + +/* + * read regs for a particular vcpu. For now only GPRs, no FPRs. + * Returns: 0 success, else failure with errno set + */ +int +xg_regs_read(regstype_t which_regs, vcpuid_t which_vcpu, + union xg_gdb_regs *regsp, int guest_bitness) +{ + union vcpu_guest_context_any anyc; + struct cpu_user_regs_x86_32 *cr32p = &anyc.ctxt32.user_regs; + struct cpu_user_regs_x86_64 *cr64p = &anyc.ctxt64.user_regs; + struct xg_gdb_regs32 *r32p = ®sp->gregs_32; + struct xg_gdb_regs64 *r64p = ®sp->gregs_64; + int rc; + + if (which_regs != XG_GPRS) { + errno = EINVAL; + XGERR("regs got: %d. Expected GPRS:%d\n", which_regs, XG_GPRS); + return 1; + } + if ((rc=_get_vcpu_ctxt(which_vcpu, &anyc))) + return rc; + + /* 64bit hyp: only 32bit PV returns 32bit context, all others 64bit. + * 32bit hyp: all contexts returned are 32bit */ + if (guest_bitness == 32) { + if (_32bit_hyp || !_hvm_guest) + _cp_32ctxt_to_32gdb(cr32p, r32p); + else + _cp_64ctxt_to_32gdb(cr64p, r32p); + } else + _cp_64ctxt_to_64gdb(cr64p, r64p); + + XGTRC("X:vcpu:%d bitness:%d rc:%d\n", which_vcpu, guest_bitness, rc); + return rc; +} + +/* + * write registers for the given vcpu + * Returns: 0 success, 1 failure with errno + */ +int +xg_regs_write(regstype_t which_regs, vcpuid_t which_vcpu, + union xg_gdb_regs *regsp, int guest_bitness) +{ + union vcpu_guest_context_any anyc; + struct cpu_user_regs_x86_32 *cr32p = &anyc.ctxt32.user_regs; + struct cpu_user_regs_x86_64 *cr64p = &anyc.ctxt64.user_regs; + struct xg_gdb_regs32 *r32p = ®sp->gregs_32; + struct xg_gdb_regs64 *r64p = ®sp->gregs_64; + int rc, sz = sizeof(anyc); + + if (which_regs != XG_GPRS) { + errno = EINVAL; + XGERR("regs got: %d. Expected GPRS:%d\n", which_regs, XG_GPRS); + return 1; + } + if ((rc=_get_vcpu_ctxt(which_vcpu, &anyc))) + return rc; + + if (guest_bitness == 32) { + if (_32bit_hyp || !_hvm_guest) + _cp_32gdb_to_32ctxt(r32p, cr32p); + else + _cp_32gdb_to_64ctxt(r32p, cr64p); + } else + _cp_64gdb_to_64ctxt(r64p, cr64p); + + /* set vcpu context back */ + if ((rc =_domctl_hcall(XEN_DOMCTL_setvcpucontext, &anyc, sz))) { + XGERR("Failed hcall to set vcpu ctxt. errno:%d\n", errno); + return rc; + } + XGTRC("X:vcpu:%d bitness:%d rc:%d\n", which_vcpu, guest_bitness, rc); + return rc; +} + +/* + * Returns: bytes remaining to be read. 0 => read all bytes, ie, success. + */ +int +xg_read_mem(uint64_t guestva, char *tobuf, int tobuf_len, uint64_t pgd3val) +{ + struct xen_domctl_gdbsx_memio *iop = &domctl.u.gdbsx_guest_memio; + union {uint64_t llbuf8; char buf8[8];} u = {0}; + int i; + + XGTRC("E:gva:%llx tobuf:%lx len:%d\n", guestva, tobuf, tobuf_len); + + memset(&domctl.u, 0, sizeof(domctl.u)); + iop->pgd3val = pgd3val; + iop->gva = guestva; + iop->uva = (uint64_aligned_t)((unsigned long)tobuf); + iop->len = tobuf_len; + iop->gwr = 0; /* not writing to guest */ + + _domctl_hcall(XEN_DOMCTL_gdbsx_guestmemio, tobuf, tobuf_len); + + for(i=0; i < XGMIN(8, tobuf_len); u.buf8[i]=tobuf[i], i++); + XGTRC("X:remain:%d buf8:0x%llx\n", iop->remain, u.llbuf8); + + return iop->remain; +} + +/* + * Returns: bytes that could not be written. 0 => wrote all bytes, ie, success. + */ +int +xg_write_mem(uint64_t guestva, char *frombuf, int buflen, uint64_t pgd3val) +{ + struct xen_domctl_gdbsx_memio *iop = &domctl.u.gdbsx_guest_memio; + union {uint64_t llbuf8; char buf8[8];} u = {0}; + int i, rc; + + for(i=0; i < XGMIN(8, buflen); u.buf8[i]=frombuf[i], i++); + XGTRC("E:gva:%llx frombuf:%lx len:%d buf8:0x%llx\n", guestva, frombuf, + buflen, u.llbuf8); + + memset(&domctl.u, 0, sizeof(domctl.u)); + iop->pgd3val = pgd3val; + iop->gva = guestva; + iop->uva = (uint64_aligned_t)((unsigned long)frombuf); + iop->len = buflen; + iop->gwr = 1; /* writing to guest */ + + if ((rc=_domctl_hcall(XEN_DOMCTL_gdbsx_guestmemio, frombuf, buflen))) + XGERR("ERROR: failed to write %d bytes. errno:%d rc:%d\n", + iop->remain, errno, rc); + return iop->remain; +} + |