diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-27 21:35:37 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-27 21:35:37 +0300 |
commit | 08fd8c17686c6b09fa410a26d516548dd80ff147 (patch) | |
tree | 0d8c17e70a94518a301e85fc7b23fbc09311068c /drivers/xen/arm-device.c | |
parent | e831101a73fbc8339ef1d1909dad3ef64f089e70 (diff) | |
parent | d34c30cc1fa80f509500ff192ea6bc7d30671061 (diff) | |
download | linux-08fd8c17686c6b09fa410a26d516548dd80ff147.tar.xz |
Merge tag 'for-linus-4.8-rc0-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip
Pull xen updates from David Vrabel:
"Features and fixes for 4.8-rc0:
- ACPI support for guests on ARM platforms.
- Generic steal time support for arm and x86.
- Support cases where kernel cpu is not Xen VCPU number (e.g., if
in-guest kexec is used).
- Use the system workqueue instead of a custom workqueue in various
places"
* tag 'for-linus-4.8-rc0-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip: (47 commits)
xen: add static initialization of steal_clock op to xen_time_ops
xen/pvhvm: run xen_vcpu_setup() for the boot CPU
xen/evtchn: use xen_vcpu_id mapping
xen/events: fifo: use xen_vcpu_id mapping
xen/events: use xen_vcpu_id mapping in events_base
x86/xen: use xen_vcpu_id mapping when pointing vcpu_info to shared_info
x86/xen: use xen_vcpu_id mapping for HYPERVISOR_vcpu_op
xen: introduce xen_vcpu_id mapping
x86/acpi: store ACPI ids from MADT for future usage
x86/xen: update cpuid.h from Xen-4.7
xen/evtchn: add IOCTL_EVTCHN_RESTRICT
xen-blkback: really don't leak mode property
xen-blkback: constify instance of "struct attribute_group"
xen-blkfront: prefer xenbus_scanf() over xenbus_gather()
xen-blkback: prefer xenbus_scanf() over xenbus_gather()
xen: support runqueue steal time on xen
arm/xen: add support for vm_assist hypercall
xen: update xen headers
xen-pciback: drop superfluous variables
xen-pciback: short-circuit read path used for merging write values
...
Diffstat (limited to 'drivers/xen/arm-device.c')
-rw-r--r-- | drivers/xen/arm-device.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/drivers/xen/arm-device.c b/drivers/xen/arm-device.c new file mode 100644 index 000000000000..778acf80aacb --- /dev/null +++ b/drivers/xen/arm-device.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2015, Linaro Limited, Shannon Zhao + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <xen/xen.h> +#include <xen/page.h> +#include <xen/interface/memory.h> +#include <asm/xen/hypervisor.h> +#include <asm/xen/hypercall.h> + +static int xen_unmap_device_mmio(const struct resource *resources, + unsigned int count) +{ + unsigned int i, j, nr; + int rc = 0; + const struct resource *r; + struct xen_remove_from_physmap xrp; + + for (i = 0; i < count; i++) { + r = &resources[i]; + nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE); + if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0)) + continue; + + for (j = 0; j < nr; j++) { + xrp.domid = DOMID_SELF; + xrp.gpfn = XEN_PFN_DOWN(r->start) + j; + rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, + &xrp); + if (rc) + return rc; + } + } + + return rc; +} + +static int xen_map_device_mmio(const struct resource *resources, + unsigned int count) +{ + unsigned int i, j, nr; + int rc = 0; + const struct resource *r; + xen_pfn_t *gpfns; + xen_ulong_t *idxs; + int *errs; + struct xen_add_to_physmap_range xatp; + + for (i = 0; i < count; i++) { + r = &resources[i]; + nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE); + if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0)) + continue; + + gpfns = kzalloc(sizeof(xen_pfn_t) * nr, GFP_KERNEL); + idxs = kzalloc(sizeof(xen_ulong_t) * nr, GFP_KERNEL); + errs = kzalloc(sizeof(int) * nr, GFP_KERNEL); + if (!gpfns || !idxs || !errs) { + kfree(gpfns); + kfree(idxs); + kfree(errs); + rc = -ENOMEM; + goto unmap; + } + + for (j = 0; j < nr; j++) { + /* + * The regions are always mapped 1:1 to DOM0 and this is + * fine because the memory map for DOM0 is the same as + * the host (except for the RAM). + */ + gpfns[j] = XEN_PFN_DOWN(r->start) + j; + idxs[j] = XEN_PFN_DOWN(r->start) + j; + } + + xatp.domid = DOMID_SELF; + xatp.size = nr; + xatp.space = XENMAPSPACE_dev_mmio; + + set_xen_guest_handle(xatp.gpfns, gpfns); + set_xen_guest_handle(xatp.idxs, idxs); + set_xen_guest_handle(xatp.errs, errs); + + rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp); + kfree(gpfns); + kfree(idxs); + kfree(errs); + if (rc) + goto unmap; + } + + return rc; + +unmap: + xen_unmap_device_mmio(resources, i); + return rc; +} + +static int xen_platform_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct platform_device *pdev = to_platform_device(data); + int r = 0; + + if (pdev->num_resources == 0 || pdev->resource == NULL) + return NOTIFY_OK; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + r = xen_map_device_mmio(pdev->resource, pdev->num_resources); + break; + case BUS_NOTIFY_DEL_DEVICE: + r = xen_unmap_device_mmio(pdev->resource, pdev->num_resources); + break; + default: + return NOTIFY_DONE; + } + if (r) + dev_err(&pdev->dev, "Platform: Failed to %s device %s MMIO!\n", + action == BUS_NOTIFY_ADD_DEVICE ? "map" : + (action == BUS_NOTIFY_DEL_DEVICE ? "unmap" : "?"), + pdev->name); + + return NOTIFY_OK; +} + +static struct notifier_block platform_device_nb = { + .notifier_call = xen_platform_notifier, +}; + +static int __init register_xen_platform_notifier(void) +{ + if (!xen_initial_domain() || acpi_disabled) + return 0; + + return bus_register_notifier(&platform_bus_type, &platform_device_nb); +} + +arch_initcall(register_xen_platform_notifier); + +#ifdef CONFIG_ARM_AMBA +#include <linux/amba/bus.h> + +static int xen_amba_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct amba_device *adev = to_amba_device(data); + int r = 0; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + r = xen_map_device_mmio(&adev->res, 1); + break; + case BUS_NOTIFY_DEL_DEVICE: + r = xen_unmap_device_mmio(&adev->res, 1); + break; + default: + return NOTIFY_DONE; + } + if (r) + dev_err(&adev->dev, "AMBA: Failed to %s device %s MMIO!\n", + action == BUS_NOTIFY_ADD_DEVICE ? "map" : + (action == BUS_NOTIFY_DEL_DEVICE ? "unmap" : "?"), + adev->dev.init_name); + + return NOTIFY_OK; +} + +static struct notifier_block amba_device_nb = { + .notifier_call = xen_amba_notifier, +}; + +static int __init register_xen_amba_notifier(void) +{ + if (!xen_initial_domain() || acpi_disabled) + return 0; + + return bus_register_notifier(&amba_bustype, &amba_device_nb); +} + +arch_initcall(register_xen_amba_notifier); +#endif |