[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v6 13/14] x86: add iommu_ops to modify and flush IOMMU mappings
This patch adds iommu_ops to add (map) or remove (unmap) frames in the domain's IOMMU mappings, and an iommu_op to synchronize (flush) those manipulations with the hardware. Currently the flags value for each op must include the XEN_IOMMUOP_map/unmap/flush_all flag as the implementation does not yet support per-device mappings. The sbdf field of each hypercall is accordingly ignored. Mappings added by the map operation are tracked and only those mappings may be removed by a subsequent unmap operation. Frames are specified by the owning domain and GFN. It is, of course, permissable for a domain to map its own frames using DOMID_SELF. NOTE: The owning domain and GFN must also be specified in the unmap operation, as well as the BFN, so that they can be cross-checked with the existent mapping. Signed-off-by: Paul Durrant <paul.durrant@xxxxxxxxxx> --- Cc: Jan Beulich <jbeulich@xxxxxxxx> Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> Cc: George Dunlap <George.Dunlap@xxxxxxxxxxxxx> Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> Cc: Julien Grall <julien.grall@xxxxxxx> Cc: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx> Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx> Cc: Tim Deegan <tim@xxxxxxx> Cc: Wei Liu <wei.liu2@xxxxxxxxxx> v6: - Add placeholder sbdf field and flag to control scope of map, unmap and flush. v4: - Fixed logic inversion when checking return of iommu_unmap_page(). v3: - Add type pinning. v2: - Heavily re-worked in v2, including explicit tracking of mappings. This avoids the need to clear non-reserved mappings from IOMMU at start of day, which would be prohibitively slow on a large host. --- xen/common/iommu_op.c | 178 ++++++++++++++++++++++++++++++++++++++++++ xen/include/public/iommu_op.h | 110 ++++++++++++++++++++++++++ xen/include/xlat.lst | 3 + 3 files changed, 291 insertions(+) diff --git a/xen/common/iommu_op.c b/xen/common/iommu_op.c index ccbb9b6340..328522f245 100644 --- a/xen/common/iommu_op.c +++ b/xen/common/iommu_op.c @@ -123,6 +123,156 @@ static int iommu_op_enable_modification( return 0; } +static int iommuop_map(struct xen_iommu_op_map *op) +{ + struct domain *d, *currd = current->domain; + struct domain_iommu *iommu = dom_iommu(currd); + bool readonly = op->flags & XEN_IOMMUOP_map_readonly; + bfn_t bfn = _bfn(op->bfn); + struct page_info *page; + unsigned int prot; + int rc, ignore; + + if ( op->pad || + (op->flags & ~(XEN_IOMMUOP_map_all | + XEN_IOMMUOP_map_readonly)) ) + return -EINVAL; + + if ( !iommu->iommu_op_ranges ) + return -EOPNOTSUPP; + + /* Per-device mapping not yet supported */ + if ( !(op->flags & XEN_IOMMUOP_map_all) ) + return -EINVAL; + + /* Check whether the specified BFN falls in a reserved region */ + if ( rangeset_contains_singleton(iommu->reserved_ranges, bfn_x(bfn)) ) + return -EINVAL; + + d = rcu_lock_domain_by_any_id(op->domid); + if ( !d ) + return -ESRCH; + + rc = get_paged_gfn(d, _gfn(op->gfn), readonly, NULL, &page); + if ( rc ) + goto unlock; + + rc = -EINVAL; + if ( !readonly && !get_page_type(page, PGT_writable_page) ) + { + put_page(page); + goto unlock; + } + + prot = IOMMUF_readable; + if ( !readonly ) + prot |= IOMMUF_writable; + + rc = -EIO; + if ( iommu_map_page(currd, bfn, page_to_mfn(page), prot) ) + goto release; + + rc = rangeset_add_singleton(iommu->iommu_op_ranges, bfn_x(bfn)); + if ( rc ) + goto unmap; + + rc = 0; + goto unlock; /* retain mapping and references */ + + unmap: + ignore = iommu_unmap_page(currd, bfn); + + release: + if ( !readonly ) + put_page_type(page); + put_page(page); + + unlock: + rcu_unlock_domain(d); + return rc; +} + +static int iommuop_unmap(struct xen_iommu_op_unmap *op) +{ + struct domain *d, *currd = current->domain; + struct domain_iommu *iommu = dom_iommu(currd); + bfn_t bfn = _bfn(op->bfn); + mfn_t mfn; + bool readonly; + unsigned int prot; + struct page_info *page; + int rc; + + if ( op->pad || + (op->flags & ~XEN_IOMMUOP_unmap_all) ) + return -EINVAL; + + if ( !iommu->iommu_op_ranges ) + return -EOPNOTSUPP; + + /* Per-device unmapping not yet supported */ + if ( !(op->flags & XEN_IOMMUOP_unmap_all) ) + return -EINVAL; + + if ( !rangeset_contains_singleton(iommu->iommu_op_ranges, bfn_x(bfn)) || + iommu_lookup_page(currd, bfn, &mfn, &prot) || + !mfn_valid(mfn) ) + return -ENOENT; + + readonly = !(prot & IOMMUF_writable); + + d = rcu_lock_domain_by_any_id(op->domid); + if ( !d ) + return -ESRCH; + + rc = get_paged_gfn(d, _gfn(op->gfn), !(prot & IOMMUF_writable), NULL, + &page); + if ( rc ) + goto unlock; + + put_page(page); /* release extra reference just taken */ + + rc = -EINVAL; + if ( !mfn_eq(page_to_mfn(page), mfn) ) + goto unlock; + + /* release reference taken in map */ + if ( !readonly ) + put_page_type(page); + put_page(page); + + rc = rangeset_remove_singleton(iommu->iommu_op_ranges, bfn_x(bfn)); + if ( rc ) + goto unlock; + + if ( iommu_unmap_page(currd, bfn) ) + rc = -EIO; + + unlock: + rcu_unlock_domain(d); + + return rc; +} + +static int iommuop_flush(struct xen_iommu_op_flush *op) +{ + struct domain *currd = current->domain; + struct domain_iommu *iommu = dom_iommu(currd); + + if ( op->pad0 || op->pad1 || + (op->flags & ~XEN_IOMMUOP_flush_all) ) + return -EINVAL; + + if ( !iommu->iommu_op_ranges ) + return -EOPNOTSUPP; + + /* Per-device flushing not yet supported */ + if ( !(op->flags & XEN_IOMMUOP_flush_all) ) + return -EINVAL; + + return !iommu_iotlb_flush_all(currd) ? 0 : -EIO; +} + static void iommu_op(xen_iommu_op_t *op) { switch ( op->op ) @@ -135,6 +285,22 @@ static void iommu_op(xen_iommu_op_t *op) op->status = iommu_op_enable_modification(&op->u.enable_modification); break; + case XEN_IOMMUOP_map: + this_cpu(iommu_dont_flush_iotlb) = 1; + op->status = iommuop_map(&op->u.map); + this_cpu(iommu_dont_flush_iotlb) = 0; + break; + + case XEN_IOMMUOP_unmap: + this_cpu(iommu_dont_flush_iotlb) = 1; + op->status = iommuop_unmap(&op->u.unmap); + this_cpu(iommu_dont_flush_iotlb) = 0; + break; + + case XEN_IOMMUOP_flush: + op->status = iommuop_flush(&op->u.flush); + break; + default: op->status = -EOPNOTSUPP; break; @@ -148,6 +314,9 @@ int do_one_iommu_op(xen_iommu_op_buf_t *buf) static const size_t op_size[] = { [XEN_IOMMUOP_query_reserved] = sizeof(struct xen_iommu_op_query_reserved), [XEN_IOMMUOP_enable_modification] = sizeof(struct xen_iommu_op_enable_modification), + [XEN_IOMMUOP_map] = sizeof(struct xen_iommu_op_map), + [XEN_IOMMUOP_unmap] = sizeof(struct xen_iommu_op_unmap), + [XEN_IOMMUOP_flush] = sizeof(struct xen_iommu_op_flush), }; size_t size; int rc; @@ -235,6 +404,9 @@ int compat_one_iommu_op(compat_iommu_op_buf_t *buf) static const size_t op_size[] = { [XEN_IOMMUOP_query_reserved] = sizeof(struct compat_iommu_op_query_reserved), [XEN_IOMMUOP_enable_modification] = sizeof(struct compat_iommu_op_enable_modification), + [XEN_IOMMUOP_map] = sizeof(struct compat_iommu_op_map), + [XEN_IOMMUOP_unmap] = sizeof(struct compat_iommu_op_unmap), + [XEN_IOMMUOP_flush] = sizeof(struct compat_iommu_op_flush), }; size_t size; xen_iommu_op_t nat; @@ -273,6 +445,9 @@ int compat_one_iommu_op(compat_iommu_op_buf_t *buf) */ #define XLAT_iommu_op_u_query_reserved XEN_IOMMUOP_query_reserved #define XLAT_iommu_op_u_enable_modification XEN_IOMMUOP_enable_modification +#define XLAT_iommu_op_u_map XEN_IOMMUOP_map +#define XLAT_iommu_op_u_unmap XEN_IOMMUOP_unmap +#define XLAT_iommu_op_u_flush XEN_IOMMUOP_flush u = cmp.op; #define XLAT_iommu_op_query_reserved_HNDL_ranges(_d_, _s_) \ @@ -332,6 +507,9 @@ int compat_one_iommu_op(compat_iommu_op_buf_t *buf) &cmp, status) ) return -EFAULT; +#undef XLAT_iommu_op_u_flush +#undef XLAT_iommu_op_u_unmap +#undef XLAT_iommu_op_u_map #undef XLAT_iommu_op_u_enable_modification #undef XLAT_iommu_op_u_query_reserved diff --git a/xen/include/public/iommu_op.h b/xen/include/public/iommu_op.h index 9b98b5cf89..e6c08f4bdd 100644 --- a/xen/include/public/iommu_op.h +++ b/xen/include/public/iommu_op.h @@ -80,6 +80,113 @@ struct xen_iommu_op_enable_modification { #define XEN_IOMMU_CAP_per_device_mappings (1u << _XEN_IOMMU_CAP_per_device_mappings) }; +/* + * XEN_IOMMUOP_map: Map a guest page in the IOMMU. + */ +#define XEN_IOMMUOP_map 3 + +struct xen_iommu_op_map { + /* IN - The domid of the guest */ + domid_t domid; + /* + * IN - flags controlling the mapping. This should be a bitwise OR of the + * flags defined below. + */ + uint16_t flags; + + /* + * Should the mapping be created for all initiators? + * + * NOTE: This flag is currently required as the implementation does not yet + * support pre-device mappings. + */ +#define _XEN_IOMMUOP_map_all 0 +#define XEN_IOMMUOP_map_all (1 << (_XEN_IOMMUOP_map_all)) + + /* Should the mapping be read-only to the initiator? */ +#define _XEN_IOMMUOP_map_readonly 1 +#define XEN_IOMMUOP_map_readonly (1 << (_XEN_IOMMUOP_map_readonly)) + + uint32_t pad; + /* + * IN - Segment/Bus/Device/Function of the initiator. + * + * NOTE: This is ignored if XEN_IOMMUOP_map_all is set. + */ + uint64_t sbdf; + /* IN - The IOMMU frame number which will hold the new mapping */ + xen_bfn_t bfn; + /* IN - The guest frame number of the page to be mapped */ + xen_pfn_t gfn; +}; + +/* + * XEN_IOMMUOP_unmap_gfn: Remove a mapping in the IOMMU. + */ +#define XEN_IOMMUOP_unmap 4 + +struct xen_iommu_op_unmap { + /* IN - The domid of the guest */ + domid_t domid; + /* + * IN - flags controlling the unmapping. This should be a bitwise OR of the + * flags defined below. + */ + uint16_t flags; + + /* + * Should the mapping be destroyed for all initiators? + * + * NOTE: This flag is currently required as the implementation does not yet + * support pre-device mappings. + */ +#define _XEN_IOMMUOP_unmap_all 0 +#define XEN_IOMMUOP_unmap_all (1 << (_XEN_IOMMUOP_unmap_all)) + + uint32_t pad; + /* + * IN - Segment/Bus/Device/Function of the initiator. + * + * NOTE: This is ignored if XEN_IOMMUOP_unmap_all is set. + */ + uint64_t sbdf; + /* IN - The IOMMU frame number which holds the mapping to be removed */ + xen_bfn_t bfn; + /* IN - The guest frame number of the page that is mapped */ + xen_pfn_t gfn; +}; + +/* + * XEN_IOMMUOP_flush: Flush the IOMMU TLB. + */ +#define XEN_IOMMUOP_flush 5 + +struct xen_iommu_op_flush { + /* + * IN - flags controlling flushing. This should be a bitwise OR of the + * flags defined below. + */ + uint16_t flags; + + /* + * Should the mappings flushed for all initiators? + * + * NOTE: This flag is currently required as the implementation does not yet + * support pre-device mappings. + */ +#define _XEN_IOMMUOP_flush_all 0 +#define XEN_IOMMUOP_flush_all (1 << (_XEN_IOMMUOP_flush_all)) + + uint16_t pad0; + uint32_t pad1; + /* + * IN - Segment/Bus/Device/Function of the initiator. + * + * NOTE: This is ignored if XEN_IOMMUOP_flush_all is set. + */ + uint64_t sbdf; +}; + struct xen_iommu_op { uint16_t op; /* op type */ uint16_t pad; @@ -88,6 +195,9 @@ struct xen_iommu_op { union { struct xen_iommu_op_query_reserved query_reserved; struct xen_iommu_op_enable_modification enable_modification; + struct xen_iommu_op_map map; + struct xen_iommu_op_unmap unmap; + struct xen_iommu_op_flush flush; } u; }; typedef struct xen_iommu_op xen_iommu_op_t; diff --git a/xen/include/xlat.lst b/xen/include/xlat.lst index c1b27e0349..5ab4c72264 100644 --- a/xen/include/xlat.lst +++ b/xen/include/xlat.lst @@ -80,7 +80,10 @@ ! iommu_op iommu_op.h ! iommu_op_buf iommu_op.h ! iommu_op_enable_modification iommu_op.h +! iommu_op_flush iommu_op.h +! iommu_op_map iommu_op.h ! iommu_op_query_reserved iommu_op.h +! iommu_op_unmap iommu_op.h ? iommu_reserved_range iommu_op.h ? kexec_exec kexec.h ! kexec_image kexec.h -- 2.11.0 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |