[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v3 08/13] x86: add iommu_op to query reserved ranges
This patch adds an iommu_op to allow the domain IOMMU reserved ranges to be queried by the guest. NOTE: The number of reserved ranges is determined by system firmware, in conjunction with Xen command line options, and is expected to be small. Thus, to avoid over-complicating the code, there is no pre-emption check within the operation. 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: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx> Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx> Cc: Tim Deegan <tim@xxxxxxx> Cc: Wei Liu <wei.liu2@xxxxxxxxxx> v3: - Avoid speculation beyond array bounds check. v2: - Re-implemented for v2 based on new rangeset. --- xen/arch/x86/iommu_op.c | 170 ++++++++++++++++++++++++++++++++++++++++-- xen/include/public/iommu_op.h | 39 ++++++++++ xen/include/xlat.lst | 2 + 3 files changed, 205 insertions(+), 6 deletions(-) diff --git a/xen/arch/x86/iommu_op.c b/xen/arch/x86/iommu_op.c index 744c0fce27..97811f8ced 100644 --- a/xen/arch/x86/iommu_op.c +++ b/xen/arch/x86/iommu_op.c @@ -22,11 +22,70 @@ #include <xen/event.h> #include <xen/guest_access.h> #include <xen/hypercall.h> +#include <xen/nospec.h> + +struct get_reserved_ctxt { + unsigned int max_entries; + unsigned int nr_entries; + XEN_GUEST_HANDLE(xen_iommu_reserved_range_t) ranges; +}; + +static int get_reserved(unsigned long s, unsigned long e, void *arg) +{ + struct get_reserved_ctxt *ctxt = arg; + + if ( ctxt->nr_entries < ctxt->max_entries ) + { + xen_iommu_reserved_range_t range = { + .start_bfn = s, + .nr_frames = e - s, + }; + + if ( copy_to_guest_offset(ctxt->ranges, ctxt->nr_entries, &range, + 1) ) + return -EFAULT; + } + + ctxt->nr_entries++; + return 0; +} + +static int iommu_op_query_reserved(struct xen_iommu_op_query_reserved *op) +{ + struct domain *currd = current->domain; + struct domain_iommu *iommu = dom_iommu(currd); + struct get_reserved_ctxt ctxt = { + .max_entries = op->nr_entries, + .ranges = op->ranges, + }; + int rc; + + if ( op->pad ) + return -EINVAL; + + rc = rangeset_report_ranges(iommu->reserved_ranges, 0, ~0ul, + get_reserved, &ctxt); + if ( rc ) + return rc; + + /* Pass back the actual number of reserved ranges */ + op->nr_entries = ctxt.nr_entries; + + if ( !guest_handle_is_null(ctxt.ranges) && + ctxt.nr_entries > ctxt.max_entries ) + return -ENOBUFS; + + return 0; +} static void iommu_op(xen_iommu_op_t *op) { switch ( op->op ) { + case XEN_IOMMUOP_query_reserved: + op->status = iommu_op_query_reserved(&op->u.query_reserved); + break; + default: op->status = -EOPNOTSUPP; break; @@ -35,13 +94,20 @@ static void iommu_op(xen_iommu_op_t *op) int do_one_iommu_op(xen_iommu_op_buf_t *buf) { - xen_iommu_op_t op; + xen_iommu_op_t op = {}; + size_t offset; + static const size_t op_size[] = { + [XEN_IOMMUOP_query_reserved] = sizeof(struct xen_iommu_op_query_reserved), + }; + size_t size; int rc; - if ( buf->size < sizeof(op) ) + offset = offsetof(struct xen_iommu_op, u); + + if ( buf->size < offset ) return -EFAULT; - if ( copy_from_guest((void *)&op, buf->h, sizeof(op)) ) + if ( copy_from_guest((void *)&op, buf->h, offset) ) return -EFAULT; if ( op.pad ) @@ -51,6 +117,16 @@ int do_one_iommu_op(xen_iommu_op_buf_t *buf) if ( rc ) return rc; + if ( op.op >= ARRAY_SIZE(op_size) ) + return -EOPNOTSUPP; + + size = op_size[array_index_nospec(op.op, ARRAY_SIZE(op_size))]; + if ( buf->size < offset + size ) + return -EFAULT; + + if ( copy_from_guest_offset((void *)&op.u, buf->h, offset, size) ) + return -EFAULT; + iommu_op(&op); if ( __copy_field_to_guest(guest_handle_cast(buf->h, xen_iommu_op_t), @@ -102,14 +178,23 @@ long do_iommu_op(unsigned int nr_bufs, int compat_one_iommu_op(compat_iommu_op_buf_t *buf) { - compat_iommu_op_t cmp; + compat_iommu_op_t cmp = {}; + size_t offset; + static const size_t op_size[] = { + [XEN_IOMMUOP_query_reserved] = sizeof(struct compat_iommu_op_query_reserved), + }; + size_t size; xen_iommu_op_t nat; + unsigned int u; + int32_t status; int rc; - if ( buf->size < sizeof(cmp) ) + offset = offsetof(struct compat_iommu_op, u); + + if ( buf->size < offset ) return -EFAULT; - if ( copy_from_compat((void *)&cmp, buf->h, sizeof(cmp)) ) + if ( copy_from_compat((void *)&cmp, buf->h, offset) ) return -EFAULT; if ( cmp.pad ) @@ -119,12 +204,85 @@ int compat_one_iommu_op(compat_iommu_op_buf_t *buf) if ( rc ) return rc; + if ( cmp.op >= ARRAY_SIZE(op_size) ) + return -EOPNOTSUPP; + + size = op_size[array_index_nospec(cmp.op, ARRAY_SIZE(op_size))]; + if ( buf->size < offset + size ) + return -EFAULT; + + if ( copy_from_compat_offset((void *)&cmp.u, buf->h, offset, size) ) + return -EFAULT; + + /* + * The xlat magic doesn't quite know how to handle the union so + * we need to fix things up here. + */ +#define XLAT_iommu_op_u_query_reserved XEN_IOMMUOP_query_reserved + u = cmp.op; + +#define XLAT_iommu_op_query_reserved_HNDL_ranges(_d_, _s_) \ + do \ + { \ + if ( !compat_handle_is_null((_s_)->ranges) ) \ + { \ + unsigned int *nr_entries = COMPAT_ARG_XLAT_VIRT_BASE; \ + xen_iommu_reserved_range_t *ranges = \ + (void *)(nr_entries + 1); \ + \ + if ( sizeof(*nr_entries) + \ + (sizeof(*ranges) * (_s_)->nr_entries) > \ + COMPAT_ARG_XLAT_SIZE ) \ + return -E2BIG; \ + \ + *nr_entries = (_s_)->nr_entries; \ + set_xen_guest_handle((_d_)->ranges, ranges); \ + } \ + else \ + set_xen_guest_handle((_d_)->ranges, NULL); \ + } while (false) + XLAT_iommu_op(&nat, &cmp); +#undef XLAT_iommu_op_query_reserved_HNDL_ranges + iommu_op(&nat); + status = nat.status; + +#define XLAT_iommu_op_query_reserved_HNDL_ranges(_d_, _s_) \ + do \ + { \ + if ( !compat_handle_is_null((_d_)->ranges) ) \ + { \ + unsigned int *nr_entries = COMPAT_ARG_XLAT_VIRT_BASE; \ + xen_iommu_reserved_range_t *ranges = \ + (void *)(nr_entries + 1); \ + unsigned int j; \ + \ + for ( j = 0; \ + j < min_t(unsigned int, (_d_)->nr_entries, \ + *nr_entries); \ + j++ ) \ + { \ + compat_iommu_reserved_range_t range; \ + \ + XLAT_iommu_reserved_range(&range, &ranges[j]); \ + \ + if ( __copy_to_compat_offset((_d_)->ranges, j, \ + &range, 1) ) \ + status = -EFAULT; \ + } \ + } \ + } while (false) + XLAT_iommu_op(&cmp, &nat); + /* status will have been modified if __copy_to_compat_offset() failed */ + cmp.status = status; + +#undef XLAT_iommu_op_query_reserved_HNDL_ranges + if ( __copy_field_to_compat(compat_handle_cast(buf->h, compat_iommu_op_t), &cmp, status) ) diff --git a/xen/include/public/iommu_op.h b/xen/include/public/iommu_op.h index c3b68f665a..02213c12a4 100644 --- a/xen/include/public/iommu_op.h +++ b/xen/include/public/iommu_op.h @@ -25,11 +25,50 @@ #include "xen.h" +typedef unsigned long xen_bfn_t; + +/* Structure describing a single range reserved in the IOMMU */ +struct xen_iommu_reserved_range { + xen_bfn_t start_bfn; + unsigned int nr_frames; + unsigned int pad; +}; +typedef struct xen_iommu_reserved_range xen_iommu_reserved_range_t; +DEFINE_XEN_GUEST_HANDLE(xen_iommu_reserved_range_t); + +/* + * XEN_IOMMUOP_query_reserved: Query ranges reserved in the IOMMU. + */ +#define XEN_IOMMUOP_query_reserved 1 + +struct xen_iommu_op_query_reserved { + /* + * IN/OUT - On entry this is the number of entries available + * in the ranges array below. + * On exit this is the actual number of reserved ranges. + */ + unsigned int nr_entries; + unsigned int pad; + /* + * OUT - This array is populated with reserved ranges. If it is + * not sufficiently large then available entries are populated, + * but the op status code will be set to -ENOBUFS. + * It is permissable to set this to NULL if nr_entries is also + * set to zero. In this case, on exit, nr_entries will still be + * set to the actual number of reserved ranges but the status + * code will be set to zero. + */ + XEN_GUEST_HANDLE(xen_iommu_reserved_range_t) ranges; +}; + struct xen_iommu_op { uint16_t op; /* op type */ uint16_t pad; int32_t status; /* op completion status: */ /* 0 for success otherwise, negative errno */ + union { + struct xen_iommu_op_query_reserved query_reserved; + } u; }; typedef struct xen_iommu_op xen_iommu_op_t; DEFINE_XEN_GUEST_HANDLE(xen_iommu_op_t); diff --git a/xen/include/xlat.lst b/xen/include/xlat.lst index 3b15c18c4e..93bcf4b4d0 100644 --- a/xen/include/xlat.lst +++ b/xen/include/xlat.lst @@ -79,6 +79,8 @@ ? vcpu_hvm_x86_64 hvm/hvm_vcpu.h ! iommu_op iommu_op.h ! iommu_op_buf iommu_op.h +! iommu_op_query_reserved iommu_op.h +! iommu_reserved_range iommu_op.h ? kexec_exec kexec.h ! kexec_image kexec.h ! kexec_range 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 |