[TOOLS] Make xc_domain_{save, restore} understand compat guests

Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
This commit is contained in:
Tim Deegan committed 2007-09-19 17:44:23 +01:00
1 parent 72f379b5b2
commit 120bafda14
7 files changed
+365 -214

No files matched your search

+3
View File
@@ -58,6 +58,9 @@
/* number of pages to write at a time */
#define DUMP_INCREMENT (4 * 1024)
/* Don't yet support cross-address-size core dump */
#define guest_width (sizeof (unsigned long))
/* string table */
struct xc_core_strtab {
char *strings;
+3
View File
@@ -21,6 +21,9 @@
#include "xg_private.h"
#include "xc_core.h"
/* Don't yet support cross-address-size core dump */
#define guest_width (sizeof (unsigned long))
static int nr_gpfns(int xc_handle, domid_t domid)
{
return xc_memory_op(xc_handle, XENMEM_maximum_gpfn, &domid) + 1;
+127 -101
View File
@@ -56,6 +56,10 @@ static xen_pfn_t *p2m = NULL;
/* A table of P2M mappings in the current region */
static xen_pfn_t *p2m_batch = NULL;
/* Address size of the guest, in bytes */
unsigned int guest_width;
static ssize_t
read_exact(int fd, void *buf, size_t count)
{
@@ -168,22 +172,17 @@ static int uncanonicalize_pagetable(int xc_handle, uint32_t dom,
static xen_pfn_t *load_p2m_frame_list(int io_fd, int *pae_extended_cr3)
{
xen_pfn_t *p2m_frame_list;
vcpu_guest_context_t ctxt;
vcpu_guest_context_either_t ctxt;
xen_pfn_t p2m_fl_zero;
if ( (p2m_frame_list = malloc(P2M_FL_SIZE)) == NULL )
{
ERROR("Couldn't allocate p2m_frame_list array");
return NULL;
}
/* Read first entry of P2M list, or extended-info signature (~0UL). */
if ( !read_exact(io_fd, p2m_frame_list, sizeof(long)) )
if ( !read_exact(io_fd, &p2m_fl_zero, sizeof(long)) )
{
ERROR("read extended-info signature failed");
return NULL;
}
if ( p2m_frame_list[0] == ~0UL )
if ( p2m_fl_zero == ~0UL )
{
uint32_t tot_bytes;
@@ -211,25 +210,42 @@ static xen_pfn_t *load_p2m_frame_list(int io_fd, int *pae_extended_cr3)
/* VCPU context structure? */
if ( !strncmp(chunk_sig, "vcpu", 4) )
{
if ( !read_exact(io_fd, &ctxt, sizeof(ctxt)) )
/* Pick a guest word-size and PT depth from the ctxt size */
if ( chunk_bytes == sizeof (ctxt.x32) )
{
guest_width = 4;
if ( pt_levels > 2 )
pt_levels = 3;
}
else if ( chunk_bytes == sizeof (ctxt.x64) )
{
guest_width = 8;
pt_levels = 4;
}
else
{
ERROR("bad extended-info context size %d", chunk_bytes);
return NULL;
}
if ( !read_exact(io_fd, &ctxt, chunk_bytes) )
{
ERROR("read extended-info vcpu context failed");
return NULL;
}
tot_bytes -= sizeof(struct vcpu_guest_context);
chunk_bytes -= sizeof(struct vcpu_guest_context);
if ( ctxt.vm_assist & (1UL << VMASST_TYPE_pae_extended_cr3) )
tot_bytes -= chunk_bytes;
chunk_bytes = 0;
if ( GET_FIELD(&ctxt, vm_assist)
& (1UL << VMASST_TYPE_pae_extended_cr3) )
*pae_extended_cr3 = 1;
}
/* Any remaining bytes of this chunk: read and discard. */
while ( chunk_bytes )
{
unsigned long sz = chunk_bytes;
if ( sz > P2M_FL_SIZE )
sz = P2M_FL_SIZE;
if ( !read_exact(io_fd, p2m_frame_list, sz) )
unsigned long sz = MIN(chunk_bytes, sizeof(xen_pfn_t));
if ( !read_exact(io_fd, &p2m_fl_zero, sz) )
{
ERROR("read-and-discard extended-info chunk bytes failed");
return NULL;
@@ -240,15 +256,25 @@ static xen_pfn_t *load_p2m_frame_list(int io_fd, int *pae_extended_cr3)
}
/* Now read the real first entry of P2M list. */
if ( !read_exact(io_fd, p2m_frame_list, sizeof(long)) )
if ( !read_exact(io_fd, &p2m_fl_zero, sizeof(xen_pfn_t)) )
{
ERROR("read first entry of p2m_frame_list failed");
return NULL;
}
}
/* First entry is already read into the p2m array. */
if ( !read_exact(io_fd, &p2m_frame_list[1], P2M_FL_SIZE - sizeof(long)) )
/* Now that we know the guest's word-size, can safely allocate
* the p2m frame list */
if ( (p2m_frame_list = malloc(P2M_FL_SIZE)) == NULL )
{
ERROR("Couldn't allocate p2m_frame_list array");
return NULL;
}
/* First entry has already been read. */
p2m_frame_list[0] = p2m_fl_zero;
if ( !read_exact(io_fd, &p2m_frame_list[1],
(P2M_FL_ENTRIES - 1) * sizeof(xen_pfn_t)) )
{
ERROR("read p2m_frame_list failed");
return NULL;
@@ -272,11 +298,11 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
/* The new domain's shared-info frame number. */
unsigned long shared_info_frame;
unsigned char shared_info_page[PAGE_SIZE]; /* saved contents from file */
shared_info_t *old_shared_info = (shared_info_t *)shared_info_page;
shared_info_t *new_shared_info;
shared_info_either_t *old_shared_info = (shared_info_either_t *)shared_info_page;
shared_info_either_t *new_shared_info;
/* A copy of the CPU context of the guest. */
vcpu_guest_context_t ctxt;
vcpu_guest_context_either_t ctxt;
/* A table containing the type of each PFN (/not/ MFN!). */
unsigned long *pfn_type = NULL;
@@ -291,7 +317,7 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
xen_pfn_t *p2m_frame_list = NULL;
/* A temporary mapping of the guest's start_info page. */
start_info_t *start_info;
start_info_either_t *start_info;
/* Our mapping of the current region (batch) */
char *region_base;
@@ -324,16 +350,38 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
}
DPRINTF("xc_domain_restore start: p2m_size = %lx\n", p2m_size);
if ( !hvm )
if ( !get_platform_info(xc_handle, dom,
&max_mfn, &hvirt_start, &pt_levels, &guest_width) )
{
/*
* XXX For now, 32bit dom0's can only save/restore 32bit domUs
* on 64bit hypervisors.
*/
ERROR("Unable to get platform info.");
return 1;
}
/* The *current* word size of the guest isn't very interesting; for now
* assume the guest will be the same as we are. We'll fix that later
* if we discover otherwise. */
guest_width = sizeof(unsigned long);
pt_levels = (guest_width == 8) ? 4 : (pt_levels == 2) ? 2 : 3;
if ( lock_pages(&ctxt, sizeof(ctxt)) )
{
/* needed for build domctl, but might as well do early */
ERROR("Unable to lock ctxt");
return 1;
}
if ( !hvm )
{
/* Load the p2m frame list, plus potential extended info chunk */
p2m_frame_list = load_p2m_frame_list(io_fd, &pae_extended_cr3);
if ( !p2m_frame_list )
goto out;
/* Now that we know the word size, tell Xen about it */
memset(&domctl, 0, sizeof(domctl));
domctl.domain = dom;
domctl.cmd = XEN_DOMCTL_set_address_size;
domctl.u.address_size.size = sizeof(unsigned long) * 8;
domctl.u.address_size.size = guest_width * 8;
rc = do_domctl(xc_handle, &domctl);
if ( rc != 0 )
{
@@ -343,30 +391,8 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
rc = 1;
}
if ( !get_platform_info(xc_handle, dom,
&max_mfn, &hvirt_start, &pt_levels) )
{
ERROR("Unable to get platform info.");
return 1;
}
if ( lock_pages(&ctxt, sizeof(ctxt)) )
{
/* needed for build domctl, but might as well do early */
ERROR("Unable to lock ctxt");
return 1;
}
/* Load the p2m frame list, plus potential extended info chunk */
if ( !hvm )
{
p2m_frame_list = load_p2m_frame_list(io_fd, &pae_extended_cr3);
if ( !p2m_frame_list )
goto out;
}
/* We want zeroed memory so use calloc rather than malloc. */
p2m = calloc(p2m_size, sizeof(xen_pfn_t));
p2m = calloc(p2m_size, MAX(guest_width, sizeof (xen_pfn_t)));
pfn_type = calloc(p2m_size, sizeof(unsigned long));
region_mfn = calloc(MAX_BATCH_SIZE, sizeof(xen_pfn_t));
p2m_batch = calloc(MAX_BATCH_SIZE, sizeof(xen_pfn_t));
@@ -963,14 +989,16 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
if ( !(vcpumap & (1ULL << i)) )
continue;
if ( !read_exact(io_fd, &ctxt, sizeof(ctxt)) )
if ( !read_exact(io_fd, &ctxt, ((guest_width == 8)
? sizeof(ctxt.x64)
: sizeof(ctxt.x32))) )
{
ERROR("Error when reading ctxt %d", i);
goto out;
}
if ( !new_ctxt_format )
ctxt.flags |= VGCF_online;
SET_FIELD(&ctxt, flags, GET_FIELD(&ctxt, flags) | VGCF_online);
if ( i == 0 )
{
@@ -978,48 +1006,49 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
* Uncanonicalise the suspend-record frame number and poke
* resume record.
*/
pfn = ctxt.user_regs.edx;
pfn = GET_FIELD(&ctxt, user_regs.edx);
if ( (pfn >= p2m_size) ||
(pfn_type[pfn] != XEN_DOMCTL_PFINFO_NOTAB) )
{
ERROR("Suspend record frame number is bad");
goto out;
}
ctxt.user_regs.edx = mfn = p2m[pfn];
mfn = p2m[pfn];
SET_FIELD(&ctxt, user_regs.edx, mfn);
start_info = xc_map_foreign_range(
xc_handle, dom, PAGE_SIZE, PROT_READ | PROT_WRITE, mfn);
start_info->nr_pages = p2m_size;
start_info->shared_info = shared_info_frame << PAGE_SHIFT;
start_info->flags = 0;
*store_mfn = start_info->store_mfn = p2m[start_info->store_mfn];
start_info->store_evtchn = store_evtchn;
start_info->console.domU.mfn = p2m[start_info->console.domU.mfn];
start_info->console.domU.evtchn = console_evtchn;
*console_mfn = start_info->console.domU.mfn;
SET_FIELD(start_info, nr_pages, p2m_size);
SET_FIELD(start_info, shared_info, shared_info_frame<<PAGE_SHIFT);
SET_FIELD(start_info, flags, 0);
*store_mfn = p2m[GET_FIELD(start_info, store_mfn)];
SET_FIELD(start_info, store_mfn, *store_mfn);
SET_FIELD(start_info, store_evtchn, store_evtchn);
*console_mfn = p2m[GET_FIELD(start_info, console.domU.mfn)];
SET_FIELD(start_info, console.domU.mfn, *console_mfn);
SET_FIELD(start_info, console.domU.evtchn, console_evtchn);
munmap(start_info, PAGE_SIZE);
}
/* Uncanonicalise each GDT frame number. */
if ( ctxt.gdt_ents > 8192 )
if ( GET_FIELD(&ctxt, gdt_ents) > 8192 )
{
ERROR("GDT entry count out of range");
goto out;
}
for ( j = 0; (512*j) < ctxt.gdt_ents; j++ )
for ( j = 0; (512*j) < GET_FIELD(&ctxt, gdt_ents); j++ )
{
pfn = ctxt.gdt_frames[j];
pfn = GET_FIELD(&ctxt, gdt_frames[j]);
if ( (pfn >= p2m_size) ||
(pfn_type[pfn] != XEN_DOMCTL_PFINFO_NOTAB) )
{
ERROR("GDT frame number is bad");
ERROR("GDT frame number %i (0x%lx) is bad",
j, (unsigned long)pfn);
goto out;
}
ctxt.gdt_frames[j] = p2m[pfn];
SET_FIELD(&ctxt, gdt_frames[j], p2m[pfn]);
}
/* Uncanonicalise the page table base pointer. */
pfn = xen_cr3_to_pfn(ctxt.ctrlreg[3]);
pfn = xen_cr3_to_pfn(GET_FIELD(&ctxt, ctrlreg[3]));
if ( pfn >= p2m_size )
{
@@ -1036,21 +1065,18 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
(unsigned long)pt_levels<<XEN_DOMCTL_PFINFO_LTAB_SHIFT);
goto out;
}
ctxt.ctrlreg[3] = xen_pfn_to_cr3(p2m[pfn]);
SET_FIELD(&ctxt, ctrlreg[3], xen_pfn_to_cr3(p2m[pfn]));
/* Guest pagetable (x86/64) stored in otherwise-unused CR1. */
if ( (pt_levels == 4) && ctxt.ctrlreg[1] )
if ( (pt_levels == 4) && (ctxt.x64.ctrlreg[1] & 1) )
{
pfn = xen_cr3_to_pfn(ctxt.ctrlreg[1]);
pfn = xen_cr3_to_pfn(ctxt.x64.ctrlreg[1] & ~1);
if ( pfn >= p2m_size )
{
ERROR("User PT base is bad: pfn=%lu p2m_size=%lu type=%08lx",
pfn, p2m_size, pfn_type[pfn]);
ERROR("User PT base is bad: pfn=%lu p2m_size=%lu",
pfn, p2m_size);
goto out;
}
if ( (pfn_type[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) !=
((unsigned long)pt_levels<<XEN_DOMCTL_PFINFO_LTAB_SHIFT) )
{
@@ -1059,14 +1085,12 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
(unsigned long)pt_levels<<XEN_DOMCTL_PFINFO_LTAB_SHIFT);
goto out;
}
ctxt.ctrlreg[1] = xen_pfn_to_cr3(p2m[pfn]);
ctxt.x64.ctrlreg[1] = xen_pfn_to_cr3(p2m[pfn]);
}
domctl.cmd = XEN_DOMCTL_setvcpucontext;
domctl.domain = (domid_t)dom;
domctl.u.vcpucontext.vcpu = i;
set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &ctxt);
set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &ctxt.c);
rc = xc_domctl(xc_handle, &domctl);
if ( rc != 0 )
{
@@ -1087,22 +1111,16 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
xc_handle, dom, PAGE_SIZE, PROT_WRITE, shared_info_frame);
/* restore saved vcpu_info and arch specific info */
memcpy(&new_shared_info->vcpu_info,
&old_shared_info->vcpu_info,
sizeof(new_shared_info->vcpu_info));
memcpy(&new_shared_info->arch,
&old_shared_info->arch,
sizeof(new_shared_info->arch));
MEMCPY_FIELD(new_shared_info, old_shared_info, vcpu_info);
MEMCPY_FIELD(new_shared_info, old_shared_info, arch);
/* clear any pending events and the selector */
memset(&(new_shared_info->evtchn_pending[0]), 0,
sizeof (new_shared_info->evtchn_pending));
MEMSET_ARRAY_FIELD(new_shared_info, evtchn_pending, 0);
for ( i = 0; i < MAX_VIRT_CPUS; i++ )
new_shared_info->vcpu_info[i].evtchn_pending_sel = 0;
SET_FIELD(new_shared_info, vcpu_info[i].evtchn_pending_sel, 0);
/* mask event channels */
memset(&(new_shared_info->evtchn_mask[0]), 0xff,
sizeof (new_shared_info->evtchn_mask));
MEMSET_ARRAY_FIELD(new_shared_info, evtchn_mask, 0xff);
/* leave wallclock time. set by hypervisor */
munmap(new_shared_info, PAGE_SIZE);
@@ -1113,10 +1131,9 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
pfn = p2m_frame_list[i];
if ( (pfn >= p2m_size) || (pfn_type[pfn] != XEN_DOMCTL_PFINFO_NOTAB) )
{
ERROR("PFN-to-MFN frame number is bad");
ERROR("PFN-to-MFN frame number %i (%#lx) is bad", i, pfn);
goto out;
}
p2m_frame_list[i] = p2m[pfn];
}
@@ -1128,8 +1145,17 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom,
goto out;
}
memcpy(live_p2m, p2m, ROUNDUP(p2m_size * sizeof(xen_pfn_t), PAGE_SHIFT));
munmap(live_p2m, ROUNDUP(p2m_size * sizeof(xen_pfn_t), PAGE_SHIFT));
/* If the domain we're restoring has a different word size to ours,
* we need to repack the p2m appropriately */
if ( guest_width > sizeof (xen_pfn_t) )
for ( i = p2m_size - 1; i >= 0; i-- )
((uint64_t *)p2m)[i] = p2m[i];
else if ( guest_width > sizeof (xen_pfn_t) )
for ( i = 0; i < p2m_size; i++ )
((uint32_t *)p2m)[i] = p2m[i];
memcpy(live_p2m, p2m, ROUNDUP(p2m_size * guest_width, PAGE_SHIFT));
munmap(live_p2m, ROUNDUP(p2m_size * guest_width, PAGE_SHIFT));
DPRINTF("Domain ready to be built.\n");
rc = 0;
+141 -80
View File
@@ -54,9 +54,17 @@ static xen_pfn_t *live_p2m = NULL;
static xen_pfn_t *live_m2p = NULL;
static unsigned long m2p_mfn0;
/* Address size of the guest */
unsigned int guest_width;
/* grep fodder: machine_to_phys */
#define mfn_to_pfn(_mfn) live_m2p[(_mfn)]
#define mfn_to_pfn(_mfn) (live_m2p[(_mfn)])
#define pfn_to_mfn(_pfn) \
((xen_pfn_t) ((guest_width==8) \
? (((uint64_t *)live_p2m)[(_pfn)]) \
: (((uint32_t *)live_p2m)[(_pfn)])))
/*
* Returns TRUE if the given machine frame number has a unique mapping
@@ -65,19 +73,7 @@ static unsigned long m2p_mfn0;
#define MFN_IS_IN_PSEUDOPHYS_MAP(_mfn) \
(((_mfn) < (max_mfn)) && \
((mfn_to_pfn(_mfn) < (p2m_size)) && \
(live_p2m[mfn_to_pfn(_mfn)] == (_mfn))))
/* Returns TRUE if MFN is successfully converted to a PFN. */
#define translate_mfn_to_pfn(_pmfn) \
({ \
unsigned long mfn = *(_pmfn); \
int _res = 1; \
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) \
_res = 0; \
else \
*(_pmfn) = mfn_to_pfn(mfn); \
_res; \
})
(pfn_to_mfn(mfn_to_pfn(_mfn)) == (_mfn))))
/*
** During (live) save/migrate, we maintain a number of bitmaps to track
@@ -451,22 +447,25 @@ static int suspend_and_state(int (*suspend)(int), int xc_handle, int io_fd,
** it to update the MFN to a reasonable value.
*/
static void *map_frame_list_list(int xc_handle, uint32_t dom,
shared_info_t *shinfo)
shared_info_either_t *shinfo)
{
int count = 100;
void *p;
uint64_t fll = GET_FIELD(shinfo, arch.pfn_to_mfn_frame_list_list);
while ( count-- && (shinfo->arch.pfn_to_mfn_frame_list_list == 0) )
while ( count-- && (fll == 0) )
{
usleep(10000);
fll = GET_FIELD(shinfo, arch.pfn_to_mfn_frame_list_list);
}
if ( shinfo->arch.pfn_to_mfn_frame_list_list == 0 )
if ( fll == 0 )
{
ERROR("Timed out waiting for frame list updated.");
return NULL;
}
p = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ,
shinfo->arch.pfn_to_mfn_frame_list_list);
p = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, fll);
if ( p == NULL )
ERROR("Couldn't map p2m_frame_list_list (errno %d)", errno);
@@ -659,15 +658,16 @@ static xen_pfn_t *map_and_save_p2m_table(int xc_handle,
int io_fd,
uint32_t dom,
unsigned long p2m_size,
shared_info_t *live_shinfo)
shared_info_either_t *live_shinfo)
{
vcpu_guest_context_t ctxt;
vcpu_guest_context_either_t ctxt;
/* Double and single indirect references to the live P2M table */
xen_pfn_t *live_p2m_frame_list_list = NULL;
xen_pfn_t *live_p2m_frame_list = NULL;
void *live_p2m_frame_list_list = NULL;
void *live_p2m_frame_list = NULL;
/* A copy of the pfn-to-mfn table frame list. */
/* Copies of the above. */
xen_pfn_t *p2m_frame_list_list = NULL;
xen_pfn_t *p2m_frame_list = NULL;
/* The mapping of the live p2m table itself */
@@ -680,9 +680,28 @@ static xen_pfn_t *map_and_save_p2m_table(int xc_handle,
if ( !live_p2m_frame_list_list )
goto out;
/* Get a local copy of the live_P2M_frame_list_list */
if ( !(p2m_frame_list_list = malloc(PAGE_SIZE)) )
{
ERROR("Couldn't allocate p2m_frame_list_list array");
goto out;
}
memcpy(p2m_frame_list_list, live_p2m_frame_list_list, PAGE_SIZE);
/* Canonicalize guest's unsigned long vs ours */
if ( guest_width > sizeof(unsigned long) )
for ( i = 0; i < PAGE_SIZE/sizeof(unsigned long); i++ )
if ( i < PAGE_SIZE/guest_width )
p2m_frame_list_list[i] = ((uint64_t *)p2m_frame_list_list)[i];
else
p2m_frame_list_list[i] = 0;
else if ( guest_width < sizeof(unsigned long) )
for ( i = PAGE_SIZE/sizeof(unsigned long) - 1; i >= 0; i++ )
p2m_frame_list_list[i] = ((uint32_t *)p2m_frame_list_list)[i];
live_p2m_frame_list =
xc_map_foreign_batch(xc_handle, dom, PROT_READ,
live_p2m_frame_list_list,
p2m_frame_list_list,
P2M_FLL_ENTRIES);
if ( !live_p2m_frame_list )
{
@@ -690,22 +709,6 @@ static xen_pfn_t *map_and_save_p2m_table(int xc_handle,
goto out;
}
/* Map all the frames of the pfn->mfn table. For migrate to succeed,
the guest must not change which frames are used for this purpose.
(its not clear why it would want to change them, and we'll be OK
from a safety POV anyhow. */
p2m = xc_map_foreign_batch(xc_handle, dom, PROT_READ,
live_p2m_frame_list,
P2M_FL_ENTRIES);
if ( !p2m )
{
ERROR("Couldn't map p2m table");
goto out;
}
live_p2m = p2m; /* So that translation macros will work */
/* Get a local copy of the live_P2M_frame_list */
if ( !(p2m_frame_list = malloc(P2M_FL_SIZE)) )
{
@@ -714,19 +717,54 @@ static xen_pfn_t *map_and_save_p2m_table(int xc_handle,
}
memcpy(p2m_frame_list, live_p2m_frame_list, P2M_FL_SIZE);
/* Canonicalise the pfn-to-mfn table frame-number list. */
for ( i = 0; i < p2m_size; i += fpp )
/* Canonicalize guest's unsigned long vs ours */
if ( guest_width > sizeof(unsigned long) )
for ( i = 0; i < P2M_FL_ENTRIES; i++ )
p2m_frame_list[i] = ((uint64_t *)p2m_frame_list)[i];
else if ( guest_width < sizeof(unsigned long) )
for ( i = P2M_FL_ENTRIES - 1; i >= 0; i++ )
p2m_frame_list[i] = ((uint32_t *)p2m_frame_list)[i];
/* Map all the frames of the pfn->mfn table. For migrate to succeed,
the guest must not change which frames are used for this purpose.
(its not clear why it would want to change them, and we'll be OK
from a safety POV anyhow. */
p2m = xc_map_foreign_batch(xc_handle, dom, PROT_READ,
p2m_frame_list,
P2M_FL_ENTRIES);
if ( !p2m )
{
if ( !translate_mfn_to_pfn(&p2m_frame_list[i/fpp]) )
ERROR("Couldn't map p2m table");
goto out;
}
live_p2m = p2m; /* So that translation macros will work */
/* Canonicalise the pfn-to-mfn table frame-number list. */
for ( i = 0; i < p2m_size; i += FPP )
{
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(p2m_frame_list[i/FPP]) )
{
ERROR("Frame# in pfn-to-mfn frame list is not in pseudophys");
ERROR("entry %d: p2m_frame_list[%ld] is 0x%"PRIx64, i, i/fpp,
(uint64_t)p2m_frame_list[i/fpp]);
ERROR("entry %d: p2m_frame_list[%ld] is 0x%"PRIx64", max 0x%lx",
i, i/FPP, (uint64_t)p2m_frame_list[i/FPP], max_mfn);
if ( p2m_frame_list[i/FPP] < max_mfn )
{
ERROR("m2p[0x%"PRIx64"] = 0x%"PRIx64,
(uint64_t)p2m_frame_list[i/FPP],
(uint64_t)live_m2p[p2m_frame_list[i/FPP]]);
ERROR("p2m[0x%"PRIx64"] = 0x%"PRIx64,
(uint64_t)live_m2p[p2m_frame_list[i/FPP]],
(uint64_t)p2m[live_m2p[p2m_frame_list[i/FPP]]]);
}
goto out;
}
p2m_frame_list[i/FPP] = mfn_to_pfn(p2m_frame_list[i/FPP]);
}
if ( xc_vcpu_getcontext(xc_handle, dom, 0, &ctxt) )
if ( xc_vcpu_getcontext(xc_handle, dom, 0, &ctxt.c) )
{
ERROR("Could not get vcpu context");
goto out;
@@ -737,25 +775,26 @@ static xen_pfn_t *map_and_save_p2m_table(int xc_handle,
* a PAE guest understands extended CR3 (PDPTs above 4GB). Turns off
* slow paths in the restore code.
*/
if ( (pt_levels == 3) &&
(ctxt.vm_assist & (1UL << VMASST_TYPE_pae_extended_cr3)) )
{
unsigned long signature = ~0UL;
uint32_t tot_sz = sizeof(struct vcpu_guest_context) + 8;
uint32_t chunk_sz = sizeof(struct vcpu_guest_context);
uint32_t chunk_sz = ((guest_width==8)
? sizeof(ctxt.x64)
: sizeof(ctxt.x32));
uint32_t tot_sz = chunk_sz + 8;
char chunk_sig[] = "vcpu";
if ( !write_exact(io_fd, &signature, sizeof(signature)) ||
!write_exact(io_fd, &tot_sz, sizeof(tot_sz)) ||
!write_exact(io_fd, &chunk_sig, 4) ||
!write_exact(io_fd, &chunk_sz, sizeof(chunk_sz)) ||
!write_exact(io_fd, &ctxt, sizeof(ctxt)) )
!write_exact(io_fd, &ctxt, chunk_sz) )
{
ERROR("write: extended info");
goto out;
}
}
if ( !write_exact(io_fd, p2m_frame_list, P2M_FL_SIZE) )
if ( !write_exact(io_fd, p2m_frame_list,
P2M_FL_ENTRIES * sizeof(xen_pfn_t)) )
{
ERROR("write: p2m_frame_list");
goto out;
@@ -774,6 +813,9 @@ static xen_pfn_t *map_and_save_p2m_table(int xc_handle,
if ( live_p2m_frame_list )
munmap(live_p2m_frame_list, P2M_FLL_ENTRIES * PAGE_SIZE);
if ( p2m_frame_list_list )
free(p2m_frame_list_list);
if ( p2m_frame_list )
free(p2m_frame_list);
@@ -798,7 +840,7 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
unsigned long shared_info_frame;
/* A copy of the CPU context of the guest. */
vcpu_guest_context_t ctxt;
vcpu_guest_context_either_t ctxt;
/* A table containing the type of each PFN (/not/ MFN!). */
unsigned long *pfn_type = NULL;
@@ -808,7 +850,7 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
char page[PAGE_SIZE];
/* Live mapping of shared info structure */
shared_info_t *live_shinfo = NULL;
shared_info_either_t *live_shinfo = NULL;
/* base of the region in which domain memory is mapped */
unsigned char *region_base = NULL;
@@ -836,6 +878,8 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
/* HVM: magic frames for ioreqs and xenstore comms. */
uint64_t magic_pfns[3]; /* ioreq_pfn, bufioreq_pfn, store_pfn */
unsigned long mfn;
/* If no explicit control parameters given, use defaults */
max_iters = max_iters ? : DEF_MAX_ITERS;
max_factor = max_factor ? : DEF_MAX_FACTOR;
@@ -843,7 +887,7 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
initialize_mbit_rate();
if ( !get_platform_info(xc_handle, dom,
&max_mfn, &hvirt_start, &pt_levels) )
&max_mfn, &hvirt_start, &pt_levels, &guest_width) )
{
ERROR("Unable to get platform info.");
return 1;
@@ -1006,7 +1050,6 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
if ( !hvm )
{
int err = 0;
unsigned long mfn;
/* Map the P2M table, and write the list of P2M frames */
live_p2m = map_and_save_p2m_table(xc_handle, io_fd, dom,
@@ -1023,7 +1066,7 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
for ( i = 0; i < p2m_size; i++ )
{
mfn = live_p2m[i];
mfn = pfn_to_mfn(i);
if( (mfn != INVALID_P2M_ENTRY) && (mfn_to_pfn(mfn) != i) )
{
DPRINTF("i=0x%x mfn=%lx live_m2p=%lx\n", i,
@@ -1083,11 +1126,16 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
int n = permute(N, p2m_size, order_nr);
if ( debug )
DPRINTF("%d pfn= %08lx mfn= %08lx %d [mfn]= %08lx\n",
iter, (unsigned long)n, hvm ? 0 : live_p2m[n],
test_bit(n, to_send),
hvm ? 0 : mfn_to_pfn(live_p2m[n]&0xFFFFF));
{
DPRINTF("%d pfn= %08lx mfn= %08lx %d",
iter, (unsigned long)n,
hvm ? 0 : pfn_to_mfn(n),
test_bit(n, to_send));
if ( !hvm && is_mapped(pfn_to_mfn(n)) )
DPRINTF(" [mfn]= %08lx",
mfn_to_pfn(pfn_to_mfn(n)&0xFFFFF));
DPRINTF("\n");
}
if ( !last_iter &&
test_bit(n, to_send) &&
test_bit(n, to_skip) )
@@ -1118,7 +1166,7 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
if ( hvm )
pfn_type[batch] = n;
else
pfn_type[batch] = live_p2m[n];
pfn_type[batch] = pfn_to_mfn(n);
if ( !is_mapped(pfn_type[batch]) )
{
@@ -1451,7 +1499,7 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
for ( i = 0, j = 0; i < p2m_size; i++ )
{
if ( !is_mapped(live_p2m[i]) )
if ( !is_mapped(pfn_to_mfn(i)) )
j++;
}
@@ -1463,7 +1511,7 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
for ( i = 0, j = 0; i < p2m_size; )
{
if ( !is_mapped(live_p2m[i]) )
if ( !is_mapped(pfn_to_mfn(i)) )
pfntab[j++] = i;
i++;
@@ -1480,63 +1528,75 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
}
}
if ( xc_vcpu_getcontext(xc_handle, dom, 0, &ctxt) )
if ( xc_vcpu_getcontext(xc_handle, dom, 0, &ctxt.c) )
{
ERROR("Could not get vcpu context");
goto out;
}
/* Canonicalise the suspend-record frame number. */
if ( !translate_mfn_to_pfn(&ctxt.user_regs.edx) )
mfn = GET_FIELD(&ctxt, user_regs.edx);
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
{
ERROR("Suspend record is not in range of pseudophys map");
goto out;
}
SET_FIELD(&ctxt, user_regs.edx, mfn_to_pfn(mfn));
for ( i = 0; i <= info.max_vcpu_id; i++ )
{
if ( !(vcpumap & (1ULL << i)) )
continue;
if ( (i != 0) && xc_vcpu_getcontext(xc_handle, dom, i, &ctxt) )
if ( (i != 0) && xc_vcpu_getcontext(xc_handle, dom, i, &ctxt.c) )
{
ERROR("No context for VCPU%d", i);
goto out;
}
/* Canonicalise each GDT frame number. */
for ( j = 0; (512*j) < ctxt.gdt_ents; j++ )
for ( j = 0; (512*j) < GET_FIELD(&ctxt, gdt_ents); j++ )
{
if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[j]) )
mfn = GET_FIELD(&ctxt, gdt_frames[j]);
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
{
ERROR("GDT frame is not in range of pseudophys map");
goto out;
}
SET_FIELD(&ctxt, gdt_frames[j], mfn_to_pfn(mfn));
}
/* Canonicalise the page table base pointer. */
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(xen_cr3_to_pfn(ctxt.ctrlreg[3])) )
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(xen_cr3_to_pfn(
GET_FIELD(&ctxt, ctrlreg[3]))) )
{
ERROR("PT base is not in range of pseudophys map");
goto out;
}
ctxt.ctrlreg[3] =
xen_pfn_to_cr3(mfn_to_pfn(xen_cr3_to_pfn(ctxt.ctrlreg[3])));
SET_FIELD(&ctxt, ctrlreg[3],
xen_pfn_to_cr3(
mfn_to_pfn(
xen_cr3_to_pfn(
GET_FIELD(&ctxt, ctrlreg[3])))));
/* Guest pagetable (x86/64) stored in otherwise-unused CR1. */
if ( (pt_levels == 4) && ctxt.ctrlreg[1] )
if ( (pt_levels == 4) && ctxt.x64.ctrlreg[1] )
{
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(xen_cr3_to_pfn(ctxt.ctrlreg[1])) )
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(
xen_cr3_to_pfn(ctxt.x64.ctrlreg[1])) )
{
ERROR("PT base is not in range of pseudophys map");
goto out;
}
/* Least-significant bit means 'valid PFN'. */
ctxt.ctrlreg[1] = 1 |
xen_pfn_to_cr3(mfn_to_pfn(xen_cr3_to_pfn(ctxt.ctrlreg[1])));
ctxt.x64.ctrlreg[1] = 1 |
xen_pfn_to_cr3(
mfn_to_pfn(xen_cr3_to_pfn(ctxt.x64.ctrlreg[1])));
}
if ( !write_exact(io_fd, &ctxt, sizeof(ctxt)) )
if ( !write_exact(io_fd, &ctxt, ((guest_width==8)
? sizeof(ctxt.x64)
: sizeof(ctxt.x32))) )
{
ERROR("Error when writing to state file (1) (errno %d)", errno);
goto out;
@@ -1547,7 +1607,8 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
* Reset the MFN to be a known-invalid value. See map_frame_list_list().
*/
memcpy(page, live_shinfo, PAGE_SIZE);
((shared_info_t *)page)->arch.pfn_to_mfn_frame_list_list = 0;
SET_FIELD(((shared_info_either_t *)page),
arch.pfn_to_mfn_frame_list_list, 0);
if ( !write_exact(io_fd, page, PAGE_SIZE) )
{
ERROR("Error when writing to state file (1) (errno %d)", errno);
+4 -9
View File
@@ -8,13 +8,8 @@
#include <xen/foreign/x86_64.h>
#include <xen/hvm/params.h>
/* Need to provide the right flavour of vcpu context for Xen */
typedef union
{
vcpu_guest_context_x86_64_t c64;
vcpu_guest_context_x86_32_t c32;
vcpu_guest_context_t c;
} vcpu_guest_context_either_t;
/* Don't yet support cross-address-size uncooperative resume */
#define guest_width (sizeof (unsigned long))
static int modify_returncode(int xc_handle, uint32_t domid)
{
@@ -50,9 +45,9 @@ static int modify_returncode(int xc_handle, uint32_t domid)
if ( !info.hvm )
ctxt.c.user_regs.eax = 1;
else if ( strstr(caps, "x86_64") )
ctxt.c64.user_regs.eax = 1;
ctxt.x64.user_regs.eax = 1;
else
ctxt.c32.user_regs.eax = 1;
ctxt.x32.user_regs.eax = 1;
if ( (rc = xc_vcpu_setcontext(xc_handle, domid, 0, &ctxt.c)) != 0 )
return rc;
+15 -13
View File
@@ -133,13 +133,6 @@ typedef l4_pgentry_64_t l4_pgentry_t;
#define PAGE_SHIFT_X86 12
#define PAGE_SIZE_X86 (1UL << PAGE_SHIFT_X86)
#define PAGE_MASK_X86 (~(PAGE_SIZE_X86-1))
#if defined(__i386__)
#define MADDR_BITS_X86 44
#elif defined(__x86_64__)
#define MADDR_BITS_X86 52
#endif
#define MFN_MASK_X86 ((1ULL << (MADDR_BITS_X86 - PAGE_SHIFT_X86)) - 1)
#define MADDR_MASK_X86 (MFN_MASK_X86 << PAGE_SHIFT_X86)
#define PAGE_SHIFT_IA64 14
#define PAGE_SIZE_IA64 (1UL << PAGE_SHIFT_IA64)
@@ -147,19 +140,28 @@ typedef l4_pgentry_64_t l4_pgentry_t;
#define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1))
/* Number of xen_pfn_t in a page */
#define fpp (PAGE_SIZE/sizeof(xen_pfn_t))
/* XXX SMH: following 3 skanky macros rely on variable p2m_size being set */
/* XXX SMH: following skanky macros rely on variable p2m_size being set */
/* XXX TJD: also, "guest_width" should be the guest's sizeof(unsigned long) */
/* Number of xen_pfn_t in a page */
#define FPP (PAGE_SIZE/(guest_width))
/* Number of entries in the pfn_to_mfn_frame_list_list */
#define P2M_FLL_ENTRIES (((p2m_size)+(fpp*fpp)-1)/(fpp*fpp))
#define P2M_FLL_ENTRIES (((p2m_size)+(FPP*FPP)-1)/(FPP*FPP))
/* Number of entries in the pfn_to_mfn_frame_list */
#define P2M_FL_ENTRIES (((p2m_size)+fpp-1)/fpp)
#define P2M_FL_ENTRIES (((p2m_size)+FPP-1)/FPP)
/* Size in bytes of the pfn_to_mfn_frame_list */
#define P2M_FL_SIZE ((P2M_FL_ENTRIES)*sizeof(unsigned long))
#define P2M_FL_SIZE ((P2M_FL_ENTRIES)*(guest_width))
/* Masks for PTE<->PFN conversions */
#define MADDR_BITS_X86 ((guest_width == 8) ? 52 : 44)
#define MFN_MASK_X86 ((1ULL << (MADDR_BITS_X86 - PAGE_SHIFT_X86)) - 1)
#define MADDR_MASK_X86 (MFN_MASK_X86 << PAGE_SHIFT_X86)
#define PAEKERN_no 0
#define PAEKERN_yes 1
+72 -11
View File
@@ -6,6 +6,9 @@
#include "xc_private.h"
#include <xen/foreign/x86_32.h>
#include <xen/foreign/x86_64.h>
/*
** We process save/restore/migrate in batches of pages; the below
** determines how many pages we (at maximum) deal with in each batch.
@@ -32,15 +35,19 @@
** be a property of the domain, but for the moment we just read it
** from the hypervisor.
**
** - The width of a guest word (unsigned long), in bytes.
**
** Returns 1 on success, 0 on failure.
*/
static inline int get_platform_info(int xc_handle, uint32_t dom,
/* OUT */ unsigned long *max_mfn,
/* OUT */ unsigned long *hvirt_start,
/* OUT */ unsigned int *pt_levels)
/* OUT */ unsigned int *pt_levels,
/* OUT */ unsigned int *guest_width)
{
xen_capabilities_info_t xen_caps = "";
xen_platform_parameters_t xen_params;
DECLARE_DOMCTL;
if (xc_version(xc_handle, XENVER_platform_parameters, &xen_params) != 0)
return 0;
@@ -52,17 +59,18 @@ static inline int get_platform_info(int xc_handle, uint32_t dom,
*hvirt_start = xen_params.virt_start;
/*
* XXX For now, 32bit dom0's can only save/restore 32bit domUs
* on 64bit hypervisors, so no need to check which type of domain
* we're dealing with.
*/
memset(&domctl, 0, sizeof(domctl));
domctl.domain = dom;
domctl.cmd = XEN_DOMCTL_get_address_size;
if ( do_domctl(xc_handle, &domctl) != 0 )
return 0;
*guest_width = domctl.u.address_size.size / 8;
if (strstr(xen_caps, "xen-3.0-x86_64"))
#if defined(__i386__)
*pt_levels = 3;
#else
*pt_levels = 4;
#endif
/* Depends on whether it's a compat 32-on-64 guest */
*pt_levels = ( (*guest_width == 8) ? 4 : 3 );
else if (strstr(xen_caps, "xen-3.0-x86_32p"))
*pt_levels = 3;
else if (strstr(xen_caps, "xen-3.0-x86_32"))
@@ -95,3 +103,56 @@ static inline int get_platform_info(int xc_handle, uint32_t dom,
/* Returns TRUE if the PFN is currently mapped */
#define is_mapped(pfn_type) (!((pfn_type) & 0x80000000UL))
/* 32-on-64 support: saving 32bit guests from 64bit tools and vice versa */
typedef union
{
vcpu_guest_context_x86_64_t x64;
vcpu_guest_context_x86_32_t x32;
vcpu_guest_context_t c;
} vcpu_guest_context_either_t;
typedef union
{
shared_info_x86_64_t x64;
shared_info_x86_32_t x32;
shared_info_t s;
} shared_info_either_t;
typedef union
{
start_info_x86_64_t x64;
start_info_x86_32_t x32;
start_info_t s;
} start_info_either_t;
#define GET_FIELD(_p, _f) ((guest_width==8) ? ((_p)->x64._f) : ((_p)->x32._f))
#define SET_FIELD(_p, _f, _v) do { \
if (guest_width == 8) \
(_p)->x64._f = (_v); \
else \
(_p)->x32._f = (_v); \
} while (0)
#define MEMCPY_FIELD(_d, _s, _f) do { \
if (guest_width == 8) \
memcpy(&(_d)->x64._f, &(_s)->x64._f,sizeof((_d)->x64._f)); \
else \
memcpy(&(_d)->x32._f, &(_s)->x32._f,sizeof((_d)->x32._f)); \
} while (0)
#define MEMSET_ARRAY_FIELD(_p, _f, _v) do { \
if (guest_width == 8) \
memset(&(_p)->x64._f[0], (_v), sizeof((_p)->x64._f)); \
else \
memset(&(_p)->x32._f[0], (_v), sizeof((_p)->x32._f)); \
} while (0)
#ifndef MAX
#define MAX(_a, _b) ((_a) >= (_b) ? (_a) : (_b))
#endif
#ifndef MIN
#define MIN(_a, _b) ((_a) <= (_b) ? (_a) : (_b))
#endif
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.