mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
324 lines
9.1 KiB
C
324 lines
9.1 KiB
C
/******************************************************************************
|
|
* xc_ia64_linux_restore.c
|
|
*
|
|
* Restore the state of a Linux session.
|
|
*
|
|
* Copyright (c) 2003, K A Fraser.
|
|
* Rewritten for ia64 by Tristan Gingold <tristan.gingold@bull.net>
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "xg_private.h"
|
|
|
|
#define PFN_TO_KB(_pfn) ((_pfn) << (PAGE_SHIFT - 10))
|
|
|
|
/* total number of pages used by the current guest */
|
|
static unsigned long max_pfn;
|
|
|
|
static ssize_t
|
|
read_exact(int fd, void *buf, size_t count)
|
|
{
|
|
int r = 0, s;
|
|
unsigned char *b = buf;
|
|
|
|
while (r < count) {
|
|
s = read(fd, &b[r], count - r);
|
|
if ((s == -1) && (errno == EINTR))
|
|
continue;
|
|
if (s <= 0) {
|
|
break;
|
|
}
|
|
r += s;
|
|
}
|
|
|
|
return (r == count) ? 1 : 0;
|
|
}
|
|
|
|
static int
|
|
read_page(int xc_handle, int io_fd, uint32_t dom, unsigned long pfn)
|
|
{
|
|
void *mem;
|
|
|
|
mem = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
|
|
PROT_READ|PROT_WRITE, pfn);
|
|
if (mem == NULL) {
|
|
ERROR("cannot map page");
|
|
return -1;
|
|
}
|
|
if (!read_exact(io_fd, mem, PAGE_SIZE)) {
|
|
ERROR("Error when reading from state file (5)");
|
|
return -1;
|
|
}
|
|
munmap(mem, PAGE_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
xc_linux_restore(int xc_handle, int io_fd, uint32_t dom,
|
|
unsigned long nr_pfns, unsigned int store_evtchn,
|
|
unsigned long *store_mfn, unsigned int console_evtchn,
|
|
unsigned long *console_mfn)
|
|
{
|
|
DECLARE_DOMCTL;
|
|
int rc = 1, i;
|
|
unsigned long mfn, pfn;
|
|
unsigned long ver;
|
|
|
|
/* 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 *shared_info = (shared_info_t *)shared_info_page;
|
|
|
|
/* A copy of the CPU context of the guest. */
|
|
vcpu_guest_context_t ctxt;
|
|
|
|
unsigned long *page_array = NULL;
|
|
|
|
/* A temporary mapping of the guest's start_info page. */
|
|
start_info_t *start_info;
|
|
|
|
max_pfn = nr_pfns;
|
|
|
|
DPRINTF("xc_linux_restore start: max_pfn = %ld\n", max_pfn);
|
|
|
|
|
|
if (!read_exact(io_fd, &ver, sizeof(unsigned long))) {
|
|
ERROR("Error when reading version");
|
|
goto out;
|
|
}
|
|
if (ver != 1) {
|
|
ERROR("version of save doesn't match");
|
|
goto out;
|
|
}
|
|
|
|
if (mlock(&ctxt, sizeof(ctxt))) {
|
|
/* needed for build domctl, but might as well do early */
|
|
ERROR("Unable to mlock ctxt");
|
|
return 1;
|
|
}
|
|
|
|
/* Get the domain's shared-info frame. */
|
|
domctl.cmd = XEN_DOMCTL_getdomaininfo;
|
|
domctl.domain = (domid_t)dom;
|
|
if (xc_domctl(xc_handle, &domctl) < 0) {
|
|
ERROR("Could not get information on new domain");
|
|
goto out;
|
|
}
|
|
shared_info_frame = domctl.u.getdomaininfo.shared_info_frame;
|
|
|
|
if (xc_domain_setmaxmem(xc_handle, dom, PFN_TO_KB(max_pfn)) != 0) {
|
|
errno = ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
if (xc_domain_memory_increase_reservation(xc_handle, dom, max_pfn,
|
|
0, 0, NULL) != 0) {
|
|
ERROR("Failed to increase reservation by %ld KB", PFN_TO_KB(max_pfn));
|
|
errno = ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
DPRINTF("Increased domain reservation by %ld KB\n", PFN_TO_KB(max_pfn));
|
|
|
|
if (!read_exact(io_fd, &domctl.u.arch_setup, sizeof(domctl.u.arch_setup))) {
|
|
ERROR("read: domain setup");
|
|
goto out;
|
|
}
|
|
|
|
/* Build firmware (will be overwritten). */
|
|
domctl.domain = (domid_t)dom;
|
|
domctl.u.arch_setup.flags &= ~XEN_DOMAINSETUP_query;
|
|
domctl.u.arch_setup.bp = ((nr_pfns - 3) << PAGE_SHIFT)
|
|
+ sizeof (start_info_t);
|
|
domctl.u.arch_setup.maxmem = (nr_pfns - 3) << PAGE_SHIFT;
|
|
|
|
domctl.cmd = XEN_DOMCTL_arch_setup;
|
|
if (xc_domctl(xc_handle, &domctl))
|
|
goto out;
|
|
|
|
/* Get pages. */
|
|
page_array = malloc(max_pfn * sizeof(unsigned long));
|
|
if (page_array == NULL ) {
|
|
ERROR("Could not allocate memory");
|
|
goto out;
|
|
}
|
|
|
|
if (xc_ia64_get_pfn_list(xc_handle, dom, page_array,
|
|
0, max_pfn) != max_pfn) {
|
|
ERROR("Could not get the page frame list");
|
|
goto out;
|
|
}
|
|
|
|
DPRINTF("Reloading memory pages: 0%%\n");
|
|
|
|
while (1) {
|
|
if (!read_exact(io_fd, &mfn, sizeof(unsigned long))) {
|
|
ERROR("Error when reading batch size");
|
|
goto out;
|
|
}
|
|
if (mfn == INVALID_MFN)
|
|
break;
|
|
|
|
pfn = page_array[mfn];
|
|
|
|
//DPRINTF("xc_linux_restore: page %lu/%lu at %lx\n", mfn, max_pfn, pfn);
|
|
|
|
if (read_page(xc_handle, io_fd, dom, page_array[mfn]) < 0)
|
|
goto out;
|
|
}
|
|
|
|
DPRINTF("Received all pages\n");
|
|
|
|
/* Get the list of PFNs that are not in the psuedo-phys map */
|
|
{
|
|
unsigned int count;
|
|
unsigned long *pfntab;
|
|
int rc;
|
|
|
|
if (!read_exact(io_fd, &count, sizeof(count))) {
|
|
ERROR("Error when reading pfn count");
|
|
goto out;
|
|
}
|
|
|
|
pfntab = malloc(sizeof(unsigned long) * count);
|
|
if (!pfntab) {
|
|
ERROR("Out of memory");
|
|
goto out;
|
|
}
|
|
|
|
if (!read_exact(io_fd, pfntab, sizeof(unsigned long)*count)) {
|
|
ERROR("Error when reading pfntab");
|
|
goto out;
|
|
}
|
|
|
|
DPRINTF ("Try to free %u pages\n", count);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
volatile unsigned long pfn;
|
|
|
|
struct xen_memory_reservation reservation = {
|
|
.nr_extents = 1,
|
|
.extent_order = 0,
|
|
.domid = dom
|
|
};
|
|
set_xen_guest_handle(reservation.extent_start,
|
|
(unsigned long *)&pfn);
|
|
|
|
pfn = pfntab[i];
|
|
rc = xc_memory_op(xc_handle, XENMEM_decrease_reservation,
|
|
&reservation);
|
|
if (rc != 1) {
|
|
ERROR("Could not decrease reservation : %d", rc);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
DPRINTF("Decreased reservation by %d pages\n", count);
|
|
}
|
|
|
|
|
|
if (!read_exact(io_fd, &ctxt, sizeof(ctxt))) {
|
|
ERROR("Error when reading ctxt");
|
|
goto out;
|
|
}
|
|
|
|
fprintf(stderr, "ip=%016lx, b0=%016lx\n", ctxt.user_regs.cr_iip,
|
|
ctxt.user_regs.b0);
|
|
|
|
/* First to initialize. */
|
|
domctl.cmd = XEN_DOMCTL_setvcpucontext;
|
|
domctl.domain = (domid_t)dom;
|
|
domctl.u.vcpucontext.vcpu = 0;
|
|
set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &ctxt);
|
|
if (xc_domctl(xc_handle, &domctl) != 0) {
|
|
ERROR("Couldn't set vcpu context");
|
|
goto out;
|
|
}
|
|
|
|
/* Second to set registers... */
|
|
ctxt.flags = VGCF_EXTRA_REGS;
|
|
domctl.cmd = XEN_DOMCTL_setvcpucontext;
|
|
domctl.domain = (domid_t)dom;
|
|
domctl.u.vcpucontext.vcpu = 0;
|
|
set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &ctxt);
|
|
if (xc_domctl(xc_handle, &domctl) != 0) {
|
|
ERROR("Couldn't set vcpu context");
|
|
goto out;
|
|
}
|
|
|
|
/* Just a check. */
|
|
if (xc_vcpu_getcontext(xc_handle, dom, 0 /* XXX */, &ctxt)) {
|
|
ERROR("Could not get vcpu context");
|
|
goto out;
|
|
}
|
|
|
|
/* Then get privreg page. */
|
|
if (read_page(xc_handle, io_fd, dom, ctxt.privregs_pfn) < 0) {
|
|
ERROR("Could not read vcpu privregs");
|
|
goto out;
|
|
}
|
|
|
|
/* Read shared info. */
|
|
shared_info = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
|
|
PROT_READ|PROT_WRITE, shared_info_frame);
|
|
if (shared_info == NULL) {
|
|
ERROR("cannot map page");
|
|
goto out;
|
|
}
|
|
if (!read_exact(io_fd, shared_info, PAGE_SIZE)) {
|
|
ERROR("Error when reading shared_info page");
|
|
goto out;
|
|
}
|
|
|
|
/* clear any pending events and the selector */
|
|
memset(&(shared_info->evtchn_pending[0]), 0,
|
|
sizeof (shared_info->evtchn_pending));
|
|
for (i = 0; i < MAX_VIRT_CPUS; i++)
|
|
shared_info->vcpu_info[i].evtchn_pending_sel = 0;
|
|
|
|
mfn = page_array[shared_info->arch.start_info_pfn];
|
|
|
|
munmap (shared_info, PAGE_SIZE);
|
|
|
|
/* Uncanonicalise the suspend-record frame number and poke resume rec. */
|
|
start_info = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
|
|
PROT_READ | PROT_WRITE, mfn);
|
|
start_info->nr_pages = max_pfn;
|
|
start_info->shared_info = shared_info_frame << PAGE_SHIFT;
|
|
start_info->flags = 0;
|
|
*store_mfn = page_array[start_info->store_mfn];
|
|
start_info->store_evtchn = store_evtchn;
|
|
*console_mfn = page_array[start_info->console.domU.mfn];
|
|
start_info->console.domU.evtchn = console_evtchn;
|
|
munmap(start_info, PAGE_SIZE);
|
|
|
|
/*
|
|
* Safety checking of saved context:
|
|
* 1. user_regs is fine, as Xen checks that on context switch.
|
|
* 2. fpu_ctxt is fine, as it can't hurt Xen.
|
|
* 3. trap_ctxt needs the code selectors checked.
|
|
* 4. ldt base must be page-aligned, no more than 8192 ents, ...
|
|
* 5. gdt already done, and further checking is done by Xen.
|
|
* 6. check that kernel_ss is safe.
|
|
* 7. pt_base is already done.
|
|
* 8. debugregs are checked by Xen.
|
|
* 9. callback code selectors need checking.
|
|
*/
|
|
DPRINTF("Domain ready to be built.\n");
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
if ((rc != 0) && (dom != 0))
|
|
xc_domain_destroy(xc_handle, dom);
|
|
|
|
free (page_array);
|
|
|
|
DPRINTF("Restore exit with rc=%d\n", rc);
|
|
|
|
return rc;
|
|
}
|