From mboxrd@z Thu Jan 1 00:00:00 1970 From: Julien Grall Subject: [PATCH v2 13/21] xen/dts: Add hypercalls to retrieve device node information Date: Thu, 31 Jul 2014 16:00:44 +0100 Message-ID: <1406818852-31856-14-git-send-email-julien.grall@linaro.org> References: <1406818852-31856-1-git-send-email-julien.grall@linaro.org> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: Received: from mail6.bemta14.messagelabs.com ([193.109.254.103]) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1XCrr7-0000hf-Kt for xen-devel@lists.xenproject.org; Thu, 31 Jul 2014 15:01:21 +0000 Received: by mail-wi0-f171.google.com with SMTP id hi2so9429557wib.16 for ; Thu, 31 Jul 2014 08:01:18 -0700 (PDT) In-Reply-To: <1406818852-31856-1-git-send-email-julien.grall@linaro.org> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: xen-devel@lists.xenproject.org Cc: ian.campbell@citrix.com, tim@xen.org, Julien Grall , Ian Jackson , stefano.stabellini@citrix.com, Jan Beulich List-Id: xen-devel@lists.xenproject.org DOM0 doesn't provide a generic way to get information about a device tree node. If we want to do it in userspace, we will have to duplicate the MMIO/IRQ translation from Xen. Therefore, we can let the hypervisor doing the job for us and get nearly all the informations. This new domctl operation will let the toolstack get the IRQ/MMIO regions and the compatible string. Most the device node can be described with only theses 3 items. If we need to add a specific properties, then we will have to implement it in userspace (some idea was to use a configuration file describing the additional properties). The hypercall is divided in 4 parts: - GET_INFO: get the numbers of IRQ/MMIO and the size of the compatible string; - GET_IRQ: get the IRQ by index. If the IRQ is not routable (i.e not an SPIs), the errno will be set to -EINVAL; - GET_MMIO: get the MMIO range by index. If the base and the size of is not page-aligned, the errno will be set to -EINVAL; - GET_COMPAT: get the compatible string All the information will be accessible if the device is not used by Xen and protected by an IOMMU. Signed-off-by: Julien Grall Cc: Ian Jackson Cc: Jan Beulich --- I'm wondering if we can let the toolstack retrieve device information for every device not used by Xen. This would allow embedded guys using passthrough "easily" when their devices are not under an IOMMU. TODO: Missing XSM stubs for this new DOMCTL. I'm not sure how to deal with it Changes in v2: - is_routable_irq has been renamed to is_assignable_irq - rename clen field of the compatible structure into len - use xen_pfn_t for both mfn and nr_mfn fields - move as a DOMCTL op rather than PHYSDEV one. So it will be easy to modify the interface in the future. Hence, this is aim to be used only by the toolstack --- tools/libxc/xc_domain.c | 136 +++++++++++++++++++++++++++++++++++++++++ tools/libxc/xenctrl.h | 38 ++++++++++++ xen/common/device_tree.c | 113 ++++++++++++++++++++++++++++++++++ xen/common/domctl.c | 10 +++ xen/include/public/domctl.h | 40 ++++++++++++ xen/include/xen/device_tree.h | 3 + 6 files changed, 340 insertions(+) diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c index 1348905..4cc0474 100644 --- a/tools/libxc/xc_domain.c +++ b/tools/libxc/xc_domain.c @@ -2145,6 +2145,142 @@ int xc_domain_set_max_evtchn(xc_interface *xch, uint32_t domid, return do_domctl(xch, &domctl); } +int xc_dtdev_getinfo(xc_interface *xch, + char *path, + xc_dtdev_info_t *info) +{ + int rc; + size_t size = strlen(path); + DECLARE_DOMCTL; + struct xen_domctl_dtdev_op *op = &domctl.u.dtdev_op; + DECLARE_HYPERCALL_BOUNCE(path, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); + + if ( xc_hypercall_bounce_pre(xch, path) ) + return -1; + + domctl.cmd = XEN_DOMCTL_dtdev_op; + op->op = DOMCTL_DTDEV_GET_INFO; + op->plen = size; + set_xen_guest_handle(op->path, path); + + rc = do_domctl(xch, &domctl); + + xc_hypercall_bounce_post(xch, path); + + if ( !rc ) + { + info->num_irqs = op->u.info.num_irqs; + info->num_mmios = op->u.info.num_mmios; + info->compat_len = op->u.info.compat_len; + } + + return rc; +} + +int xc_dtdev_getirq(xc_interface *xch, + char *path, + uint32_t index, + xc_dtdev_irq_t *irq) +{ + int rc; + size_t size = strlen(path); + DECLARE_DOMCTL; + struct xen_domctl_dtdev_op *op = &domctl.u.dtdev_op; + DECLARE_HYPERCALL_BOUNCE(path, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); + + if ( xc_hypercall_bounce_pre(xch, path) ) + return -1; + + domctl.cmd = XEN_DOMCTL_dtdev_op; + op->op = DOMCTL_DTDEV_GET_IRQ; + op->plen = size; + op->index = index; + set_xen_guest_handle(op->path, path); + + rc = do_domctl(xch, &domctl); + + xc_hypercall_bounce_post(xch, path); + + if ( !rc ) + { + irq->irq = op->u.irq.irq; + irq->type = op->u.irq.type; + } + + return rc; +} + +int xc_dtdev_getmmio(xc_interface *xch, + char *path, + uint32_t index, + xc_dtdev_mmio_t *mmio) +{ + int rc; + size_t size = strlen(path); + DECLARE_DOMCTL; + struct xen_domctl_dtdev_op *op = &domctl.u.dtdev_op; + DECLARE_HYPERCALL_BOUNCE(path, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); + + if ( xc_hypercall_bounce_pre(xch, path) ) + return -1; + + op->op = DOMCTL_DTDEV_GET_MMIO; + op->plen = size; + op->index = index; + set_xen_guest_handle(op->path, path); + + rc = do_domctl(xch, &domctl); + + xc_hypercall_bounce_post(xch, path); + + if ( !rc ) + { + mmio->mfn = op->u.mmio.mfn; + mmio->nr_mfn = op->u.mmio.nr_mfn; + } + + return rc; +} + +int xc_dtdev_getcompat(xc_interface *xch, + char *path, + char *compat, + uint32_t *clen) +{ + int rc; + size_t size = strlen(path); + DECLARE_DOMCTL; + struct xen_domctl_dtdev_op *op = &domctl.u.dtdev_op; + DECLARE_HYPERCALL_BOUNCE(path, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); + DECLARE_HYPERCALL_BOUNCE(compat, *clen, XC_HYPERCALL_BUFFER_BOUNCE_OUT); + + if ( xc_hypercall_bounce_pre(xch, path) ) + return -1; + + rc = -1; + if ( xc_hypercall_bounce_pre(xch, compat) ) + goto out; + + op->op = DOMCTL_DTDEV_GET_COMPAT; + op->plen = size; + set_xen_guest_handle(op->path, path); + + op->u.compat.len = *clen; + set_xen_guest_handle(op->u.compat.compat, compat); + + rc = do_domctl(xch, &domctl); + + if ( !rc ) + *clen = op->u.compat.len; + + xc_hypercall_bounce_post(xch, compat); + +out: + xc_hypercall_bounce_post(xch, path); + + return rc; +} + /* * Local variables: * mode: C diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h index cdda4e5..c549753 100644 --- a/tools/libxc/xenctrl.h +++ b/tools/libxc/xenctrl.h @@ -1208,6 +1208,44 @@ int xc_physdev_pci_access_modify(xc_interface *xch, int func, int enable); +typedef struct xc_dtdev_info { + uint32_t num_irqs; + uint32_t num_mmios; + uint32_t compat_len; +} xc_dtdev_info_t; + +/* Retrieve device tree information */ + +int xc_dtdev_getinfo(xc_interface *xch, + char *path, + xc_dtdev_info_t *info); + +typedef struct xc_dtdev_irq { + uint32_t irq; + /* TODO: Maybe an enum here? */ + uint32_t type; +} xc_dtdev_irq_t; + +int xc_dtdev_getirq(xc_interface *xch, + char *path, + uint32_t index, + xc_dtdev_irq_t *irq); + +typedef struct xc_dtdev_mmio { + uint64_t mfn; + uint64_t nr_mfn; +} xc_dtdev_mmio_t; + +int xc_dtdev_getmmio(xc_interface *xch, + char *path, + uint32_t index, + xc_dtdev_mmio_t *mmio); + +int xc_dtdev_getcompat(xc_interface *xch, + char *path, + char *compat, + uint32_t *clen); + int xc_readconsolering(xc_interface *xch, char *buffer, unsigned int *pnr_chars, diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c index 071109e..2f6c154 100644 --- a/xen/common/device_tree.c +++ b/xen/common/device_tree.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include const void *device_tree_flattened; dt_irq_xlate_func dt_irq_xlate; @@ -1642,6 +1644,117 @@ void __init dt_unflatten_host_device_tree(void) dt_alias_scan(); } +/* TODO: I think Xen needs a bit of caching in each device node to get the + * information in constant time. + * For now, Xen has to translate IRQs/MMIOs every time + */ +int dt_do_domctl(struct xen_domctl *domctl) +{ + struct xen_domctl_dtdev_op *info = &domctl->u.dtdev_op; + struct dt_device_node *dev; + int ret; + + ret = dt_find_node_by_gpath(info->path, info->plen, &dev); + if ( ret ) + return ret; + + /* Only allow access to protected device and not used by Xen */ + if ( !dt_device_is_protected(dev) || dt_device_used_by(dev) == DOMID_XEN ) + return -EACCES; + + switch ( info->op ) + { + case DOMCTL_DTDEV_GET_INFO: + { + const struct dt_property *compat; + + compat = dt_find_property(dev, "compatible", NULL); + /* Hopefully, this case should never happen, print error + * if it occurs + */ + if ( !compat ) + { + dprintk(XENLOG_G_ERR, "Unable to find compatible node for %s\n", + dt_node_full_name(dev)); + return -EBADFD; + } + + info->u.info.num_irqs = dt_number_of_irq(dev); + info->u.info.num_mmios = dt_number_of_address(dev); + info->u.info.compat_len = compat->length; + } + break; + + case DOMCTL_DTDEV_GET_IRQ: + { + struct dt_irq irq; + + ret = dt_device_get_irq(dev, info->index, &irq); + if ( ret ) + return ret; + + /* Check if Xen is able to assign the IRQ to the guest */ + if ( !is_assignable_irq(irq.irq) ) + return -EINVAL; + + info->u.irq.irq = irq.irq; + /* TODO: Translate the type into an exportable value */ + info->u.irq.type = irq.type; + } + break; + + case DOMCTL_DTDEV_GET_MMIO: + { + uint64_t addr, size; + + ret = dt_device_get_address(dev, info->index, &addr, &size); + if ( ret ) + return ret; + + /* Make sure the address and the size are page aligned. + * If not, we may passthrough MMIO regions which may belong + * to another device. Deny it! + */ + if ( (addr & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) + { + dprintk(XENLOG_ERR, "%s: contain non-page aligned range:" + " addr = 0x%"PRIx64" size = 0x%"PRIx64"\n", + dt_node_full_name(dev), addr, size); + return -EINVAL; + } + + info->u.mmio.mfn = paddr_to_pfn(addr); + info->u.mmio.nr_mfn = paddr_to_pfn(size); + } + break; + + case DOMCTL_DTDEV_GET_COMPAT: + { + const struct dt_property *compat; + + compat = dt_find_property(dev, "compatible", NULL); + if ( !compat || !compat->length ) + return -ENOENT; + + if ( info->u.compat.len < compat->length ) + return -ENOSPC; + + if ( copy_to_guest(info->u.compat.compat, compat->value, + compat->length) != 0 ) + return -EFAULT; + + info->u.compat.len = compat->length; + } + break; + + default: + return -ENOSYS; + } + + + return 0; +} + /* * Local variables: * mode: C diff --git a/xen/common/domctl.c b/xen/common/domctl.c index 04ecd53..0c9926a 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -315,6 +315,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) case XEN_DOMCTL_createdomain: case XEN_DOMCTL_getdomaininfo: case XEN_DOMCTL_test_assign_device: + case XEN_DOMCTL_dtdev_op: d = NULL; break; default: @@ -1017,6 +1018,15 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) } break; +#ifdef HAS_DEVICE_TREE + case XEN_DOMCTL_dtdev_op: + { + ret = dt_do_domctl(op); + copyback = 1; + } + break; +#endif + default: ret = arch_do_domctl(op, d, u_domctl); break; diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index b5f2ed7..f7bad9b 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -946,6 +946,44 @@ typedef struct xen_domctl_vcpu_msrs xen_domctl_vcpu_msrs_t; DEFINE_XEN_GUEST_HANDLE(xen_domctl_vcpu_msrs_t); #endif +/* Device Tree: Retrieve informations about a device node */ +struct xen_domctl_dtdev_op { + /* IN */ + uint32_t plen; /* Length of the path */ + XEN_GUEST_HANDLE(char) path; /* Path to the device tree node */ +#define DOMCTL_DTDEV_GET_INFO 0 +#define DOMCTL_DTDEV_GET_IRQ 1 +#define DOMCTL_DTDEV_GET_MMIO 2 +#define DOMCTL_DTDEV_GET_COMPAT 3 + uint8_t op; + uint32_t pad0:24; + uint32_t index; /* Index for the IRQ/MMIO to retrieve */ + /* OUT */ + union { + struct { + uint32_t num_irqs; /* Number of IRQs */ + uint32_t num_mmios; /* Number of MMIOs */ + uint32_t compat_len; /* Length of the compatible string */ + } info; + struct { + /* TODO: Do we need to handle MSI-X? */ + uint32_t irq; /* IRQ number */ + /* TODO: Describe with defines the IRQ type */ + uint32_t type; /* IRQ type (i.e edge, level...) */ + } irq; + struct { + xen_pfn_t mfn; + xen_pfn_t nr_mfn; + } mmio; + struct { + uint32_t len; /* IN: Size of buffer. OUT: Size copied */ + XEN_GUEST_HANDLE_64(char) compat; + } compat; + } u; +}; +typedef struct xen_domctl_dtdev_op xen_domctl_dtdev_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_domctl_dtdev_op_t); + struct xen_domctl { uint32_t cmd; #define XEN_DOMCTL_createdomain 1 @@ -1019,6 +1057,7 @@ struct xen_domctl { #define XEN_DOMCTL_get_vcpu_msrs 72 #define XEN_DOMCTL_set_vcpu_msrs 73 #define XEN_DOMCTL_configure_domain 74 +#define XEN_DOMCTL_dtdev_op 75 #define XEN_DOMCTL_gdbsx_guestmemio 1000 #define XEN_DOMCTL_gdbsx_pausevcpu 1001 #define XEN_DOMCTL_gdbsx_unpausevcpu 1002 @@ -1080,6 +1119,7 @@ struct xen_domctl { struct xen_domctl_gdbsx_memio gdbsx_guest_memio; struct xen_domctl_set_broken_page_p2m set_broken_page_p2m; struct xen_domctl_cacheflush cacheflush; + struct xen_domctl_dtdev_op dtdev_op; struct xen_domctl_gdbsx_pauseunp_vcpu gdbsx_pauseunp_vcpu; struct xen_domctl_gdbsx_domstatus gdbsx_domstatus; uint8_t pad[128]; diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h index 6dc34df..8481939 100644 --- a/xen/include/xen/device_tree.h +++ b/xen/include/xen/device_tree.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -669,6 +670,8 @@ int dt_parse_phandle_with_args(const struct dt_device_node *np, const char *cells_name, int index, struct dt_phandle_args *out_args); +int dt_do_domctl(struct xen_domctl *domctl); + #endif /* __XEN_DEVICE_TREE_H */ /* -- 1.7.10.4