diff --git a/xen/arch/x86/hvm/ioreq.c b/xen/arch/x86/hvm/ioreq.c index cad6567f88..9435291e87 100644 --- a/xen/arch/x86/hvm/ioreq.c +++ b/xen/arch/x86/hvm/ioreq.c @@ -270,6 +270,19 @@ static int hvm_map_ioreq_gfn(struct hvm_ioreq_server *s, bool buf) struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq; int rc; + if ( iorp->page ) + { + /* + * If a page has already been allocated (which will happen on + * demand if hvm_get_ioreq_server_frame() is called), then + * mapping a guest frame is not permitted. + */ + if ( gfn_eq(iorp->gfn, INVALID_GFN) ) + return -EPERM; + + return 0; + } + if ( d->is_dying ) return -EINVAL; @@ -292,6 +305,70 @@ static int hvm_map_ioreq_gfn(struct hvm_ioreq_server *s, bool buf) return rc; } +static int hvm_alloc_ioreq_mfn(struct hvm_ioreq_server *s, bool buf) +{ + struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq; + + if ( iorp->page ) + { + /* + * If a guest frame has already been mapped (which may happen + * on demand if hvm_get_ioreq_server_info() is called), then + * allocating a page is not permitted. + */ + if ( !gfn_eq(iorp->gfn, INVALID_GFN) ) + return -EPERM; + + return 0; + } + + /* + * Allocated IOREQ server pages are assigned to the emulating + * domain, not the target domain. This is safe because the emulating + * domain cannot be destroyed until the ioreq server is destroyed. + * Also we must use MEMF_no_refcount otherwise page allocation + * could fail if the emulating domain has already reached its + * maximum allocation. + */ + iorp->page = alloc_domheap_page(s->emulator, MEMF_no_refcount); + + if ( !iorp->page ) + return -ENOMEM; + + if ( !get_page_type(iorp->page, PGT_writable_page) ) + goto fail1; + + iorp->va = __map_domain_page_global(iorp->page); + if ( !iorp->va ) + goto fail2; + + clear_page(iorp->va); + return 0; + + fail2: + put_page_type(iorp->page); + + fail1: + put_page(iorp->page); + iorp->page = NULL; + + return -ENOMEM; +} + +static void hvm_free_ioreq_mfn(struct hvm_ioreq_server *s, bool buf) +{ + struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq; + + if ( !iorp->page ) + return; + + unmap_domain_page_global(iorp->va); + iorp->va = NULL; + + put_page_and_type(iorp->page); + iorp->page = NULL; +} + bool is_ioreq_server_page(struct domain *d, const struct page_info *page) { const struct hvm_ioreq_server *s; @@ -496,6 +573,27 @@ static void hvm_ioreq_server_unmap_pages(struct hvm_ioreq_server *s) hvm_unmap_ioreq_gfn(s, false); } +static int hvm_ioreq_server_alloc_pages(struct hvm_ioreq_server *s) +{ + int rc; + + rc = hvm_alloc_ioreq_mfn(s, false); + + if ( !rc && (s->bufioreq_handling != HVM_IOREQSRV_BUFIOREQ_OFF) ) + rc = hvm_alloc_ioreq_mfn(s, true); + + if ( rc ) + hvm_free_ioreq_mfn(s, false); + + return rc; +} + +static void hvm_ioreq_server_free_pages(struct hvm_ioreq_server *s) +{ + hvm_free_ioreq_mfn(s, true); + hvm_free_ioreq_mfn(s, false); +} + static void hvm_ioreq_server_free_rangesets(struct hvm_ioreq_server *s) { unsigned int i; @@ -647,7 +745,19 @@ static void hvm_ioreq_server_deinit(struct hvm_ioreq_server *s) { ASSERT(!s->enabled); hvm_ioreq_server_remove_all_vcpus(s); + + /* + * NOTE: It is safe to call both hvm_ioreq_server_unmap_pages() and + * hvm_ioreq_server_free_pages() in that order. + * This is because the former will do nothing if the pages + * are not mapped, leaving the page to be freed by the latter. + * However if the pages are mapped then the former will set + * the page_info pointer to NULL, meaning the latter will do + * nothing. + */ hvm_ioreq_server_unmap_pages(s); + hvm_ioreq_server_free_pages(s); + hvm_ioreq_server_free_rangesets(s); put_domain(s->emulator); @@ -825,6 +935,63 @@ int hvm_get_ioreq_server_info(struct domain *d, ioservid_t id, return rc; } +int hvm_get_ioreq_server_frame(struct domain *d, ioservid_t id, + unsigned long idx, mfn_t *mfn) +{ + struct hvm_ioreq_server *s; + int rc; + + if ( id == DEFAULT_IOSERVID ) + return -EOPNOTSUPP; + + if ( !is_hvm_domain(d) ) + return -EINVAL; + + spin_lock_recursive(&d->arch.hvm_domain.ioreq_server.lock); + + s = get_ioreq_server(d, id); + + rc = -ENOENT; + if ( !s ) + goto out; + + ASSERT(!IS_DEFAULT(s)); + + rc = -EPERM; + if ( s->emulator != current->domain ) + goto out; + + rc = hvm_ioreq_server_alloc_pages(s); + if ( rc ) + goto out; + + switch ( idx ) + { + case XENMEM_resource_ioreq_server_frame_bufioreq: + rc = -ENOENT; + if ( !HANDLE_BUFIOREQ(s) ) + goto out; + + *mfn = _mfn(page_to_mfn(s->bufioreq.page)); + rc = 0; + break; + + case XENMEM_resource_ioreq_server_frame_ioreq(0): + *mfn = _mfn(page_to_mfn(s->ioreq.page)); + rc = 0; + break; + + default: + rc = -EINVAL; + break; + } + + out: + spin_unlock_recursive(&d->arch.hvm_domain.ioreq_server.lock); + + return rc; +} + int hvm_map_io_range_to_ioreq_server(struct domain *d, ioservid_t id, uint32_t type, uint64_t start, uint64_t end) diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 17558e0c8c..4964910d09 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -123,6 +123,7 @@ #include #include #include +#include #include #include @@ -4228,6 +4229,52 @@ int xenmem_add_to_physmap_one( return rc; } +int arch_acquire_resource(struct domain *d, unsigned int type, + unsigned int id, unsigned long frame, + unsigned int nr_frames, xen_pfn_t mfn_list[], + unsigned int *flags) +{ + int rc; + + switch ( type ) + { + case XENMEM_resource_ioreq_server: + { + ioservid_t ioservid = id; + unsigned int i; + + rc = -EINVAL; + if ( id != (unsigned int)ioservid ) + break; + + rc = 0; + for ( i = 0; i < nr_frames; i++ ) + { + mfn_t mfn; + + rc = hvm_get_ioreq_server_frame(d, id, frame + i, &mfn); + if ( rc ) + break; + + mfn_list[i] = mfn_x(mfn); + } + + /* + * The frames will have been assigned to the domain that created + * the ioreq server. + */ + *flags |= XENMEM_rsrc_acq_caller_owned; + break; + } + + default: + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + long arch_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg) { int rc; diff --git a/xen/common/memory.c b/xen/common/memory.c index 9e9ac445f7..2091bb8c2f 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -1013,7 +1013,8 @@ static int acquire_resource( switch ( xmar.type ) { default: - rc = -EOPNOTSUPP; + rc = arch_acquire_resource(d, xmar.type, xmar.id, xmar.frame, + xmar.nr_frames, mfn_list, &xmar.flags); break; } diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h index a0e922f360..f4a8e7953c 100644 --- a/xen/include/asm-arm/mm.h +++ b/xen/include/asm-arm/mm.h @@ -374,6 +374,14 @@ static inline void put_page_and_type(struct page_info *page) void clear_and_clean_page(struct page_info *page); +static inline +int arch_acquire_resource(struct domain *d, unsigned int type, unsigned int id, + unsigned long frame, unsigned int nr_frames, + xen_pfn_t mfn_list[], unsigned int *flags) +{ + return -EOPNOTSUPP; +} + #endif /* __ARCH_ARM_MM__ */ /* * Local variables: diff --git a/xen/include/asm-x86/hvm/ioreq.h b/xen/include/asm-x86/hvm/ioreq.h index 1bd1a02f23..bab473cf44 100644 --- a/xen/include/asm-x86/hvm/ioreq.h +++ b/xen/include/asm-x86/hvm/ioreq.h @@ -30,6 +30,8 @@ int hvm_get_ioreq_server_info(struct domain *d, ioservid_t id, unsigned long *ioreq_gfn, unsigned long *bufioreq_gfn, evtchn_port_t *bufioreq_port); +int hvm_get_ioreq_server_frame(struct domain *d, ioservid_t id, + unsigned long idx, mfn_t *mfn); int hvm_map_io_range_to_ioreq_server(struct domain *d, ioservid_t id, uint32_t type, uint64_t start, uint64_t end); diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h index c115661837..d3cd5b9b22 100644 --- a/xen/include/asm-x86/mm.h +++ b/xen/include/asm-x86/mm.h @@ -623,4 +623,9 @@ static inline bool arch_mfn_in_directmap(unsigned long mfn) return mfn <= (virt_to_mfn(eva - 1) + 1); } +int arch_acquire_resource(struct domain *d, unsigned int type, + unsigned int id, unsigned long frame, + unsigned int nr_frames, xen_pfn_t mfn_list[], + unsigned int *flags); + #endif /* __ASM_X86_MM_H__ */ diff --git a/xen/include/public/hvm/dm_op.h b/xen/include/public/hvm/dm_op.h index 9823726d79..d3b554d019 100644 --- a/xen/include/public/hvm/dm_op.h +++ b/xen/include/public/hvm/dm_op.h @@ -90,6 +90,10 @@ struct xen_dm_op_create_ioreq_server { * the frame numbers passed back in gfns and * respectively. (If the IOREQ Server is not handling buffered emulation * only will be valid). + * + * NOTE: To access the synchronous ioreq structures and buffered ioreq + * ring, it is preferable to use the XENMEM_acquire_resource memory + * op specifying resource type XENMEM_resource_ioreq_server. */ #define XEN_DMOP_get_ioreq_server_info 2 diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h index fddc973f19..bf2f81faae 100644 --- a/xen/include/public/memory.h +++ b/xen/include/public/memory.h @@ -609,9 +609,14 @@ struct xen_mem_acquire_resource { domid_t domid; /* IN - the type of resource */ uint16_t type; + +#define XENMEM_resource_ioreq_server 0 + /* * IN - a type-specific resource identifier, which must be zero * unless stated otherwise. + * + * type == XENMEM_resource_ioreq_server -> id == ioreq server id */ uint32_t id; /* @@ -636,6 +641,10 @@ struct xen_mem_acquire_resource { * is ignored if nr_frames is 0. */ uint64_aligned_t frame; + +#define XENMEM_resource_ioreq_server_frame_bufioreq 0 +#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n)) + /* * IN/OUT - If the tools domain is PV then, upon return, frame_list * will be populated with the MFNs of the resource.