mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
bitkeeper revision 1.612 (3fbba6e271_EVTI6k2Ndd7VThsLqTA)
Many files: new file Makefile: New library (libxi) which makes it easier to access the control interfaces.
This commit is contained in:
1 parent
52a6c2c90b
commit
6cfe1d4b8b
15 files changed
+2062
No files matched your search
@@ -184,6 +184,19 @@
|
||||
3fb01fd5B-UeibZkmSCOUZckNyNFYA tools/internal/xi_vbd_list.c
|
||||
3f86be322bd0h9jG3krZFOUgCDoxZg tools/internal/xi_vif_params.c
|
||||
3eb781fd7211MZsLxJSiuy7W4KnJXg tools/internal/xi_vifinit
|
||||
3fbba6dbDfYvJSsw9500b4SZyUhxjQ tools/libxi/Makefile
|
||||
3fbba6dbEVkVMX0JuDFzap9jeaucGA tools/libxi/libxi_bvtsched.c
|
||||
3fbba6dbasJQV-MVElDC0DGSHMiL5w tools/libxi/libxi_domain.c
|
||||
3fbba6dbNCU7U6nsMYiXzKkp3ztaJg tools/libxi/libxi_linux_build.c
|
||||
3fbba6dbl267zZOAVHYLOdLCdhcZMw tools/libxi/libxi_linux_restore.c
|
||||
3fbba6db7li3FJiABYtCmuGxOJxEGw tools/libxi/libxi_linux_save.c
|
||||
3fbba6db7WnnJr0KFrIFrqNlSKvFYg tools/libxi/libxi_misc.c
|
||||
3fbba6dctWRWlFJkYb6hdix2X4WMuw tools/libxi/libxi_private.c
|
||||
3fbba6dcbVrG2hPzEzwdeV_UC8kydQ tools/libxi/libxi_private.h
|
||||
3fbba6dcoGq9hQlksrBUfC2P5F6sGg tools/libxi/libxi_vbd.c
|
||||
3fbba6dc38q-ioRlwSR_quw4G3qUeQ tools/libxi/libxi_vif.c
|
||||
3fbba6dc1uU7U3IFeF6A-XEOYF2MkQ tools/libxi/rpm.spec
|
||||
3fbba6dcrNxtygEcgJYAJJ1gCQqfsA tools/libxi/xi.h
|
||||
3f776bd2Xd-dUcPKlPN2vG89VGtfvQ tools/misc/Makefile
|
||||
3f6dc136ZKOjd8PIqLbFBl_v-rnkGg tools/misc/miniterm/Makefile
|
||||
3f6dc140C8tAeBfroAF24VrmCS4v_w tools/misc/miniterm/README
|
||||
|
||||
@@ -21,17 +21,20 @@ all:
|
||||
$(MAKE) -C balloon
|
||||
$(MAKE) -C control
|
||||
$(MAKE) -C internal
|
||||
$(MAKE) -C libxi
|
||||
$(MAKE) -C misc
|
||||
|
||||
install: all
|
||||
$(MAKE) -C balloon install
|
||||
$(MAKE) -C control install
|
||||
$(MAKE) -C internal install
|
||||
$(MAKE) -C libxi install
|
||||
$(MAKE) -C misc install
|
||||
|
||||
clean:
|
||||
$(MAKE) -C balloon clean
|
||||
$(MAKE) -C control clean
|
||||
$(MAKE) -C internal clean
|
||||
$(MAKE) -C libxi clean
|
||||
$(MAKE) -C misc clean
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -c -Wall -O3
|
||||
CFLAGS += -I../../xen/include -I../../xenolinux-sparse/include
|
||||
|
||||
HDRS = $(wildcard *.h)
|
||||
OBJS = $(patsubst %.c,%.o,$(wildcard libxi_*.c))
|
||||
|
||||
LIBS = libxi.a libxi.so
|
||||
|
||||
all: check-for-zlib $(LIBS)
|
||||
ranlib libxi.a
|
||||
|
||||
check-for-zlib:
|
||||
@if [ ! -e /usr/include/zlib.h ]; then \
|
||||
echo "***********************************************************"; \
|
||||
echo "ERROR: install zlib header files (http://www.gzip.org/zlib)"; \
|
||||
echo "***********************************************************"; \
|
||||
false; \
|
||||
fi
|
||||
|
||||
install: all
|
||||
mkdir -p ../../../install/lib
|
||||
mkdir -p ../../../install/include
|
||||
cp -a $(LIBS) ../../../install/lib
|
||||
for i in $(LIBS) do ; chmod 755 ../../../install/bin/$i ; done
|
||||
cp -a xi.h ../../../install/include
|
||||
chmod 644 ../../../install/include/xi.h
|
||||
|
||||
clean:
|
||||
$(RM) *.a *.so *.o *.rpm $(LIBS)
|
||||
|
||||
rpm: all
|
||||
rm -rf staging
|
||||
mkdir staging
|
||||
mkdir staging/i386
|
||||
rpmbuild --define "staging$$PWD/staging" --define '_builddir.' \
|
||||
--define "_rpmdir$$PWD/staging" -bb rpm.spec
|
||||
mv staging/i386/*.rpm .
|
||||
rm -rf staging
|
||||
|
||||
libxi.so: $(OBJS)
|
||||
$(LD) -shared -o $@ $^ -lz
|
||||
|
||||
libxi.a: libxi.a($(OBJS))
|
||||
|
||||
%.o: %.c $(HDRS) Makefile
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
@@ -0,0 +1,33 @@
|
||||
/******************************************************************************
|
||||
* libxi_bvtsched.c
|
||||
*
|
||||
* API for manipulating parameters of the Borrowed Virtual Time scheduler.
|
||||
*
|
||||
* Copyright (c) 2003, K A Fraser.
|
||||
*/
|
||||
|
||||
#include "libxi_private.h"
|
||||
|
||||
int xi_bvtsched_global_set(unsigned long ctx_allow)
|
||||
{
|
||||
dom0_op_t op;
|
||||
op.cmd = DOM0_BVTCTL;
|
||||
op.u.bvtctl.ctx_allow = ctx_allow;
|
||||
return do_dom0_op(&op);
|
||||
}
|
||||
|
||||
int xi_bvtsched_domain_set(unsigned int domid,
|
||||
unsigned long mcuadv,
|
||||
unsigned long warp,
|
||||
unsigned long warpl,
|
||||
unsigned long warpu)
|
||||
{
|
||||
dom0_op_t op;
|
||||
op.cmd = DOM0_ADJUSTDOM;
|
||||
op.u.adjustdom.domain = domid;
|
||||
op.u.adjustdom.mcu_adv = mcuadv;
|
||||
op.u.adjustdom.warp = warp;
|
||||
op.u.adjustdom.warpl = warpl;
|
||||
op.u.adjustdom.warpu = warpu;
|
||||
return do_dom0_op(&op);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/******************************************************************************
|
||||
* libxi_domain.c
|
||||
*
|
||||
* API for manipulating and obtaining information on domains.
|
||||
*
|
||||
* Copyright (c) 2003, K A Fraser.
|
||||
*/
|
||||
|
||||
#include "libxi_private.h"
|
||||
|
||||
int xi_domain_create(unsigned int mem_kb, const char *name)
|
||||
{
|
||||
int err;
|
||||
dom0_op_t op;
|
||||
|
||||
op.cmd = DOM0_CREATEDOMAIN;
|
||||
op.u.createdomain.memory_kb = mem_kb;
|
||||
strncpy(op.u.createdomain.name, name, MAX_DOMAIN_NAME);
|
||||
op.u.createdomain.name[MAX_DOMAIN_NAME-1] = '\0';
|
||||
|
||||
err = do_dom0_op(&op);
|
||||
|
||||
return (err < 0) ? err : op.u.createdomain.domain;
|
||||
}
|
||||
|
||||
|
||||
int xi_domain_start(unsigned int domid)
|
||||
{
|
||||
dom0_op_t op;
|
||||
op.cmd = DOM0_STARTDOMAIN;
|
||||
op.u.startdomain.domain = domid;
|
||||
return do_dom0_op(&op);
|
||||
}
|
||||
|
||||
|
||||
int xi_domain_stop(unsigned int domid)
|
||||
{
|
||||
dom0_op_t op;
|
||||
op.cmd = DOM0_STOPDOMAIN;
|
||||
op.u.stopdomain.domain = domid;
|
||||
return do_dom0_op(&op);
|
||||
}
|
||||
|
||||
|
||||
int xi_domain_destroy(unsigned int domid, int force)
|
||||
{
|
||||
dom0_op_t op;
|
||||
op.cmd = DOM0_DESTROYDOMAIN;
|
||||
op.u.destroydomain.domain = domid;
|
||||
op.u.destroydomain.force = !!force;
|
||||
return do_dom0_op(&op);
|
||||
}
|
||||
|
||||
int xi_domain_getinfo(unsigned int first_domid,
|
||||
unsigned int max_doms,
|
||||
xi_dominfo_t *info)
|
||||
{
|
||||
unsigned int nr_doms, next_domid = first_domid;
|
||||
dom0_op_t op;
|
||||
|
||||
for ( nr_doms = 0; nr_doms < max_doms; nr_doms++ )
|
||||
{
|
||||
op.cmd = DOM0_GETDOMAININFO;
|
||||
op.u.getdomaininfo.domain = next_domid;
|
||||
if ( do_dom0_op(&op) < 0 )
|
||||
break;
|
||||
info->domid = op.u.getdomaininfo.domain;
|
||||
info->cpu = op.u.getdomaininfo.processor;
|
||||
info->has_cpu = op.u.getdomaininfo.has_cpu;
|
||||
info->stopped = (op.u.getdomaininfo.state == DOMSTATE_STOPPED);
|
||||
info->nr_pages = op.u.getdomaininfo.tot_pages;
|
||||
info->cpu_time = op.u.getdomaininfo.cpu_time;
|
||||
strncpy(info->name, op.u.getdomaininfo.name, XI_DOMINFO_MAXNAME);
|
||||
info->name[XI_DOMINFO_MAXNAME-1] = '\0';
|
||||
|
||||
next_domid = op.u.getdomaininfo.domain + 1;
|
||||
}
|
||||
|
||||
return nr_doms;
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
/******************************************************************************
|
||||
* libxi_linux_build.c
|
||||
*/
|
||||
|
||||
#include "libxi_private.h"
|
||||
#include <zlib.h>
|
||||
|
||||
/* This string is written to the head of every guest kernel image. */
|
||||
#define GUEST_SIG "XenoGues"
|
||||
#define SIG_LEN 8
|
||||
|
||||
#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED)
|
||||
#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER)
|
||||
|
||||
static long get_tot_pages(int domid)
|
||||
{
|
||||
dom0_op_t op;
|
||||
op.cmd = DOM0_GETDOMAININFO;
|
||||
op.u.getdomaininfo.domain = domid;
|
||||
return (do_dom0_op(&op) < 0) ? -1 : op.u.getdomaininfo.tot_pages;
|
||||
}
|
||||
|
||||
static int get_pfn_list(
|
||||
int domid, unsigned long *pfn_buf, unsigned long max_pfns)
|
||||
{
|
||||
dom0_op_t op;
|
||||
int ret;
|
||||
op.cmd = DOM0_GETMEMLIST;
|
||||
op.u.getmemlist.domain = domid;
|
||||
op.u.getmemlist.max_pfns = max_pfns;
|
||||
op.u.getmemlist.buffer = pfn_buf;
|
||||
|
||||
if ( mlock(pfn_buf, max_pfns * sizeof(unsigned long)) != 0 )
|
||||
return -1;
|
||||
|
||||
ret = do_dom0_op(&op);
|
||||
|
||||
(void)munlock(pfn_buf, max_pfns * sizeof(unsigned long));
|
||||
|
||||
return (ret < 0) ? -1 : op.u.getmemlist.num_pfns;
|
||||
}
|
||||
|
||||
static int send_pgupdates(mmu_update_t *updates, int nr_updates)
|
||||
{
|
||||
int ret = -1;
|
||||
privcmd_hypercall_t hypercall;
|
||||
|
||||
hypercall.op = __HYPERVISOR_mmu_update;
|
||||
hypercall.arg[0] = (unsigned long)updates;
|
||||
hypercall.arg[1] = (unsigned long)nr_updates;
|
||||
|
||||
if ( mlock(updates, nr_updates * sizeof(*updates)) != 0 )
|
||||
goto out1;
|
||||
|
||||
if ( do_xen_hypercall(&hypercall) < 0 )
|
||||
goto out2;
|
||||
|
||||
ret = 0;
|
||||
|
||||
out2: (void)munlock(updates, nr_updates * sizeof(*updates));
|
||||
out1: return ret;
|
||||
}
|
||||
|
||||
/* Read the kernel header, extracting the image size and load address. */
|
||||
static int read_kernel_header(gzFile gfd, long dom_size,
|
||||
unsigned long *load_addr, int verbose)
|
||||
{
|
||||
char signature[SIG_LEN];
|
||||
|
||||
gzread(gfd, signature, SIG_LEN);
|
||||
if ( strncmp(signature, GUEST_SIG, SIG_LEN) )
|
||||
{
|
||||
if ( verbose )
|
||||
ERROR("Kernel image does not contain required signature");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read the load address which immediately follows the Xeno signature. */
|
||||
gzread(gfd, load_addr, sizeof(unsigned long));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copy_to_domain_page(unsigned long dst_pfn, void *src_page)
|
||||
{
|
||||
void *vaddr = map_pfn(dst_pfn);
|
||||
if ( vaddr == NULL )
|
||||
return -1;
|
||||
memcpy(vaddr, src_page, PAGE_SIZE);
|
||||
unmap_pfn(vaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_guestos(
|
||||
int dom, gzFile kernel_gfd, int initrd_fd, unsigned long tot_pages,
|
||||
unsigned long *virt_startinfo_addr, unsigned long virt_load_addr,
|
||||
dom0_builddomain_t *builddomain, const char *cmdline,
|
||||
unsigned long shared_info_frame, int verbose)
|
||||
{
|
||||
l1_pgentry_t *vl1tab = NULL, *vl1e = NULL;
|
||||
l2_pgentry_t *vl2tab = NULL, *vl2e = NULL;
|
||||
unsigned long *page_array = NULL;
|
||||
mmu_update_t *pgt_update_arr = NULL, *pgt_updates = NULL;
|
||||
int alloc_index, num_pt_pages;
|
||||
unsigned long l2tab;
|
||||
unsigned long l1tab = 0;
|
||||
unsigned long num_pgt_updates = 0;
|
||||
unsigned long count, pt_start, i, j;
|
||||
unsigned long initrd_addr = 0, initrd_len = 0;
|
||||
start_info_t *start_info;
|
||||
shared_info_t *shared_info;
|
||||
unsigned long ksize;
|
||||
|
||||
memset(builddomain, 0, sizeof(*builddomain));
|
||||
|
||||
if ( init_pfn_mapper() < 0 )
|
||||
goto error_out;
|
||||
|
||||
pgt_updates = malloc((tot_pages + 1024) * 3 * sizeof(mmu_update_t));
|
||||
page_array = malloc(tot_pages * sizeof(unsigned long));
|
||||
pgt_update_arr = pgt_updates;
|
||||
if ( (pgt_update_arr == NULL) || (page_array == NULL) )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Could not allocate memory");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ( get_pfn_list(dom, page_array, tot_pages) != tot_pages )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Could not get the page frame list");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
/* Load the guest OS image. Let it take no more than 1/2 memory.*/
|
||||
for ( i = 0; i < ((tot_pages/2)*PAGE_SIZE); i += PAGE_SIZE )
|
||||
{
|
||||
char page[PAGE_SIZE];
|
||||
int size;
|
||||
if ( (size = gzread(kernel_gfd, page, PAGE_SIZE)) == -1 )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Error reading kernel image, could not"
|
||||
" read the whole image.");
|
||||
goto error_out;
|
||||
}
|
||||
if ( size == 0 )
|
||||
goto kernel_copied;
|
||||
copy_to_domain_page(page_array[i>>PAGE_SHIFT], page);
|
||||
}
|
||||
if ( verbose )
|
||||
ERROR("Kernel too big to safely fit in domain memory");
|
||||
goto error_out;
|
||||
|
||||
kernel_copied:
|
||||
/* ksize is kernel-image size rounded up to a page boundary. */
|
||||
ksize = i;
|
||||
|
||||
/* Load the initial ramdisk image. */
|
||||
if ( initrd_fd >= 0 )
|
||||
{
|
||||
struct stat stat;
|
||||
unsigned long isize;
|
||||
|
||||
if ( fstat(initrd_fd, &stat) < 0 )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Could not stat the initrd image");
|
||||
goto error_out;
|
||||
}
|
||||
isize = stat.st_size;
|
||||
if ( (isize + ksize) > ((tot_pages/2) * PAGE_SIZE) )
|
||||
{
|
||||
if ( verbose )
|
||||
ERROR("Kernel/initrd too big to safely fit in domain memory");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
initrd_addr = virt_load_addr + ksize;
|
||||
initrd_len = isize;
|
||||
|
||||
for ( j = 0, i = ksize; j < isize; j += PAGE_SIZE, i += PAGE_SIZE )
|
||||
{
|
||||
char page[PAGE_SIZE];
|
||||
int size = ((isize-j) < PAGE_SIZE) ? (isize-j) : PAGE_SIZE;
|
||||
if ( read(initrd_fd, page, size) != size )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Error reading initrd image, could not"
|
||||
" read the whole image.");
|
||||
goto error_out;
|
||||
}
|
||||
copy_to_domain_page(page_array[i>>PAGE_SHIFT], page);
|
||||
}
|
||||
}
|
||||
|
||||
alloc_index = tot_pages - 1;
|
||||
|
||||
/* Count bottom-level PTs, rounding up. */
|
||||
num_pt_pages = (l1_table_offset(virt_load_addr) + tot_pages + 1023) / 1024;
|
||||
|
||||
/* We must also count the page directory. */
|
||||
num_pt_pages++;
|
||||
|
||||
/* Index of first PT page. */
|
||||
pt_start = tot_pages - num_pt_pages;
|
||||
|
||||
/*
|
||||
* First allocate page for page dir. Allocation goes backwards from the end
|
||||
* of the allocated physical address space.
|
||||
*/
|
||||
l2tab = page_array[alloc_index] << PAGE_SHIFT;
|
||||
alloc_index--;
|
||||
builddomain->ctxt.pt_base = l2tab;
|
||||
|
||||
/*
|
||||
* Pin down l2tab addr as page dir page - causes hypervisor to provide
|
||||
* correct protection for the page
|
||||
*/
|
||||
pgt_updates->ptr = l2tab | MMU_EXTENDED_COMMAND;
|
||||
pgt_updates->val = MMUEXT_PIN_L2_TABLE;
|
||||
pgt_updates++;
|
||||
num_pgt_updates++;
|
||||
|
||||
/* Initialise the page tables. */
|
||||
if ( (vl2tab = map_pfn(l2tab >> PAGE_SHIFT)) == NULL )
|
||||
goto error_out;
|
||||
memset(vl2tab, 0, PAGE_SIZE);
|
||||
vl2e = vl2tab + l2_table_offset(virt_load_addr);
|
||||
for ( count = 0; count < tot_pages; count++ )
|
||||
{
|
||||
if ( ((unsigned long)vl1e & (PAGE_SIZE-1)) == 0 )
|
||||
{
|
||||
l1tab = page_array[alloc_index] << PAGE_SHIFT;
|
||||
if ( (vl1tab = map_pfn(l1tab >> PAGE_SHIFT)) == NULL )
|
||||
goto error_out;
|
||||
memset(vl1tab, 0, PAGE_SIZE);
|
||||
alloc_index--;
|
||||
|
||||
vl1e = vl1tab + l1_table_offset(virt_load_addr +
|
||||
(count << PAGE_SHIFT));
|
||||
|
||||
/* make apropriate entry in the page directory */
|
||||
pgt_updates->ptr = (unsigned long)vl2e;
|
||||
pgt_updates->val = l1tab | L2_PROT;
|
||||
pgt_updates++;
|
||||
num_pgt_updates++;
|
||||
vl2e++;
|
||||
}
|
||||
|
||||
if ( count < pt_start )
|
||||
{
|
||||
pgt_updates->ptr = (unsigned long)vl1e;
|
||||
pgt_updates->val = (page_array[count] << PAGE_SHIFT) | L1_PROT;
|
||||
pgt_updates++;
|
||||
num_pgt_updates++;
|
||||
vl1e++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pgt_updates->ptr = (unsigned long)vl1e;
|
||||
pgt_updates->val =
|
||||
((page_array[count] << PAGE_SHIFT) | L1_PROT) & ~_PAGE_RW;
|
||||
pgt_updates++;
|
||||
num_pgt_updates++;
|
||||
vl1e++;
|
||||
}
|
||||
|
||||
pgt_updates->ptr =
|
||||
(page_array[count] << PAGE_SHIFT) | MMU_MACHPHYS_UPDATE;
|
||||
pgt_updates->val = count;
|
||||
pgt_updates++;
|
||||
num_pgt_updates++;
|
||||
}
|
||||
|
||||
*virt_startinfo_addr =
|
||||
virt_load_addr + ((alloc_index-1) << PAGE_SHIFT);
|
||||
|
||||
start_info = map_pfn(page_array[alloc_index-1]);
|
||||
memset(start_info, 0, sizeof(*start_info));
|
||||
start_info->pt_base = virt_load_addr + ((tot_pages-1) << PAGE_SHIFT);
|
||||
start_info->mod_start = initrd_addr;
|
||||
start_info->mod_len = initrd_len;
|
||||
start_info->nr_pages = tot_pages;
|
||||
start_info->shared_info = shared_info_frame << PAGE_SHIFT;
|
||||
start_info->dom_id = dom;
|
||||
start_info->flags = 0;
|
||||
strncpy(start_info->cmd_line, cmdline, MAX_CMD_LEN);
|
||||
start_info->cmd_line[MAX_CMD_LEN-1] = '\0';
|
||||
|
||||
unmap_pfn(start_info);
|
||||
|
||||
/* shared_info page starts its life empty. */
|
||||
shared_info = map_pfn(shared_info_frame);
|
||||
memset(shared_info, 0, PAGE_SIZE);
|
||||
unmap_pfn(shared_info);
|
||||
|
||||
/* Send the page update requests down to the hypervisor. */
|
||||
if ( send_pgupdates(pgt_update_arr, num_pgt_updates) < 0 )
|
||||
goto error_out;
|
||||
|
||||
free(page_array);
|
||||
free(pgt_update_arr);
|
||||
return 0;
|
||||
|
||||
error_out:
|
||||
if ( page_array == NULL )
|
||||
free(page_array);
|
||||
if ( pgt_update_arr == NULL )
|
||||
free(pgt_update_arr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int xi_domain_build(unsigned int domid,
|
||||
const char *image_name,
|
||||
const char *ramdisk_name,
|
||||
const char *cmdline,
|
||||
int verbose)
|
||||
{
|
||||
dom0_op_t launch_op, op;
|
||||
unsigned long load_addr;
|
||||
long tot_pages;
|
||||
int kernel_fd, initrd_fd = -1;
|
||||
gzFile kernel_gfd;
|
||||
int rc, i;
|
||||
full_execution_context_t *ctxt;
|
||||
unsigned long virt_startinfo_addr;
|
||||
|
||||
if ( (tot_pages = get_tot_pages(domid)) < 0 )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Could not find total pages for domain");
|
||||
return 1;
|
||||
}
|
||||
|
||||
kernel_fd = open(image_name, O_RDONLY);
|
||||
if ( kernel_fd < 0 )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Could not open kernel image");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( (kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Could not allocate decompression state for state file");
|
||||
close(kernel_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = read_kernel_header(kernel_gfd,
|
||||
tot_pages << (PAGE_SHIFT - 10),
|
||||
&load_addr, verbose);
|
||||
if ( rc < 0 )
|
||||
goto error_out;
|
||||
|
||||
if ( (load_addr & (PAGE_SIZE-1)) != 0 )
|
||||
{
|
||||
if ( verbose )
|
||||
ERROR("We can only deal with page-aligned load addresses");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ( (load_addr + (tot_pages << PAGE_SHIFT)) > HYPERVISOR_VIRT_START )
|
||||
{
|
||||
if ( verbose )
|
||||
ERROR("Cannot map all domain memory without hitting Xen space");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ( ramdisk_name != NULL )
|
||||
{
|
||||
initrd_fd = open(ramdisk_name, O_RDONLY);
|
||||
if ( initrd_fd < 0 )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Could not open the initial ramdisk image");
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
op.cmd = DOM0_GETDOMAININFO;
|
||||
op.u.getdomaininfo.domain = domid;
|
||||
if ( (do_dom0_op(&op) < 0) || (op.u.getdomaininfo.domain != domid) )
|
||||
{
|
||||
if ( verbose )
|
||||
PERROR("Could not get info on domain");
|
||||
goto error_out;
|
||||
}
|
||||
if ( (op.u.getdomaininfo.state != DOMSTATE_STOPPED) ||
|
||||
(op.u.getdomaininfo.ctxt.pt_base != 0) )
|
||||
{
|
||||
if ( verbose )
|
||||
ERROR("Domain is already constructed");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ( setup_guestos(domid, kernel_gfd, initrd_fd, tot_pages,
|
||||
&virt_startinfo_addr,
|
||||
load_addr, &launch_op.u.builddomain, cmdline,
|
||||
op.u.getdomaininfo.shared_info_frame, verbose) < 0 )
|
||||
{
|
||||
if ( verbose )
|
||||
ERROR("Error constructing guest OS");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ( initrd_fd >= 0 )
|
||||
close(initrd_fd);
|
||||
gzclose(kernel_gfd);
|
||||
|
||||
ctxt = &launch_op.u.builddomain.ctxt;
|
||||
|
||||
ctxt->flags = 0;
|
||||
|
||||
/*
|
||||
* Initial register values:
|
||||
* DS,ES,FS,GS = FLAT_RING1_DS
|
||||
* CS:EIP = FLAT_RING1_CS:start_pc
|
||||
* SS:ESP = FLAT_RING1_DS:start_stack
|
||||
* ESI = start_info
|
||||
* [EAX,EBX,ECX,EDX,EDI,EBP are zero]
|
||||
* EFLAGS = IF | 2 (bit 1 is reserved and should always be 1)
|
||||
*/
|
||||
ctxt->i386_ctxt.ds = FLAT_RING1_DS;
|
||||
ctxt->i386_ctxt.es = FLAT_RING1_DS;
|
||||
ctxt->i386_ctxt.fs = FLAT_RING1_DS;
|
||||
ctxt->i386_ctxt.gs = FLAT_RING1_DS;
|
||||
ctxt->i386_ctxt.ss = FLAT_RING1_DS;
|
||||
ctxt->i386_ctxt.cs = FLAT_RING1_CS;
|
||||
ctxt->i386_ctxt.eip = load_addr;
|
||||
ctxt->i386_ctxt.esp = virt_startinfo_addr;
|
||||
ctxt->i386_ctxt.esi = virt_startinfo_addr;
|
||||
ctxt->i386_ctxt.eflags = (1<<9) | (1<<2);
|
||||
|
||||
/* FPU is set up to default initial state. */
|
||||
memset(ctxt->i387_ctxt, 0, sizeof(ctxt->i387_ctxt));
|
||||
|
||||
/* Virtual IDT is empty at start-of-day. */
|
||||
for ( i = 0; i < 256; i++ )
|
||||
{
|
||||
ctxt->trap_ctxt[i].vector = i;
|
||||
ctxt->trap_ctxt[i].cs = FLAT_RING1_CS;
|
||||
}
|
||||
ctxt->fast_trap_idx = 0;
|
||||
|
||||
/* No LDT. */
|
||||
ctxt->ldt_ents = 0;
|
||||
|
||||
/* Use the default Xen-provided GDT. */
|
||||
ctxt->gdt_ents = 0;
|
||||
|
||||
/* Ring 1 stack is the initial stack. */
|
||||
ctxt->ring1_ss = FLAT_RING1_DS;
|
||||
ctxt->ring1_esp = virt_startinfo_addr;
|
||||
|
||||
/* No debugging. */
|
||||
memset(ctxt->debugreg, 0, sizeof(ctxt->debugreg));
|
||||
|
||||
/* No callback handlers. */
|
||||
ctxt->event_callback_cs = FLAT_RING1_CS;
|
||||
ctxt->event_callback_eip = 0;
|
||||
ctxt->failsafe_callback_cs = FLAT_RING1_CS;
|
||||
ctxt->failsafe_callback_eip = 0;
|
||||
|
||||
launch_op.u.builddomain.domain = domid;
|
||||
launch_op.u.builddomain.num_vifs = 1;
|
||||
|
||||
launch_op.cmd = DOM0_BUILDDOMAIN;
|
||||
rc = do_dom0_op(&launch_op);
|
||||
|
||||
return rc;
|
||||
|
||||
error_out:
|
||||
if ( initrd_fd >= 0 )
|
||||
close(initrd_fd);
|
||||
gzclose(kernel_gfd);
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
/******************************************************************************
|
||||
* libxi_linux_restore.c
|
||||
*
|
||||
* Restore the state of a Xenolinux session.
|
||||
*
|
||||
* Copyright (c) 2003, K A Fraser.
|
||||
*/
|
||||
|
||||
#include "libxi_private.h"
|
||||
#include <asm-xeno/suspend.h>
|
||||
#include <zlib.h>
|
||||
|
||||
/* This may allow us to create a 'quiet' command-line option, if necessary. */
|
||||
#define verbose_printf(_f, _a...) \
|
||||
do { \
|
||||
if ( !verbose ) break; \
|
||||
printf( _f , ## _a ); \
|
||||
fflush(stdout); \
|
||||
} while ( 0 )
|
||||
|
||||
static int get_pfn_list(
|
||||
int domain_id, unsigned long *pfn_buf, unsigned long max_pfns)
|
||||
{
|
||||
dom0_op_t op;
|
||||
int ret;
|
||||
op.cmd = DOM0_GETMEMLIST;
|
||||
op.u.getmemlist.domain = domain_id;
|
||||
op.u.getmemlist.max_pfns = max_pfns;
|
||||
op.u.getmemlist.buffer = pfn_buf;
|
||||
|
||||
if ( mlock(pfn_buf, max_pfns * sizeof(unsigned long)) != 0 )
|
||||
{
|
||||
PERROR("Could not lock pfn list buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = do_dom0_op(&op);
|
||||
|
||||
(void)munlock(pfn_buf, max_pfns * sizeof(unsigned long));
|
||||
|
||||
return (ret < 0) ? -1 : op.u.getmemlist.num_pfns;
|
||||
}
|
||||
|
||||
#define MAX_MMU_UPDATES 1024
|
||||
|
||||
static int flush_mmu_updates(mmu_update_t *mmu_updates,
|
||||
int *mmu_update_idx)
|
||||
{
|
||||
int err = 0;
|
||||
privcmd_hypercall_t hypercall;
|
||||
|
||||
if ( *mmu_update_idx == 0 )
|
||||
return 0;
|
||||
|
||||
hypercall.op = __HYPERVISOR_mmu_update;
|
||||
hypercall.arg[0] = (unsigned long)mmu_updates;
|
||||
hypercall.arg[1] = (unsigned long)*mmu_update_idx;
|
||||
|
||||
if ( mlock(mmu_updates, sizeof(mmu_updates)) != 0 )
|
||||
{
|
||||
PERROR("Could not lock pagetable update array");
|
||||
err = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( do_xen_hypercall(&hypercall) < 0 )
|
||||
{
|
||||
ERROR("Failure when submitting mmu updates");
|
||||
err = 1;
|
||||
}
|
||||
|
||||
*mmu_update_idx = 0;
|
||||
|
||||
(void)munlock(mmu_updates, sizeof(mmu_updates));
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int add_mmu_update(mmu_update_t *mmu_updates,
|
||||
int *mmu_update_idx,
|
||||
unsigned long ptr,
|
||||
unsigned long val)
|
||||
{
|
||||
mmu_updates[*mmu_update_idx].ptr = ptr;
|
||||
mmu_updates[*mmu_update_idx].val = val;
|
||||
if ( ++*mmu_update_idx == MAX_MMU_UPDATES )
|
||||
return flush_mmu_updates(mmu_updates, mmu_update_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checked_read(gzFile fd, void *buf, size_t count)
|
||||
{
|
||||
int rc;
|
||||
while ( ((rc = gzread(fd, buf, count)) == -1) && (errno == EINTR) )
|
||||
continue;
|
||||
return rc == count;
|
||||
}
|
||||
|
||||
int xi_linux_restore(const char *state_file, int verbose)
|
||||
{
|
||||
dom0_op_t op;
|
||||
int rc = 1, i, j;
|
||||
unsigned long mfn, pfn, dom = 0;
|
||||
unsigned int prev_pc, this_pc;
|
||||
|
||||
/* Number of page frames in use by this XenoLinux session. */
|
||||
unsigned long nr_pfns;
|
||||
|
||||
/* The new domain's shared-info frame number. */
|
||||
unsigned long shared_info_frame;
|
||||
unsigned char shared_info[PAGE_SIZE]; /* saved contents from file */
|
||||
|
||||
/* A copy of the CPU context of the guest. */
|
||||
full_execution_context_t ctxt;
|
||||
|
||||
/* First 16 bytes of the state file must contain 'XenoLinuxSuspend'. */
|
||||
char signature[16];
|
||||
|
||||
/* A copy of the domain's name. */
|
||||
char name[MAX_DOMAIN_NAME];
|
||||
|
||||
/* A table containg the type of each PFN (/not/ MFN!). */
|
||||
unsigned long *pfn_type = NULL;
|
||||
|
||||
/* A temporary mapping, and a copy, of one frame of guest memory. */
|
||||
unsigned long *ppage, page[1024];
|
||||
|
||||
/* A copy of the pfn-to-mfn table frame list. */
|
||||
unsigned long pfn_to_mfn_frame_list[1024];
|
||||
|
||||
/* A table mapping each PFN to its new MFN. */
|
||||
unsigned long *pfn_to_mfn_table = NULL;
|
||||
|
||||
/* A temporary mapping of the guest's suspend record. */
|
||||
suspend_record_t *p_srec;
|
||||
|
||||
/* The name and descriptor of the file that we are reading from. */
|
||||
int fd;
|
||||
gzFile gfd;
|
||||
|
||||
mmu_update_t mmu_updates[MAX_MMU_UPDATES];
|
||||
int mmu_update_idx = 0;
|
||||
|
||||
if ( (fd = open(state_file, O_RDONLY)) == -1 )
|
||||
{
|
||||
PERROR("Could not open state file for reading");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( (gfd = gzdopen(fd, "rb")) == NULL )
|
||||
{
|
||||
ERROR("Could not allocate decompression state for state file");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Start writing out the saved-domain record. */
|
||||
if ( !checked_read(gfd, signature, 16) ||
|
||||
(memcmp(signature, "XenoLinuxSuspend", 16) != 0) )
|
||||
{
|
||||
ERROR("Unrecognised state format -- no signature found");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( !checked_read(gfd, name, sizeof(name)) ||
|
||||
!checked_read(gfd, &nr_pfns, sizeof(unsigned long)) ||
|
||||
!checked_read(gfd, &ctxt, sizeof(ctxt)) ||
|
||||
!checked_read(gfd, shared_info, PAGE_SIZE) ||
|
||||
!checked_read(gfd, pfn_to_mfn_frame_list, PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when reading from state file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for ( i = 0; i < MAX_DOMAIN_NAME; i++ )
|
||||
{
|
||||
if ( name[i] == '\0' ) break;
|
||||
if ( name[i] & 0x80 )
|
||||
{
|
||||
ERROR("Random characters in domain name");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
name[MAX_DOMAIN_NAME-1] = '\0';
|
||||
|
||||
if ( nr_pfns > 1024*1024 )
|
||||
{
|
||||
ERROR("Invalid state file -- pfn count out of range");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We want zeroed memory so use calloc rather than malloc. */
|
||||
pfn_to_mfn_table = calloc(1, 4 * nr_pfns);
|
||||
pfn_type = calloc(1, 4 * nr_pfns);
|
||||
|
||||
if ( (pfn_to_mfn_table == NULL) || (pfn_type == NULL) )
|
||||
{
|
||||
errno = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( !checked_read(gfd, pfn_type, 4 * nr_pfns) )
|
||||
{
|
||||
ERROR("Error when reading from state file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Create a new domain of the appropriate size, and find it's dom_id. */
|
||||
op.cmd = DOM0_CREATEDOMAIN;
|
||||
op.u.createdomain.memory_kb = nr_pfns * (PAGE_SIZE / 1024);
|
||||
memcpy(op.u.createdomain.name, name, MAX_DOMAIN_NAME);
|
||||
if ( do_dom0_op(&op) < 0 )
|
||||
{
|
||||
ERROR("Could not create new domain");
|
||||
goto out;
|
||||
}
|
||||
dom = op.u.createdomain.domain;
|
||||
|
||||
/* Get the domain's shared-info frame. */
|
||||
op.cmd = DOM0_GETDOMAININFO;
|
||||
op.u.getdomaininfo.domain = dom;
|
||||
if ( do_dom0_op(&op) < 0 )
|
||||
{
|
||||
ERROR("Could not get information on new domain");
|
||||
goto out;
|
||||
}
|
||||
shared_info_frame = op.u.getdomaininfo.shared_info_frame;
|
||||
|
||||
if ( init_pfn_mapper() < 0 )
|
||||
goto out;
|
||||
|
||||
/* Copy saved contents of shared-info page. No checking needed. */
|
||||
ppage = map_pfn(shared_info_frame);
|
||||
memcpy(ppage, shared_info, PAGE_SIZE);
|
||||
unmap_pfn(ppage);
|
||||
|
||||
/* Build the pfn-to-mfn table. We choose MFN ordering returned by Xen. */
|
||||
if ( get_pfn_list(dom, pfn_to_mfn_table, nr_pfns) != nr_pfns )
|
||||
{
|
||||
ERROR("Did not read correct number of frame numbers for new dom");
|
||||
goto out;
|
||||
}
|
||||
|
||||
verbose_printf("Reloading memory pages: 0%%");
|
||||
|
||||
/*
|
||||
* Now simply read each saved frame into its new machine frame.
|
||||
* We uncanonicalise page tables as we go.
|
||||
*/
|
||||
prev_pc = 0;
|
||||
for ( i = 0; i < nr_pfns; i++ )
|
||||
{
|
||||
this_pc = (i * 100) / nr_pfns;
|
||||
if ( (this_pc - prev_pc) >= 5 )
|
||||
{
|
||||
verbose_printf("\b\b\b\b%3d%%", this_pc);
|
||||
prev_pc = this_pc;
|
||||
}
|
||||
|
||||
mfn = pfn_to_mfn_table[i];
|
||||
|
||||
if ( !checked_read(gfd, page, PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when reading from state file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ppage = map_pfn(mfn);
|
||||
switch ( pfn_type[i] )
|
||||
{
|
||||
case L1TAB:
|
||||
memset(ppage, 0, PAGE_SIZE);
|
||||
if ( add_mmu_update(mmu_updates, &mmu_update_idx,
|
||||
(mfn<<PAGE_SHIFT) | MMU_EXTENDED_COMMAND,
|
||||
MMUEXT_PIN_L1_TABLE) )
|
||||
goto out;
|
||||
for ( j = 0; j < 1024; j++ )
|
||||
{
|
||||
if ( page[j] & _PAGE_PRESENT )
|
||||
{
|
||||
if ( (pfn = page[j] >> PAGE_SHIFT) >= nr_pfns )
|
||||
{
|
||||
ERROR("Frame number in page table is out of range");
|
||||
goto out;
|
||||
}
|
||||
if ( (pfn_type[pfn] != NONE) && (page[j] & _PAGE_RW) )
|
||||
{
|
||||
ERROR("Write access requested for a restricted frame");
|
||||
goto out;
|
||||
}
|
||||
page[j] &= (PAGE_SIZE - 1) & ~(_PAGE_GLOBAL | _PAGE_PAT);
|
||||
page[j] |= pfn_to_mfn_table[pfn] << PAGE_SHIFT;
|
||||
}
|
||||
if ( add_mmu_update(mmu_updates, &mmu_update_idx,
|
||||
(unsigned long)&ppage[j], page[j]) )
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case L2TAB:
|
||||
memset(ppage, 0, PAGE_SIZE);
|
||||
if ( add_mmu_update(mmu_updates, &mmu_update_idx,
|
||||
(mfn<<PAGE_SHIFT) | MMU_EXTENDED_COMMAND,
|
||||
MMUEXT_PIN_L2_TABLE) )
|
||||
goto out;
|
||||
for ( j = 0; j < (HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT); j++ )
|
||||
{
|
||||
if ( page[j] & _PAGE_PRESENT )
|
||||
{
|
||||
if ( (pfn = page[j] >> PAGE_SHIFT) >= nr_pfns )
|
||||
{
|
||||
ERROR("Frame number in page table is out of range");
|
||||
goto out;
|
||||
}
|
||||
if ( pfn_type[pfn] != L1TAB )
|
||||
{
|
||||
ERROR("Page table mistyping");
|
||||
goto out;
|
||||
}
|
||||
/* Haven't reached the L1 table yet. Ensure it is safe! */
|
||||
if ( pfn > i )
|
||||
{
|
||||
unsigned long **l1 = map_pfn(pfn_to_mfn_table[pfn]);
|
||||
memset(l1, 0, PAGE_SIZE);
|
||||
unmap_pfn(l1);
|
||||
}
|
||||
page[j] &= (PAGE_SIZE - 1) & ~(_PAGE_GLOBAL | _PAGE_PSE);
|
||||
page[j] |= pfn_to_mfn_table[pfn] << PAGE_SHIFT;
|
||||
}
|
||||
if ( add_mmu_update(mmu_updates, &mmu_update_idx,
|
||||
(unsigned long)&ppage[j], page[j]) )
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
memcpy(ppage, page, PAGE_SIZE);
|
||||
break;
|
||||
}
|
||||
/* NB. Must flush before unmapping page, as pass VAs to Xen. */
|
||||
if ( flush_mmu_updates(mmu_updates, &mmu_update_idx) )
|
||||
goto out;
|
||||
unmap_pfn(ppage);
|
||||
|
||||
if ( add_mmu_update(mmu_updates, &mmu_update_idx,
|
||||
(mfn<<PAGE_SHIFT) | MMU_MACHPHYS_UPDATE, i) )
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( flush_mmu_updates(mmu_updates, &mmu_update_idx) )
|
||||
goto out;
|
||||
|
||||
verbose_printf("\b\b\b\b100%%\nMemory reloaded.\n");
|
||||
|
||||
/* Uncanonicalise the suspend-record frame number and poke resume rec. */
|
||||
pfn = ctxt.i386_ctxt.esi;
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NONE) )
|
||||
{
|
||||
ERROR("Suspend record frame number is bad");
|
||||
goto out;
|
||||
}
|
||||
ctxt.i386_ctxt.esi = mfn = pfn_to_mfn_table[pfn];
|
||||
p_srec = map_pfn(mfn);
|
||||
p_srec->resume_info.nr_pages = nr_pfns;
|
||||
p_srec->resume_info.shared_info = shared_info_frame << PAGE_SHIFT;
|
||||
p_srec->resume_info.dom_id = dom;
|
||||
p_srec->resume_info.flags = 0;
|
||||
unmap_pfn(p_srec);
|
||||
|
||||
/* Uncanonicalise each GDT frame number. */
|
||||
if ( ctxt.gdt_ents > 8192 )
|
||||
{
|
||||
ERROR("GDT entry count out of range");
|
||||
goto out;
|
||||
}
|
||||
for ( i = 0; i < ctxt.gdt_ents; i += 512 )
|
||||
{
|
||||
pfn = ctxt.gdt_frames[i];
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NONE) )
|
||||
{
|
||||
ERROR("GDT frame number is bad");
|
||||
goto out;
|
||||
}
|
||||
ctxt.gdt_frames[i] = pfn_to_mfn_table[pfn];
|
||||
}
|
||||
|
||||
/* Uncanonicalise the page table base pointer. */
|
||||
pfn = ctxt.pt_base >> PAGE_SHIFT;
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != L2TAB) )
|
||||
{
|
||||
ERROR("PT base is bad");
|
||||
goto out;
|
||||
}
|
||||
ctxt.pt_base = pfn_to_mfn_table[pfn] << PAGE_SHIFT;
|
||||
|
||||
/* Uncanonicalise the pfn-to-mfn table frame-number list. */
|
||||
for ( i = 0; i < nr_pfns; i += 1024 )
|
||||
{
|
||||
unsigned long copy_size = (nr_pfns - i) * sizeof(unsigned long);
|
||||
if ( copy_size > PAGE_SIZE ) copy_size = PAGE_SIZE;
|
||||
pfn = pfn_to_mfn_frame_list[i/1024];
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NONE) )
|
||||
{
|
||||
ERROR("PFN-to-MFN frame number is bad");
|
||||
goto out;
|
||||
}
|
||||
ppage = map_pfn(pfn_to_mfn_table[pfn]);
|
||||
memcpy(ppage, &pfn_to_mfn_table[i], copy_size);
|
||||
unmap_pfn(ppage);
|
||||
}
|
||||
|
||||
/*
|
||||
* Safety checking of saved context:
|
||||
* 1. i386_ctxt is fine, as Xen checks that on context switch.
|
||||
* 2. i387_ctxt is fine, as it can't hurt Xen.
|
||||
* 3. trap_ctxt needs the code selectors checked.
|
||||
* 4. fast_trap_idx is checked by Xen.
|
||||
* 5. ldt base must be page-aligned, no more than 8192 ents, ...
|
||||
* 6. gdt already done, and further checking is done by Xen.
|
||||
* 7. check that ring1_ss is safe.
|
||||
* 8. pt_base is already done.
|
||||
* 9. debugregs are checked by Xen.
|
||||
* 10. callback code selectors need checking.
|
||||
*/
|
||||
for ( i = 0; i < 256; i++ )
|
||||
{
|
||||
ctxt.trap_ctxt[i].vector = i;
|
||||
if ( (ctxt.trap_ctxt[i].cs & 3) == 0 )
|
||||
ctxt.trap_ctxt[i].cs = FLAT_RING1_CS;
|
||||
}
|
||||
if ( (ctxt.ring1_ss & 3) == 0 )
|
||||
ctxt.ring1_ss = FLAT_RING1_DS;
|
||||
if ( (ctxt.event_callback_cs & 3) == 0 )
|
||||
ctxt.event_callback_cs = FLAT_RING1_CS;
|
||||
if ( (ctxt.failsafe_callback_cs & 3) == 0 )
|
||||
ctxt.failsafe_callback_cs = FLAT_RING1_CS;
|
||||
if ( ((ctxt.ldt_base & (PAGE_SIZE - 1)) != 0) ||
|
||||
(ctxt.ldt_ents > 8192) ||
|
||||
(ctxt.ldt_base > HYPERVISOR_VIRT_START) ||
|
||||
((ctxt.ldt_base + ctxt.ldt_ents*8) > HYPERVISOR_VIRT_START) )
|
||||
{
|
||||
ERROR("Bad LDT base or size");
|
||||
goto out;
|
||||
}
|
||||
|
||||
op.cmd = DOM0_BUILDDOMAIN;
|
||||
op.u.builddomain.domain = dom;
|
||||
op.u.builddomain.num_vifs = 1;
|
||||
memcpy(&op.u.builddomain.ctxt, &ctxt, sizeof(ctxt));
|
||||
rc = do_dom0_op(&op);
|
||||
|
||||
out:
|
||||
if ( rc != 0 )
|
||||
{
|
||||
if ( dom != 0 )
|
||||
{
|
||||
op.cmd = DOM0_DESTROYDOMAIN;
|
||||
op.u.destroydomain.domain = dom;
|
||||
op.u.destroydomain.force = 1;
|
||||
(void)do_dom0_op(&op);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success: print the domain id. */
|
||||
verbose_printf("DOM=%ld\n", dom);
|
||||
}
|
||||
|
||||
if ( pfn_to_mfn_table != NULL )
|
||||
free(pfn_to_mfn_table);
|
||||
if ( pfn_type != NULL )
|
||||
free(pfn_type);
|
||||
|
||||
gzclose(gfd);
|
||||
|
||||
return (rc == 0) ? dom : rc;
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
/******************************************************************************
|
||||
* libxi_linux_save.c
|
||||
*
|
||||
* Save the state of a running Xenolinux session.
|
||||
*
|
||||
* Copyright (c) 2003, K A Fraser.
|
||||
*/
|
||||
|
||||
#include "libxi_private.h"
|
||||
#include <asm-xeno/suspend.h>
|
||||
#include <zlib.h>
|
||||
|
||||
/* This may allow us to create a 'quiet' command-line option, if necessary. */
|
||||
#define verbose_printf(_f, _a...) \
|
||||
do { \
|
||||
if ( !verbose ) break; \
|
||||
printf( _f , ## _a ); \
|
||||
fflush(stdout); \
|
||||
} while ( 0 )
|
||||
|
||||
/*
|
||||
* Returns TRUE if the given machine frame number has a unique mapping
|
||||
* in the guest's pseudophysical map.
|
||||
*/
|
||||
#define MFN_IS_IN_PSEUDOPHYS_MAP(_mfn) \
|
||||
(((_mfn) < (1024*1024)) && \
|
||||
(pfn_to_mfn_table[mfn_to_pfn_table[_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_table[mfn]; \
|
||||
_res; \
|
||||
})
|
||||
|
||||
static int check_pfn_ownership(unsigned long mfn, unsigned int dom)
|
||||
{
|
||||
dom0_op_t op;
|
||||
op.cmd = DOM0_GETPAGEFRAMEINFO;
|
||||
op.u.getpageframeinfo.pfn = mfn;
|
||||
if ( (do_dom0_op(&op) < 0) || (op.u.getpageframeinfo.domain != dom) )
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define GETPFN_ERR (~0U)
|
||||
static unsigned int get_pfn_type(unsigned long mfn)
|
||||
{
|
||||
dom0_op_t op;
|
||||
op.cmd = DOM0_GETPAGEFRAMEINFO;
|
||||
op.u.getpageframeinfo.pfn = mfn;
|
||||
if ( do_dom0_op(&op) < 0 )
|
||||
{
|
||||
PERROR("Unexpected failure when getting page frame info!");
|
||||
return GETPFN_ERR;
|
||||
}
|
||||
return op.u.getpageframeinfo.type;
|
||||
}
|
||||
|
||||
static int checked_write(gzFile fd, void *buf, size_t count)
|
||||
{
|
||||
int rc;
|
||||
while ( ((rc = gzwrite(fd, buf, count)) == -1) && (errno = EINTR) )
|
||||
continue;
|
||||
return rc == count;
|
||||
}
|
||||
|
||||
int xi_linux_save(unsigned int domid, const char *state_file, int verbose)
|
||||
{
|
||||
dom0_op_t op;
|
||||
int rc = 1, i, j;
|
||||
unsigned long mfn;
|
||||
unsigned int prev_pc, this_pc;
|
||||
|
||||
/* Remember if we stopped the guest, so we can restart it on exit. */
|
||||
int we_stopped_it = 0;
|
||||
|
||||
/* The new domain's shared-info frame number. */
|
||||
unsigned long shared_info_frame;
|
||||
|
||||
/* A copy of the CPU context of the guest. */
|
||||
full_execution_context_t ctxt;
|
||||
|
||||
/* A copy of the domain's name. */
|
||||
char name[MAX_DOMAIN_NAME];
|
||||
|
||||
/* A table containg the type of each PFN (/not/ MFN!). */
|
||||
unsigned long *pfn_type = NULL;
|
||||
|
||||
/* A temporary mapping, and a copy, of one frame of guest memory. */
|
||||
unsigned long *ppage, page[1024];
|
||||
|
||||
/* A temporary mapping, and a copy, of the pfn-to-mfn table frame list. */
|
||||
unsigned long *p_pfn_to_mfn_frame_list, pfn_to_mfn_frame_list[1024];
|
||||
/* A temporary mapping of one frame in the above list. */
|
||||
unsigned long *pfn_to_mfn_frame;
|
||||
|
||||
/* A table mapping each PFN to its current MFN. */
|
||||
unsigned long *pfn_to_mfn_table = NULL;
|
||||
/* A table mapping each current MFN to its canonical PFN. */
|
||||
unsigned long *mfn_to_pfn_table = NULL;
|
||||
|
||||
/* A temporary mapping, and a copy, of the guest's suspend record. */
|
||||
suspend_record_t *p_srec, srec;
|
||||
|
||||
/* The name and descriptor of the file that we are writing to. */
|
||||
int fd;
|
||||
gzFile gfd;
|
||||
|
||||
if ( (fd = open(state_file, O_CREAT|O_EXCL|O_WRONLY, 0644)) == -1 )
|
||||
{
|
||||
PERROR("Could not open file for writing");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compression rate 1: we want speed over compression. We're mainly going
|
||||
* for those zero pages, after all.
|
||||
*/
|
||||
if ( (gfd = gzdopen(fd, "wb1")) == NULL )
|
||||
{
|
||||
ERROR("Could not allocate compression state for state file");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Ensure that the domain exists, and that it is stopped. */
|
||||
for ( ; ; )
|
||||
{
|
||||
op.cmd = DOM0_GETDOMAININFO;
|
||||
op.u.getdomaininfo.domain = domid;
|
||||
if ( (do_dom0_op(&op) < 0) || (op.u.getdomaininfo.domain != domid) )
|
||||
{
|
||||
PERROR("Could not get info on domain");
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&ctxt, &op.u.getdomaininfo.ctxt, sizeof(ctxt));
|
||||
memcpy(name, op.u.getdomaininfo.name, sizeof(name));
|
||||
shared_info_frame = op.u.getdomaininfo.shared_info_frame;
|
||||
|
||||
if ( op.u.getdomaininfo.state == DOMSTATE_STOPPED )
|
||||
break;
|
||||
|
||||
we_stopped_it = 1;
|
||||
|
||||
op.cmd = DOM0_STOPDOMAIN;
|
||||
op.u.stopdomain.domain = domid;
|
||||
(void)do_dom0_op(&op);
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
/* A cheesy test to see whether the domain contains valid state. */
|
||||
if ( ctxt.pt_base == 0 )
|
||||
{
|
||||
ERROR("Domain is not in a valid Xenolinux state");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( init_pfn_mapper() < 0 )
|
||||
goto out;
|
||||
|
||||
/* Is the suspend-record MFN actually valid for this domain? */
|
||||
if ( !check_pfn_ownership(ctxt.i386_ctxt.esi, domid) )
|
||||
{
|
||||
ERROR("Invalid state record pointer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If the suspend-record MFN is okay then grab a copy of it to @srec. */
|
||||
p_srec = map_pfn(ctxt.i386_ctxt.esi);
|
||||
memcpy(&srec, p_srec, sizeof(srec));
|
||||
unmap_pfn(p_srec);
|
||||
|
||||
if ( srec.nr_pfns > 1024*1024 )
|
||||
{
|
||||
ERROR("Invalid state record -- pfn count out of range");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( !check_pfn_ownership(srec.pfn_to_mfn_frame_list, domid) )
|
||||
{
|
||||
ERROR("Invalid pfn-to-mfn frame list pointer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Grab a copy of the pfn-to-mfn table frame list. */
|
||||
p_pfn_to_mfn_frame_list = map_pfn(srec.pfn_to_mfn_frame_list);
|
||||
memcpy(pfn_to_mfn_frame_list, p_pfn_to_mfn_frame_list, PAGE_SIZE);
|
||||
unmap_pfn(p_pfn_to_mfn_frame_list);
|
||||
|
||||
/* We want zeroed memory so use calloc rather than malloc. */
|
||||
mfn_to_pfn_table = calloc(1, 4 * 1024 * 1024);
|
||||
pfn_to_mfn_table = calloc(1, 4 * srec.nr_pfns);
|
||||
pfn_type = calloc(1, 4 * srec.nr_pfns);
|
||||
|
||||
if ( (mfn_to_pfn_table == NULL) ||
|
||||
(pfn_to_mfn_table == NULL) ||
|
||||
(pfn_type == NULL) )
|
||||
{
|
||||
errno = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct the local pfn-to-mfn and mfn-to-pfn tables. On exit from this
|
||||
* loop we have each MFN mapped at most once. Note that there may be MFNs
|
||||
* that aren't mapped at all: we detect these by MFN_IS_IN_PSEUDOPHYS_MAP.
|
||||
*/
|
||||
pfn_to_mfn_frame = NULL;
|
||||
for ( i = 0; i < srec.nr_pfns; i++ )
|
||||
{
|
||||
/* Each frameful of table frames must be checked & mapped on demand. */
|
||||
if ( (i & 1023) == 0 )
|
||||
{
|
||||
mfn = pfn_to_mfn_frame_list[i/1024];
|
||||
if ( !check_pfn_ownership(mfn, domid) )
|
||||
{
|
||||
ERROR("Invalid frame number if pfn-to-mfn frame list");
|
||||
goto out;
|
||||
}
|
||||
if ( pfn_to_mfn_frame != NULL )
|
||||
unmap_pfn(pfn_to_mfn_frame);
|
||||
pfn_to_mfn_frame = map_pfn(mfn);
|
||||
}
|
||||
|
||||
mfn = pfn_to_mfn_frame[i & 1023];
|
||||
|
||||
if ( !check_pfn_ownership(mfn, domid) )
|
||||
{
|
||||
ERROR("Invalid frame specified with pfn-to-mfn table");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Did we map this MFN already? That would be invalid! */
|
||||
if ( MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
|
||||
{
|
||||
ERROR("A machine frame appears twice in pseudophys space");
|
||||
goto out;
|
||||
}
|
||||
|
||||
pfn_to_mfn_table[i] = mfn;
|
||||
mfn_to_pfn_table[mfn] = i;
|
||||
|
||||
/* Query page type by MFN, but store it by PFN. */
|
||||
if ( (pfn_type[i] = get_pfn_type(mfn)) == GETPFN_ERR )
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Canonicalise the suspend-record frame number. */
|
||||
if ( !translate_mfn_to_pfn(&ctxt.i386_ctxt.esi) )
|
||||
{
|
||||
ERROR("State record is not in range of pseudophys map");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Canonicalise each GDT frame number. */
|
||||
for ( i = 0; i < ctxt.gdt_ents; i += 512 )
|
||||
{
|
||||
if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) )
|
||||
{
|
||||
ERROR("GDT frame is not in range of pseudophys map");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Canonicalise the page table base pointer. */
|
||||
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) )
|
||||
{
|
||||
ERROR("PT base is not in range of pseudophys map");
|
||||
goto out;
|
||||
}
|
||||
ctxt.pt_base = mfn_to_pfn_table[ctxt.pt_base >> PAGE_SHIFT] << PAGE_SHIFT;
|
||||
|
||||
/* Canonicalise the pfn-to-mfn table frame-number list. */
|
||||
for ( i = 0; i < srec.nr_pfns; i += 1024 )
|
||||
{
|
||||
if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) )
|
||||
{
|
||||
ERROR("Frame # in pfn-to-mfn frame list is not in pseudophys");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start writing out the saved-domain record. */
|
||||
ppage = map_pfn(shared_info_frame);
|
||||
if ( !checked_write(gfd, "XenoLinuxSuspend", 16) ||
|
||||
!checked_write(gfd, name, sizeof(name)) ||
|
||||
!checked_write(gfd, &srec.nr_pfns, sizeof(unsigned long)) ||
|
||||
!checked_write(gfd, &ctxt, sizeof(ctxt)) ||
|
||||
!checked_write(gfd, ppage, PAGE_SIZE) ||
|
||||
!checked_write(gfd, pfn_to_mfn_frame_list, PAGE_SIZE) ||
|
||||
!checked_write(gfd, pfn_type, 4 * srec.nr_pfns) )
|
||||
{
|
||||
ERROR("Error when writing to state file");
|
||||
goto out;
|
||||
}
|
||||
unmap_pfn(ppage);
|
||||
|
||||
verbose_printf("Saving memory pages: 0%%");
|
||||
|
||||
/* Now write out each data page, canonicalising page tables as we go... */
|
||||
prev_pc = 0;
|
||||
for ( i = 0; i < srec.nr_pfns; i++ )
|
||||
{
|
||||
this_pc = (i * 100) / srec.nr_pfns;
|
||||
if ( (this_pc - prev_pc) >= 5 )
|
||||
{
|
||||
verbose_printf("\b\b\b\b%3d%%", this_pc);
|
||||
prev_pc = this_pc;
|
||||
}
|
||||
|
||||
mfn = pfn_to_mfn_table[i];
|
||||
|
||||
ppage = map_pfn(mfn);
|
||||
memcpy(page, ppage, PAGE_SIZE);
|
||||
unmap_pfn(ppage);
|
||||
|
||||
if ( (pfn_type[i] == L1TAB) || (pfn_type[i] == L2TAB) )
|
||||
{
|
||||
for ( j = 0;
|
||||
j < ((pfn_type[i] == L2TAB) ?
|
||||
(HYPERVISOR_VIRT_START >> L2_PAGETABLE_SHIFT) : 1024);
|
||||
j++ )
|
||||
{
|
||||
if ( !(page[j] & _PAGE_PRESENT) ) continue;
|
||||
mfn = page[j] >> PAGE_SHIFT;
|
||||
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
|
||||
{
|
||||
ERROR("Frame number in pagetable page is invalid");
|
||||
goto out;
|
||||
}
|
||||
page[j] &= PAGE_SIZE - 1;
|
||||
page[j] |= mfn_to_pfn_table[mfn] << PAGE_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !checked_write(gfd, page, PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when writing to state file");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
verbose_printf("\b\b\b\b100%%\nMemory saved.\n");
|
||||
|
||||
/* Success! */
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
/* Restart the domain if we had to stop it to save its state. */
|
||||
if ( we_stopped_it )
|
||||
{
|
||||
op.cmd = DOM0_STARTDOMAIN;
|
||||
op.u.startdomain.domain = domid;
|
||||
(void)do_dom0_op(&op);
|
||||
}
|
||||
|
||||
gzclose(gfd);
|
||||
|
||||
if ( pfn_to_mfn_table != NULL )
|
||||
free(pfn_to_mfn_table);
|
||||
if ( mfn_to_pfn_table != NULL )
|
||||
free(mfn_to_pfn_table);
|
||||
if ( pfn_type != NULL )
|
||||
free(pfn_type);
|
||||
|
||||
/* On error, make sure the file is deleted. */
|
||||
if ( rc != 0 )
|
||||
unlink(state_file);
|
||||
|
||||
return !!rc;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/******************************************************************************
|
||||
* libxi_misc.c
|
||||
*
|
||||
* Miscellaneous control interface functions.
|
||||
*/
|
||||
|
||||
#include "libxi_private.h"
|
||||
|
||||
int privcmd_fd = -1;
|
||||
|
||||
int xi_interface_open(void)
|
||||
{
|
||||
if ( (privcmd_fd == -1) &&
|
||||
((privcmd_fd = open("/proc/xeno/privcmd", O_RDWR)) < 0) )
|
||||
{
|
||||
privcmd_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xi_interface_close(void)
|
||||
{
|
||||
if ( privcmd_fd != -1 )
|
||||
{
|
||||
close(privcmd_fd);
|
||||
privcmd_fd = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define CONSOLE_RING_CLEAR 1
|
||||
|
||||
int xi_readconsolering(char *str, unsigned int max_chars, int clear)
|
||||
{
|
||||
int ret;
|
||||
dom0_op_t op;
|
||||
|
||||
op.cmd = DOM0_READCONSOLE;
|
||||
op.u.readconsole.str = (unsigned long)str;
|
||||
op.u.readconsole.count = max_chars;
|
||||
op.u.readconsole.cmd = clear ? CONSOLE_RING_CLEAR : 0;
|
||||
|
||||
if ( (ret = do_dom0_op(&op)) > 0 )
|
||||
str[ret] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/******************************************************************************
|
||||
* libxi_private.c
|
||||
*
|
||||
* Helper functions for the rest of the library.
|
||||
*/
|
||||
|
||||
#include "libxi_private.h"
|
||||
|
||||
static int devmem_fd = -1;
|
||||
|
||||
int init_pfn_mapper(void)
|
||||
{
|
||||
if ( (devmem_fd == -1) &&
|
||||
((devmem_fd = open("/dev/mem", O_RDWR)) < 0) )
|
||||
{
|
||||
devmem_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *map_pfn(unsigned long pfn)
|
||||
{
|
||||
void *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, devmem_fd, pfn << PAGE_SHIFT);
|
||||
if ( vaddr == MAP_FAILED )
|
||||
return NULL;
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
void unmap_pfn(void *vaddr)
|
||||
{
|
||||
(void)munmap(vaddr, PAGE_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
|
||||
#ifndef __LIBXI_PRIVATE_H__
|
||||
#define __LIBXI_PRIVATE_H__
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned long u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef signed char s8;
|
||||
typedef signed short s16;
|
||||
typedef signed long s32;
|
||||
typedef signed long long s64;
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xi.h"
|
||||
|
||||
#include <asm-xeno/proc_cmd.h>
|
||||
#include <hypervisor-ifs/hypervisor-if.h>
|
||||
#include <hypervisor-ifs/dom0_ops.h>
|
||||
#include <hypervisor-ifs/vbd.h>
|
||||
|
||||
#define _PAGE_PRESENT 0x001
|
||||
#define _PAGE_RW 0x002
|
||||
#define _PAGE_USER 0x004
|
||||
#define _PAGE_PWT 0x008
|
||||
#define _PAGE_PCD 0x010
|
||||
#define _PAGE_ACCESSED 0x020
|
||||
#define _PAGE_DIRTY 0x040
|
||||
#define _PAGE_PAT 0x080
|
||||
#define _PAGE_PSE 0x080
|
||||
#define _PAGE_GLOBAL 0x100
|
||||
|
||||
|
||||
#define L1_PAGETABLE_SHIFT 12
|
||||
#define L2_PAGETABLE_SHIFT 22
|
||||
|
||||
#define ENTRIES_PER_L1_PAGETABLE 1024
|
||||
#define ENTRIES_PER_L2_PAGETABLE 1024
|
||||
|
||||
#define PAGE_SHIFT L1_PAGETABLE_SHIFT
|
||||
#define PAGE_SIZE (1UL << PAGE_SHIFT)
|
||||
#define PAGE_MASK (~(PAGE_SIZE-1))
|
||||
|
||||
typedef struct { unsigned long l1_lo; } l1_pgentry_t;
|
||||
typedef struct { unsigned long l2_lo; } l2_pgentry_t;
|
||||
|
||||
#define l1_table_offset(_a) \
|
||||
(((_a) >> L1_PAGETABLE_SHIFT) & (ENTRIES_PER_L1_PAGETABLE - 1))
|
||||
#define l2_table_offset(_a) \
|
||||
((_a) >> L2_PAGETABLE_SHIFT)
|
||||
|
||||
#define ERROR(_m) \
|
||||
fprintf(stderr, "ERROR: %s\n", (_m))
|
||||
|
||||
#define PERROR(_m) \
|
||||
fprintf(stderr, "ERROR: %s (%d = %s)\n", (_m), errno, strerror(errno))
|
||||
|
||||
extern int privcmd_fd;
|
||||
static inline int do_privcmd(unsigned int cmd, unsigned long data)
|
||||
{
|
||||
return ioctl(privcmd_fd, cmd, data);
|
||||
}
|
||||
|
||||
static inline int do_xen_hypercall(privcmd_hypercall_t *hypercall)
|
||||
{
|
||||
return do_privcmd(IOCTL_PRIVCMD_HYPERCALL, (unsigned long)hypercall);
|
||||
}
|
||||
|
||||
static inline int do_dom0_op(dom0_op_t *op)
|
||||
{
|
||||
int ret = -1;
|
||||
privcmd_hypercall_t hypercall;
|
||||
|
||||
op->interface_version = DOM0_INTERFACE_VERSION;
|
||||
|
||||
hypercall.op = __HYPERVISOR_dom0_op;
|
||||
hypercall.arg[0] = (unsigned long)op;
|
||||
|
||||
if ( mlock(op, sizeof(*op)) != 0 )
|
||||
goto out1;
|
||||
|
||||
if ( (ret = do_xen_hypercall(&hypercall)) < 0 )
|
||||
{
|
||||
if ( errno == EACCES )
|
||||
fprintf(stderr, "Dom0 operation failed -- need to"
|
||||
" rebuild the user-space tool set?\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out2: (void)munlock(op, sizeof(*op));
|
||||
out1: return ret;
|
||||
}
|
||||
|
||||
static inline int do_network_op(network_op_t *op)
|
||||
{
|
||||
int ret = -1;
|
||||
privcmd_hypercall_t hypercall;
|
||||
|
||||
hypercall.op = __HYPERVISOR_network_op;
|
||||
hypercall.arg[0] = (unsigned long)op;
|
||||
|
||||
if ( mlock(op, sizeof(*op)) != 0 )
|
||||
goto out1;
|
||||
|
||||
if ( (ret = do_xen_hypercall(&hypercall)) < 0 )
|
||||
goto out2;
|
||||
|
||||
ret = 0;
|
||||
|
||||
out2: (void)munlock(op, sizeof(*op));
|
||||
out1: return ret;
|
||||
}
|
||||
|
||||
|
||||
static inline int do_block_io_op(block_io_op_t *op)
|
||||
{
|
||||
int ret = -1;
|
||||
privcmd_hypercall_t hypercall;
|
||||
|
||||
hypercall.op = __HYPERVISOR_block_io_op;
|
||||
hypercall.arg[0] = (unsigned long)op;
|
||||
|
||||
if ( mlock(op, sizeof(*op)) != 0 )
|
||||
goto out1;
|
||||
|
||||
if ( do_xen_hypercall(&hypercall) < 0 )
|
||||
goto out2;
|
||||
|
||||
ret = 0;
|
||||
|
||||
out2: (void)munlock(op, sizeof(*op));
|
||||
out1: return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* PFN mapping.
|
||||
*/
|
||||
int init_pfn_mapper(void);
|
||||
void *map_pfn(unsigned long pfn);
|
||||
void unmap_pfn(void *vaddr);
|
||||
|
||||
#endif /* __LIBXI_PRIVATE_H__ */
|
||||
@@ -0,0 +1,116 @@
|
||||
/******************************************************************************
|
||||
* libxi_vbd.c
|
||||
*
|
||||
* API for manipulating and accessing per-domain virtual block devices.
|
||||
*
|
||||
* Copyright (c) 2003, K A Fraser.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "libxi_private.h"
|
||||
|
||||
int xi_vbd_create(unsigned int domid, unsigned short vbdid, int writeable)
|
||||
{
|
||||
block_io_op_t op;
|
||||
op.cmd = BLOCK_IO_OP_VBD_CREATE;
|
||||
op.u.create_params.domain = domid;
|
||||
op.u.create_params.vdevice = vbdid;
|
||||
op.u.create_params.mode = VBD_MODE_R | (writeable ? VBD_MODE_W : 0);
|
||||
return do_block_io_op(&op);
|
||||
}
|
||||
|
||||
|
||||
int xi_vbd_destroy(unsigned int domid, unsigned short vbdid)
|
||||
{
|
||||
block_io_op_t op;
|
||||
op.cmd = BLOCK_IO_OP_VBD_DELETE;
|
||||
op.u.delete_params.domain = domid;
|
||||
op.u.delete_params.vdevice = vbdid;
|
||||
return do_block_io_op(&op);
|
||||
}
|
||||
|
||||
|
||||
int xi_vbd_add_extent(unsigned int domid,
|
||||
unsigned short vbdid,
|
||||
unsigned short real_device,
|
||||
unsigned long start_sector,
|
||||
unsigned long nr_sectors)
|
||||
{
|
||||
block_io_op_t op;
|
||||
op.cmd = BLOCK_IO_OP_VBD_ADD;
|
||||
op.u.add_params.domain = domid;
|
||||
op.u.add_params.vdevice = vbdid;
|
||||
op.u.add_params.extent.device = real_device;
|
||||
op.u.add_params.extent.start_sector = start_sector;
|
||||
op.u.add_params.extent.nr_sectors = nr_sectors;
|
||||
return do_block_io_op(&op);
|
||||
}
|
||||
|
||||
|
||||
int xi_vbd_delete_extent(unsigned int domid,
|
||||
unsigned short vbdid,
|
||||
unsigned short real_device,
|
||||
unsigned long start_sector,
|
||||
unsigned long nr_sectors)
|
||||
{
|
||||
block_io_op_t op;
|
||||
op.cmd = BLOCK_IO_OP_VBD_REMOVE;
|
||||
op.u.add_params.domain = domid;
|
||||
op.u.add_params.vdevice = vbdid;
|
||||
op.u.add_params.extent.device = real_device;
|
||||
op.u.add_params.extent.start_sector = start_sector;
|
||||
op.u.add_params.extent.nr_sectors = nr_sectors;
|
||||
return do_block_io_op(&op);
|
||||
}
|
||||
|
||||
|
||||
int xi_vbd_probe(unsigned int domid,
|
||||
unsigned short vbdid,
|
||||
unsigned int max_vbds,
|
||||
xi_vbd_t *vbds)
|
||||
{
|
||||
block_io_op_t op;
|
||||
xen_disk_info_t *xdi = &op.u.probe_params.xdi;
|
||||
int i, j, ret, allocsz = max_vbds * sizeof(xen_disk_t);
|
||||
|
||||
op.cmd = BLOCK_IO_OP_VBD_PROBE;
|
||||
op.u.probe_params.domain = domid;
|
||||
|
||||
xdi->max = max_vbds;
|
||||
xdi->disks = malloc(allocsz);
|
||||
xdi->count = 0;
|
||||
|
||||
if ( (xdi->disks == NULL) || (mlock(xdi->disks, allocsz) != 0) )
|
||||
{
|
||||
if ( xdi->disks != NULL )
|
||||
free(xdi->disks);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = do_block_io_op(&op);
|
||||
|
||||
(void)munlock(xdi->disks, allocsz);
|
||||
|
||||
if ( ret >= 0 )
|
||||
{
|
||||
for ( i = 0, j = 0; i < xdi->count; i++ )
|
||||
{
|
||||
if ( !(xdi->disks[i].info & XD_FLAG_VIRT) )
|
||||
continue;
|
||||
|
||||
vbds[j].domid = xdi->disks[i].domain;
|
||||
vbds[j].vbdid = xdi->disks[i].device;
|
||||
vbds[j].flags = (xdi->disks[i].info & XD_FLAG_RO) ?
|
||||
0 : XI_VBDF_WRITEABLE;
|
||||
vbds[j].nr_sectors = xdi->disks[i].capacity;
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
ret = j;
|
||||
}
|
||||
|
||||
free(xdi->disks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/******************************************************************************
|
||||
* libxi_vif.c
|
||||
*
|
||||
* API for manipulating and accessing per-network-interface parameters.
|
||||
*
|
||||
* Copyright (c) 2003, K A Fraser.
|
||||
*/
|
||||
|
||||
#include "libxi_private.h"
|
||||
|
||||
int xi_vif_scheduler_set(unsigned int domid,
|
||||
unsigned int vifid,
|
||||
xi_vif_sched_params_t *params)
|
||||
{
|
||||
network_op_t netop;
|
||||
netop.cmd = NETWORK_OP_VIFSETPARAMS;
|
||||
netop.u.vif_setparams.domain = domid;
|
||||
netop.u.vif_setparams.vif = vifid;
|
||||
netop.u.vif_setparams.credit_bytes = params->credit_bytes;
|
||||
netop.u.vif_setparams.credit_usec = params->credit_usec;
|
||||
return do_network_op(&netop);
|
||||
}
|
||||
|
||||
|
||||
int xi_vif_scheduler_get(unsigned int domid,
|
||||
unsigned int vifid,
|
||||
xi_vif_sched_params_t *params)
|
||||
{
|
||||
network_op_t netop;
|
||||
int rc;
|
||||
|
||||
netop.cmd = NETWORK_OP_VIFGETINFO;
|
||||
netop.u.vif_getinfo.domain = domid;
|
||||
netop.u.vif_getinfo.vif = vifid;
|
||||
|
||||
if ( (rc = do_network_op(&netop)) >= 0 )
|
||||
{
|
||||
params->credit_bytes = netop.u.vif_getinfo.credit_bytes;
|
||||
params->credit_usec = netop.u.vif_getinfo.credit_usec;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int xi_vif_stats_get(unsigned int domid,
|
||||
unsigned int vifid,
|
||||
xi_vif_stats_t *stats)
|
||||
{
|
||||
network_op_t netop;
|
||||
int rc;
|
||||
|
||||
netop.cmd = NETWORK_OP_VIFGETINFO;
|
||||
netop.u.vif_getinfo.domain = domid;
|
||||
netop.u.vif_getinfo.vif = vifid;
|
||||
|
||||
if ( (rc = do_network_op(&netop)) >= 0 )
|
||||
{
|
||||
stats->tx_bytes = netop.u.vif_getinfo.total_bytes_sent;
|
||||
stats->tx_pkts = netop.u.vif_getinfo.total_packets_sent;
|
||||
stats->rx_bytes = netop.u.vif_getinfo.total_bytes_received;
|
||||
stats->rx_pkts = netop.u.vif_getinfo.total_packets_received;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
Summary: Xen control interface library
|
||||
Name: xen-internal-library
|
||||
Version: 1.2
|
||||
Release: 1
|
||||
License: Xen
|
||||
Group: Xen
|
||||
BuildRoot: %{staging}
|
||||
%description
|
||||
Library to make it easier to access the Xen control interfaces.
|
||||
|
||||
%pre
|
||||
%preun
|
||||
%install
|
||||
install -m 0755 -d $RPM_BUILD_ROOT/lib
|
||||
install -m 0755 libxi.a $RPM_BUILD_ROOT/lib/libxi.a
|
||||
install -m 0755 libxi.so $RPM_BUILD_ROOT/lib/libxi.so
|
||||
install -m 0755 -d $RPM_BUILD_ROOT/include
|
||||
install -m 0644 xi.h $RPM_BUILD_ROOT/include/xi.h
|
||||
%clean
|
||||
%post
|
||||
%postun
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%dir /lib
|
||||
/lib/libxi.a
|
||||
/lib/libxi.so
|
||||
%dir /include
|
||||
/include/xi.h
|
||||
@@ -0,0 +1,99 @@
|
||||
/******************************************************************************
|
||||
* xi.h
|
||||
*
|
||||
* A library for low-level access to the Xen control interfaces.
|
||||
*
|
||||
* Copyright (c) 2003, K A Fraser.
|
||||
*/
|
||||
|
||||
#ifndef __XI_H__
|
||||
#define __XI_H__
|
||||
|
||||
int xi_interface_open(void);
|
||||
int xi_interface_close(void);
|
||||
|
||||
typedef struct {
|
||||
unsigned int domid;
|
||||
unsigned int cpu;
|
||||
int has_cpu;
|
||||
int stopped;
|
||||
unsigned long nr_pages;
|
||||
unsigned long long cpu_time;
|
||||
#define XI_DOMINFO_MAXNAME 16
|
||||
char name[XI_DOMINFO_MAXNAME];
|
||||
} xi_dominfo_t;
|
||||
|
||||
int xi_domain_create(unsigned int mem_kb, const char *name);
|
||||
int xi_domain_start(unsigned int domid);
|
||||
int xi_domain_stop(unsigned int domid);
|
||||
int xi_domain_destroy(unsigned int domid, int force);
|
||||
int xi_domain_getinfo(unsigned int first_domid,
|
||||
unsigned int max_doms,
|
||||
xi_dominfo_t *info);
|
||||
|
||||
int xi_linux_save(unsigned int domid, const char *state_file, int verbose);
|
||||
int xi_linux_restore(const char *state_file, int verbose);
|
||||
int xi_linux_build(unsigned int domid,
|
||||
const char *image_name,
|
||||
const char *ramdisk_name,
|
||||
const char *cmdline,
|
||||
int verbose);
|
||||
|
||||
int xi_bvtsched_global_set(unsigned long ctx_allow);
|
||||
int xi_bvtsched_domain_set(unsigned int domid,
|
||||
unsigned long mcuadv,
|
||||
unsigned long warp,
|
||||
unsigned long warpl,
|
||||
unsigned long warpu);
|
||||
|
||||
typedef struct {
|
||||
unsigned long credit_bytes;
|
||||
unsigned long credit_usec;
|
||||
} xi_vif_sched_params_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned long long tx_bytes, tx_pkts;
|
||||
unsigned long long rx_bytes, rx_pkts;
|
||||
} xi_vif_stats_t;
|
||||
|
||||
int xi_vif_scheduler_set(unsigned int domid,
|
||||
unsigned int vifid,
|
||||
xi_vif_sched_params_t *params);
|
||||
int xi_vif_scheduler_get(unsigned int domid,
|
||||
unsigned int vifid,
|
||||
xi_vif_sched_params_t *params);
|
||||
int xi_vif_stats_get(unsigned int domid,
|
||||
unsigned int vifid,
|
||||
xi_vif_stats_t *stats);
|
||||
|
||||
typedef struct {
|
||||
#define XI_VBDDOM_PROBE_ALL (~0U)
|
||||
unsigned int domid;
|
||||
unsigned short vbdid;
|
||||
#define XI_VBDF_WRITEABLE (1<<0)
|
||||
unsigned long flags;
|
||||
unsigned long nr_sectors;
|
||||
} xi_vbd_t;
|
||||
|
||||
|
||||
int xi_vbd_create(unsigned int domid, unsigned short vbdid, int writeable);
|
||||
int xi_vbd_destroy(unsigned int domid, unsigned short vbdid);
|
||||
int xi_vbd_add_extent(unsigned int domid,
|
||||
unsigned short vbdid,
|
||||
unsigned short real_device,
|
||||
unsigned long start_sector,
|
||||
unsigned long nr_sectors);
|
||||
int xi_vbd_delete_extent(unsigned int domid,
|
||||
unsigned short vbdid,
|
||||
unsigned short real_device,
|
||||
unsigned long start_sector,
|
||||
unsigned long nr_sectors);
|
||||
int xi_vbd_probe(unsigned int domid,
|
||||
unsigned short vbdid,
|
||||
unsigned int max_vbds,
|
||||
xi_vbd_t *vbds);
|
||||
|
||||
int xi_readconsolering(char *str, unsigned int max_chars, int clear);
|
||||
|
||||
|
||||
#endif /* __XI_H__ */
|
||||