diff options
-rw-r--r-- | tools/include/xen-sys/Linux/privcmd.h | 10 | ||||
-rw-r--r-- | tools/libxc/Makefile | 2 | ||||
-rw-r--r-- | tools/libxc/xc_domain_restore.c | 16 | ||||
-rw-r--r-- | tools/libxc/xc_domain_save.c | 49 | ||||
-rw-r--r-- | tools/libxc/xc_linux.c | 133 | ||||
-rw-r--r-- | tools/libxc/xc_misc.c | 69 | ||||
-rw-r--r-- | tools/libxc/xc_resume.c | 4 | ||||
-rw-r--r-- | tools/libxc/xenctrl.h | 10 | ||||
-rw-r--r-- | tools/xenpaging/xenpaging.c | 13 |
9 files changed, 249 insertions, 57 deletions
diff --git a/tools/include/xen-sys/Linux/privcmd.h b/tools/include/xen-sys/Linux/privcmd.h index d1162eebb0..d35aac9baa 100644 --- a/tools/include/xen-sys/Linux/privcmd.h +++ b/tools/include/xen-sys/Linux/privcmd.h @@ -64,6 +64,14 @@ typedef struct privcmd_mmapbatch { xen_pfn_t __user *arr; /* array of mfns - top nibble set on err */ } privcmd_mmapbatch_t; +typedef struct privcmd_mmapbatch_v2 { + unsigned int num; /* number of pages to populate */ + domid_t dom; /* target domain */ + __u64 addr; /* virtual address */ + const xen_pfn_t __user *arr; /* array of mfns */ + int __user *err; /* array of error codes */ +} privcmd_mmapbatch_v2_t; + /* * @cmd: IOCTL_PRIVCMD_HYPERCALL * @arg: &privcmd_hypercall_t @@ -75,5 +83,7 @@ typedef struct privcmd_mmapbatch { _IOC(_IOC_NONE, 'P', 2, sizeof(privcmd_mmap_t)) #define IOCTL_PRIVCMD_MMAPBATCH \ _IOC(_IOC_NONE, 'P', 3, sizeof(privcmd_mmapbatch_t)) +#define IOCTL_PRIVCMD_MMAPBATCH_V2 \ + _IOC(_IOC_NONE, 'P', 4, sizeof(privcmd_mmapbatch_v2_t)) #endif /* __LINUX_PUBLIC_PRIVCMD_H__ */ diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile index 8a5bdb5ced..8a565e1370 100644 --- a/tools/libxc/Makefile +++ b/tools/libxc/Makefile @@ -1,7 +1,7 @@ XEN_ROOT = ../.. include $(XEN_ROOT)/tools/Rules.mk -MAJOR = 3.4 +MAJOR = 4.0 MINOR = 0 CTRL_SRCS-y := diff --git a/tools/libxc/xc_domain_restore.c b/tools/libxc/xc_domain_restore.c index 827ba6bfd0..3999569582 100644 --- a/tools/libxc/xc_domain_restore.c +++ b/tools/libxc/xc_domain_restore.c @@ -118,7 +118,7 @@ static int break_super_page(int xc_handle, page_array[i] = start_pfn + i; } - ram_base = xc_map_foreign_batch(xc_handle, dom, PROT_READ, + ram_base = xc_map_foreign_pages(xc_handle, dom, PROT_READ, page_array, tot_pfns); if ( ram_base == NULL ) @@ -166,7 +166,7 @@ static int break_super_page(int xc_handle, page_array[i] = start_pfn + i; } - ram_base = xc_map_foreign_batch(xc_handle, dom, PROT_WRITE, + ram_base = xc_map_foreign_pages(xc_handle, dom, PROT_WRITE, page_array, tot_pfns); if ( ram_base == NULL ) { @@ -494,7 +494,7 @@ static ssize_t read_exact_timed(int fd, void* buf, size_t size) ** the (now known) appropriate mfn values. */ static int uncanonicalize_pagetable(int xc_handle, uint32_t dom, struct restore_ctx *ctx, - unsigned long type, void *page, int superpages) + void *page, int superpages) { int i, pte_last; unsigned long pfn; @@ -1186,7 +1186,7 @@ static int apply_batch(int xc_handle, uint32_t dom, struct restore_ctx *ctx, } /* Map relevant mfns */ - region_base = xc_map_foreign_batch( + region_base = xc_map_foreign_pages( xc_handle, dom, PROT_WRITE, region_mfn, j); if ( region_base == NULL ) @@ -1240,7 +1240,7 @@ static int apply_batch(int xc_handle, uint32_t dom, struct restore_ctx *ctx, (pagetype != XEN_DOMCTL_PFINFO_L1TAB)) { if (!uncanonicalize_pagetable(xc_handle, dom, ctx, - pagetype, page, superpages)) { + page, superpages)) { /* ** Failing to uncanonicalize a page table can be ok ** under live migration since the pages type may have @@ -1655,7 +1655,7 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom, if ( (i == (dinfo->p2m_size-1)) || (j == MAX_BATCH_SIZE) ) { - region_base = xc_map_foreign_batch( + region_base = xc_map_foreign_pages( xc_handle, dom, PROT_READ | PROT_WRITE, region_mfn, j); if ( region_base == NULL ) { @@ -1666,7 +1666,7 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom, for ( k = 0; k < j; k++ ) { if ( !uncanonicalize_pagetable( - xc_handle, dom, ctx, XEN_DOMCTL_PFINFO_L1TAB, + xc_handle, dom, ctx, region_base + k*PAGE_SIZE, superpages) ) { ERROR("failed uncanonicalize pt!"); @@ -1949,7 +1949,7 @@ int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom, } /* Copy the P2M we've constructed to the 'live' P2M */ - if ( !(ctx->live_p2m = xc_map_foreign_batch(xc_handle, dom, PROT_WRITE, + if ( !(ctx->live_p2m = xc_map_foreign_pages(xc_handle, dom, PROT_WRITE, p2m_frame_list, P2M_FL_ENTRIES)) ) { ERROR("Couldn't map p2m table"); diff --git a/tools/libxc/xc_domain_save.c b/tools/libxc/xc_domain_save.c index 3f351ad071..87ec7dbbd6 100644 --- a/tools/libxc/xc_domain_save.c +++ b/tools/libxc/xc_domain_save.c @@ -709,7 +709,7 @@ static xen_pfn_t *map_and_save_p2m_table(int xc_handle, 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, + xc_map_foreign_pages(xc_handle, dom, PROT_READ, p2m_frame_list_list, P2M_FLL_ENTRIES); if ( !live_p2m_frame_list ) @@ -727,6 +727,9 @@ static xen_pfn_t *map_and_save_p2m_table(int xc_handle, memset(p2m_frame_list, 0, P2M_TOOLS_FL_SIZE); memcpy(p2m_frame_list, live_p2m_frame_list, P2M_GUEST_FL_SIZE); + munmap(live_p2m_frame_list, P2M_FLL_ENTRIES * PAGE_SIZE); + live_p2m_frame_list = NULL; + /* Canonicalize guest's unsigned long vs ours */ if ( dinfo->guest_width > sizeof(unsigned long) ) for ( i = 0; i < P2M_FL_ENTRIES; i++ ) @@ -741,7 +744,7 @@ static xen_pfn_t *map_and_save_p2m_table(int xc_handle, (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 = xc_map_foreign_pages(xc_handle, dom, PROT_READ, p2m_frame_list, P2M_FL_ENTRIES); if ( !p2m ) @@ -873,8 +876,9 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, vcpu_guest_context_any_t ctxt; /* A table containing the type of each PFN (/not/ MFN!). */ - unsigned long *pfn_type = NULL; + xen_pfn_t *pfn_type = NULL; unsigned long *pfn_batch = NULL; + int *pfn_err = NULL; /* A copy of one frame of guest memory. */ char page[PAGE_SIZE]; @@ -1263,8 +1267,8 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, if ( batch == 0 ) goto skip; /* vanishingly unlikely... */ - region_base = xc_map_foreign_batch( - xc_handle, dom, PROT_READ, pfn_type, batch); + region_base = xc_map_foreign_bulk( + xc_handle, dom, PROT_READ, pfn_type, pfn_err, batch); if ( region_base == NULL ) { ERROR("map batch failed"); @@ -1275,14 +1279,19 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, { /* Look for and skip completely empty batches. */ for ( j = 0; j < batch; j++ ) - if ( (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) != - XEN_DOMCTL_PFINFO_XTAB ) + { + if ( !pfn_err[j] ) break; + pfn_type[j] |= XEN_DOMCTL_PFINFO_XTAB; + } if ( j == batch ) { munmap(region_base, batch*PAGE_SIZE); continue; /* bail on this batch: no valid pages */ } + for ( ; j < batch; j++ ) + if ( pfn_err[j] ) + pfn_type[j] |= XEN_DOMCTL_PFINFO_XTAB; } else { @@ -1296,16 +1305,17 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, goto out; } for ( j = batch-1; j >= 0; j-- ) - pfn_type[j] = ((uint32_t *)pfn_type)[j]; + pfn_type[j] = ((uint32_t *)pfn_type)[j] & + XEN_DOMCTL_PFINFO_LTAB_MASK; for ( j = 0; j < batch; j++ ) { + unsigned long mfn = pfn_to_mfn(pfn_batch[j]); - if ( (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) == - XEN_DOMCTL_PFINFO_XTAB ) + if ( pfn_type[j] == XEN_DOMCTL_PFINFO_XTAB ) { DPRINTF("type fail: page %i mfn %08lx\n", - j, pfn_type[j]); + j, mfn); continue; } @@ -1313,16 +1323,13 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, DPRINTF("%d pfn= %08lx mfn= %08lx [mfn]= %08lx" " sum= %08lx\n", iter, - (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) | - pfn_batch[j], - pfn_type[j], - mfn_to_pfn(pfn_type[j] & - ~XEN_DOMCTL_PFINFO_LTAB_MASK), + pfn_type[j] | pfn_batch[j], + mfn, + mfn_to_pfn(mfn), csum_page(region_base + (PAGE_SIZE*j))); /* canonicalise mfn->pfn */ - pfn_type[j] = (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) | - pfn_batch[j]; + pfn_type[j] |= pfn_batch[j]; } } @@ -1332,11 +1339,17 @@ int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, goto out; } + if ( sizeof(unsigned long) < sizeof(*pfn_type) ) + for ( j = 0; j < batch; j++ ) + ((unsigned long *)pfn_type)[j] = pfn_type[j]; if ( write_exact(io_fd, pfn_type, sizeof(unsigned long)*batch) ) { PERROR("Error when writing to state file (3)"); goto out; } + if ( sizeof(unsigned long) < sizeof(*pfn_type) ) + while ( --j >= 0 ) + pfn_type[j] = ((unsigned long *)pfn_type)[j]; /* entering this loop, pfn_type is now in pfns (Not mfns) */ run = 0; diff --git a/tools/libxc/xc_linux.c b/tools/libxc/xc_linux.c index 0f1b78aa57..e0e807f2cc 100644 --- a/tools/libxc/xc_linux.c +++ b/tools/libxc/xc_linux.c @@ -64,7 +64,7 @@ int xc_interface_close(int xc_handle) return close(xc_handle); } -static int xc_map_foreign_batch_single(int xc_handle, uint32_t dom, int prot, +static int xc_map_foreign_batch_single(int xc_handle, uint32_t dom, xen_pfn_t *mfn, unsigned long addr) { privcmd_mmapbatch_t ioctlx; @@ -116,7 +116,7 @@ void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot, XEN_DOMCTL_PFINFO_PAGEDTAB ) { unsigned long paged_addr = (unsigned long)addr + (i << PAGE_SHIFT); - rc = xc_map_foreign_batch_single(xc_handle, dom, prot, &arr[i], + rc = xc_map_foreign_batch_single(xc_handle, dom, &arr[i], paged_addr); if ( rc < 0 ) goto out; @@ -137,6 +137,131 @@ void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot, return addr; } +void *xc_map_foreign_bulk(int xc_handle, uint32_t dom, int prot, + const xen_pfn_t *arr, int *err, unsigned int num) +{ + privcmd_mmapbatch_v2_t ioctlx; + void *addr; + unsigned int i; + int rc; + + addr = mmap(NULL, (unsigned long)num << PAGE_SHIFT, prot, MAP_SHARED, + xc_handle, 0); + if ( addr == MAP_FAILED ) + { + perror("xc_map_foreign_batch: mmap failed"); + return NULL; + } + + ioctlx.num = num; + ioctlx.dom = dom; + ioctlx.addr = (unsigned long)addr; + ioctlx.arr = arr; + ioctlx.err = err; + + rc = ioctl(xc_handle, IOCTL_PRIVCMD_MMAPBATCH_V2, &ioctlx); + + if ( rc < 0 && errno == ENOENT ) + { + for ( i = rc = 0; rc == 0 && i < num; i++ ) + { + if ( err[i] != -ENOENT ) + continue; + + ioctlx.num = 1; + ioctlx.dom = dom; + ioctlx.addr = (unsigned long)addr + ((unsigned long)i<<PAGE_SHIFT); + ioctlx.arr = arr + i; + ioctlx.err = err + i; + do { + usleep(100); + rc = ioctl(xc_handle, IOCTL_PRIVCMD_MMAPBATCH_V2, &ioctlx); + } while ( rc < 0 && err[i] == -ENOENT ); + } + } + + if ( rc < 0 && errno == ENOTTY && (int)num > 0 ) + { + /* + * IOCTL_PRIVCMD_MMAPBATCH_V2 is not supported - fall back to + * IOCTL_PRIVCMD_MMAPBATCH. + */ + xen_pfn_t *pfn = calloc(num, sizeof(*pfn)); + + if ( pfn ) + { + privcmd_mmapbatch_t ioctlx; + + memcpy(pfn, arr, num * sizeof(*arr)); + + ioctlx.num = num; + ioctlx.dom = dom; + ioctlx.addr = (unsigned long)addr; + ioctlx.arr = pfn; + + rc = ioctl(xc_handle, IOCTL_PRIVCMD_MMAPBATCH, &ioctlx); + + rc = rc < 0 ? -errno : 0; + + for ( i = 0; i < num; ++i ) + { + switch ( pfn[i] ^ arr[i] ) + { + case 0: + err[i] = rc != -ENOENT ? rc : 0; + continue; + default: + err[i] = -EINVAL; + continue; + case XEN_DOMCTL_PFINFO_PAGEDTAB: + if ( rc != -ENOENT ) + { + err[i] = rc ?: -EINVAL; + continue; + } + rc = xc_map_foreign_batch_single(xc_handle, dom, pfn + i, + (unsigned long)addr + ((unsigned long)i<<PAGE_SHIFT)); + if ( rc < 0 ) + { + rc = -errno; + break; + } + rc = -ENOENT; + continue; + } + break; + } + + free(pfn); + + if ( rc == -ENOENT && i == num ) + rc = 0; + else if ( rc ) + { + errno = -rc; + rc = -1; + } + } + else + { + errno = -ENOMEM; + rc = -1; + } + } + + if ( rc < 0 ) + { + int saved_errno = errno; + + perror("xc_map_foreign_bulk: ioctl failed"); + (void)munmap(addr, (unsigned long)num << PAGE_SHIFT); + errno = saved_errno; + return NULL; + } + + return addr; +} + void *xc_map_foreign_range(int xc_handle, uint32_t dom, int size, int prot, unsigned long mfn) { @@ -151,7 +276,7 @@ void *xc_map_foreign_range(int xc_handle, uint32_t dom, int size, int prot, for ( i = 0; i < num; i++ ) arr[i] = mfn + i; - ret = xc_map_foreign_batch(xc_handle, dom, prot, arr, num); + ret = xc_map_foreign_pages(xc_handle, dom, prot, arr, num); free(arr); return ret; } @@ -175,7 +300,7 @@ void *xc_map_foreign_ranges(int xc_handle, uint32_t dom, size_t size, int prot, for ( j = 0; j < num_per_entry; j++ ) arr[i * num_per_entry + j] = entries[i].mfn + j; - ret = xc_map_foreign_batch(xc_handle, dom, prot, arr, num); + ret = xc_map_foreign_pages(xc_handle, dom, prot, arr, num); free(arr); return ret; } diff --git a/tools/libxc/xc_misc.c b/tools/libxc/xc_misc.c index ffc04fb50f..e237b7862d 100644 --- a/tools/libxc/xc_misc.c +++ b/tools/libxc/xc_misc.c @@ -352,25 +352,23 @@ int xc_hvm_set_mem_type( void *xc_map_foreign_pages(int xc_handle, uint32_t dom, int prot, const xen_pfn_t *arr, int num) { - xen_pfn_t *pfn; void *res; - int i; + int i, *err; + + if (num < 0) { + errno = -EINVAL; + return NULL; + } - pfn = malloc(num * sizeof(*pfn)); - if (!pfn) + err = calloc(num, sizeof(*err)); + if (!err) return NULL; - memcpy(pfn, arr, num * sizeof(*pfn)); - res = xc_map_foreign_batch(xc_handle, dom, prot, pfn, num); + res = xc_map_foreign_bulk(xc_handle, dom, prot, arr, err, num); if (res) { for (i = 0; i < num; i++) { - if ((pfn[i] & 0xF0000000UL) == 0xF0000000UL) { - /* - * xc_map_foreign_batch() doesn't give us an error - * code, so we have to make one up. May not be the - * appropriate one. - */ - errno = EINVAL; + if (err[i]) { + errno = -err[i]; munmap(res, num * PAGE_SIZE); res = NULL; break; @@ -378,10 +376,53 @@ void *xc_map_foreign_pages(int xc_handle, uint32_t dom, int prot, } } - free(pfn); + free(err); return res; } +/* stub for all not yet converted OSes */ +void * +#ifdef __GNUC__ +__attribute__((__weak__)) +#endif +xc_map_foreign_bulk(int xc_handle, uint32_t dom, int prot, + const xen_pfn_t *arr, int *err, unsigned int num) +{ + xen_pfn_t *pfn; + unsigned int i; + void *ret; + + if ((int)num <= 0) { + errno = EINVAL; + return NULL; + } + + pfn = calloc(num, sizeof(*pfn)); + if (!pfn) { + errno = ENOMEM; + return NULL; + } + + memcpy(pfn, arr, num * sizeof(*arr)); + ret = xc_map_foreign_batch(xc_handle, dom, prot, pfn, num); + + if (ret) { + for (i = 0; i < num; ++i) + switch (pfn[i] ^ arr[i]) { + case 0: + err[i] = 0; + break; + default: + err[i] = -EINVAL; + break; + } + } + + free(pfn); + + return ret; +} + /* * Local variables: * mode: C diff --git a/tools/libxc/xc_resume.c b/tools/libxc/xc_resume.c index 2816609b0d..c17ae262d3 100644 --- a/tools/libxc/xc_resume.c +++ b/tools/libxc/xc_resume.c @@ -158,7 +158,7 @@ static int xc_domain_resume_any(int xc_handle, uint32_t domid) goto out; } - p2m_frame_list = xc_map_foreign_batch(xc_handle, domid, PROT_READ, + p2m_frame_list = xc_map_foreign_pages(xc_handle, domid, PROT_READ, p2m_frame_list_list, P2M_FLL_ENTRIES); if ( p2m_frame_list == NULL ) @@ -171,7 +171,7 @@ static int xc_domain_resume_any(int xc_handle, uint32_t domid) 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, domid, PROT_READ, + p2m = xc_map_foreign_pages(xc_handle, domid, PROT_READ, p2m_frame_list, P2M_FL_ENTRIES); if ( p2m == NULL ) diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h index 4be69a814f..7a35a1439c 100644 --- a/tools/libxc/xenctrl.h +++ b/tools/libxc/xenctrl.h @@ -749,6 +749,8 @@ void *xc_map_foreign_pages(int xc_handle, uint32_t dom, int prot, const xen_pfn_t *arr, int num ); /** + * DEPRECATED - use xc_map_foreign_bulk() instead. + * * Like xc_map_foreign_pages(), except it can succeeed partially. * When a page cannot be mapped, its PFN in @arr is or'ed with * 0xF0000000 to indicate the error. @@ -757,6 +759,14 @@ void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot, xen_pfn_t *arr, int num ); /** + * Like xc_map_foreign_pages(), except it can succeed partially. + * When a page cannot be mapped, its respective field in @err is + * set to the corresponding errno value. + */ +void *xc_map_foreign_bulk(int xc_handle, uint32_t dom, int prot, + const xen_pfn_t *arr, int *err, unsigned int num); + +/** * Translates a virtual address in the context of a given domain and * vcpu returning the GFN containing the address (that is, an MFN for * PV guests, a PFN for HVM guests). Returns 0 for failure. diff --git a/tools/xenpaging/xenpaging.c b/tools/xenpaging/xenpaging.c index 2817cb7312..a207ca8ac9 100644 --- a/tools/xenpaging/xenpaging.c +++ b/tools/xenpaging/xenpaging.c @@ -310,9 +310,9 @@ int xenpaging_evict_page(xenpaging_t *paging, xenpaging_victim_t *victim, int fd /* Map page */ gfn = victim->gfn; ret = -EFAULT; - page = xc_map_foreign_batch(paging->xc_handle, victim->domain_id, + page = xc_map_foreign_pages(paging->xc_handle, victim->domain_id, PROT_READ | PROT_WRITE, &gfn, 1); - if ( (gfn & XEN_DOMCTL_PFINFO_LTAB_MASK) == XEN_DOMCTL_PFINFO_XTAB ) + if ( page == NULL ) { ERROR("Error mapping page"); goto out; @@ -386,7 +386,7 @@ int xenpaging_populate_page(xenpaging_t *paging, unsigned long *gfn, int fd, int /* Map page */ ret = -EFAULT; - page = xc_map_foreign_batch(paging->xc_handle, paging->mem_event.domain_id, + page = xc_map_foreign_pages(paging->xc_handle, paging->mem_event.domain_id, PROT_READ | PROT_WRITE, gfn, 1); if ( page == NULL ) { @@ -394,13 +394,6 @@ int xenpaging_populate_page(xenpaging_t *paging, unsigned long *gfn, int fd, int goto out_map; } - /* Check that the page exists */ - if ( (*gfn & XEN_DOMCTL_PFINFO_LTAB_MASK) == XEN_DOMCTL_PFINFO_XTAB ) - { - ERROR("Error mapping page: gfn is invalid"); - goto out; - } - /* Read page */ ret = read_page(fd, page, i); if ( ret != 0 ) |