Files
xen/tools/debugger/gdbsx/xg/xg_main.c
T
Marek Marczykowski-Górecki 0c9821d5c8 tools/gdbsx: fix 'g' packet response for 64bit guests
gdb 8.0 fixed bounds checking for 'g' packet (commit
9dc193c3be85aafa60ceff57d3b0430af607b4ce "Check for truncated
registers in process_g_packet"). This revealed that gdbsx did
not properly formatted 'g' packet - segment registers and eflags are
expected to be 32-bit fields in the response (according to
gdb/features/i386/64bit-core.xml in gdb sources). Specific error is:

    Truncated register 26 in remote 'g' packet

instead of silently truncating part of register.

Additionally, it looks like segment registers of 64bit guests were never
reported correctly, because of type mismatch.

Signed-off-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
Acked-by: Wei Liu <wei.liu2@citrix.com>
2018-08-01 10:26:11 +01:00

837 lines
23 KiB
C

/*
* 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, see <http://www.gnu.org/licenses/>.
*/
/* 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 <sys/types.h>
#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("/dev/xen/privcmd", O_RDWR)) == -1) {
if ((_dom0_fd=open("/proc/xen/privcmd", O_RDWR)) == -1) {
perror("Failed to open /dev/xen/privcmd or /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;
}
/*
* Precondition: domctl global struct must be filled
* 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, &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, &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);
/* first try the MTF for hvm guest. otherwise do manually */
if (_hvm_guest) {
domctl.u.debug_op.vcpu = which_vcpu;
domctl.u.debug_op.op = setit ? XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON :
XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_OFF;
if (_domctl_hcall(XEN_DOMCTL_debug_op, NULL, 0) == 0) {
XGTRC("vcpu:%d:MTF success setit:%d\n", which_vcpu, setit);
return 0;
}
XGTRC("vcpu:%d:MTF failed. setit:%d\n", which_vcpu, setit);
}
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->eflags = cp->rflags;
rp->cs = cp->cs;
rp->ss = cp->ss;
rp->es = cp->es;
rp->ds = cp->ds;
rp->fs = cp->fs;
rp->gs = 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->eflags = 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->eflags;
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 = &regsp->gregs_32;
struct xg_gdb_regs64 *r64p = &regsp->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 = &regsp->gregs_32;
struct xg_gdb_regs64 *r64p = &regsp->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, rc;
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 */
if ( (rc = _domctl_hcall(XEN_DOMCTL_gdbsx_guestmemio, tobuf, tobuf_len)) )
{
XGTRC("ERROR: failed to read bytes. errno:%d rc:%d\n", errno, rc);
return 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 bytes to %llx. errno:%d rc:%d\n",
guestva, errno, rc);
return buflen;
}
return iop->remain;
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/