mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Clang-15 (as seen in the FreeBSD 14 tests) complains:
xg_main.c:1248 error: a function declaration without a
prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
xg_init()
^
void
The error message is a bit confusing but appears to new as part of
-Wdeprecated-non-prototype which is part of supporting C2x which formally
removes K&R syntax.
Either way, fix the identified function.
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Anthony PERARD <anthony.perard@citrix.com>
837 lines
23 KiB
C
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(void)
|
|
{
|
|
int flags, saved_errno;
|
|
|
|
XGTRC("E\n");
|
|
if ((_dom0_fd=open("/dev/xen/privcmd", O_RDWR)) == -1 &&
|
|
(_dom0_fd=open("/proc/xen/privcmd", O_RDWR)) == -1 &&
|
|
(_dom0_fd=open("/kern/xen/privcmd", O_RDWR)) == -1) {
|
|
perror("Failed to open 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 = ®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, 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:
|
|
*/
|