brintos

brintos / linux-shallow public Read only

0
0
Text · 16.2 KiB · e53757d Raw
696 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013 - Virtual Open Systems4 * Author: Antonios Motakis <a.motakis@virtualopensystems.com>5 */6 7#define dev_fmt(fmt)	"VFIO: " fmt8 9#include <linux/device.h>10#include <linux/acpi.h>11#include <linux/iommu.h>12#include <linux/module.h>13#include <linux/mutex.h>14#include <linux/pm_runtime.h>15#include <linux/slab.h>16#include <linux/types.h>17#include <linux/uaccess.h>18#include <linux/vfio.h>19 20#include "vfio_platform_private.h"21 22#define DRIVER_VERSION  "0.10"23#define DRIVER_AUTHOR   "Antonios Motakis <a.motakis@virtualopensystems.com>"24#define DRIVER_DESC     "VFIO platform base module"25 26#define VFIO_PLATFORM_IS_ACPI(vdev) ((vdev)->acpihid != NULL)27 28static LIST_HEAD(reset_list);29static DEFINE_MUTEX(driver_lock);30 31static vfio_platform_reset_fn_t vfio_platform_lookup_reset(const char *compat,32					struct module **module)33{34	struct vfio_platform_reset_node *iter;35	vfio_platform_reset_fn_t reset_fn = NULL;36 37	mutex_lock(&driver_lock);38	list_for_each_entry(iter, &reset_list, link) {39		if (!strcmp(iter->compat, compat) &&40			try_module_get(iter->owner)) {41			*module = iter->owner;42			reset_fn = iter->of_reset;43			break;44		}45	}46	mutex_unlock(&driver_lock);47	return reset_fn;48}49 50static int vfio_platform_acpi_probe(struct vfio_platform_device *vdev,51				    struct device *dev)52{53	struct acpi_device *adev;54 55	if (acpi_disabled)56		return -ENOENT;57 58	adev = ACPI_COMPANION(dev);59	if (!adev) {60		dev_err(dev, "ACPI companion device not found for %s\n",61			vdev->name);62		return -ENODEV;63	}64 65#ifdef CONFIG_ACPI66	vdev->acpihid = acpi_device_hid(adev);67#endif68	return WARN_ON(!vdev->acpihid) ? -EINVAL : 0;69}70 71static int vfio_platform_acpi_call_reset(struct vfio_platform_device *vdev,72				  const char **extra_dbg)73{74#ifdef CONFIG_ACPI75	struct device *dev = vdev->device;76	acpi_handle handle = ACPI_HANDLE(dev);77	acpi_status acpi_ret;78 79	acpi_ret = acpi_evaluate_object(handle, "_RST", NULL, NULL);80	if (ACPI_FAILURE(acpi_ret)) {81		if (extra_dbg)82			*extra_dbg = acpi_format_exception(acpi_ret);83		return -EINVAL;84	}85 86	return 0;87#else88	return -ENOENT;89#endif90}91 92static bool vfio_platform_acpi_has_reset(struct vfio_platform_device *vdev)93{94#ifdef CONFIG_ACPI95	struct device *dev = vdev->device;96	acpi_handle handle = ACPI_HANDLE(dev);97 98	return acpi_has_method(handle, "_RST");99#else100	return false;101#endif102}103 104static bool vfio_platform_has_reset(struct vfio_platform_device *vdev)105{106	if (VFIO_PLATFORM_IS_ACPI(vdev))107		return vfio_platform_acpi_has_reset(vdev);108 109	return vdev->of_reset ? true : false;110}111 112static int vfio_platform_get_reset(struct vfio_platform_device *vdev)113{114	if (VFIO_PLATFORM_IS_ACPI(vdev))115		return vfio_platform_acpi_has_reset(vdev) ? 0 : -ENOENT;116 117	vdev->of_reset = vfio_platform_lookup_reset(vdev->compat,118						    &vdev->reset_module);119	if (!vdev->of_reset) {120		request_module("vfio-reset:%s", vdev->compat);121		vdev->of_reset = vfio_platform_lookup_reset(vdev->compat,122							&vdev->reset_module);123	}124 125	return vdev->of_reset ? 0 : -ENOENT;126}127 128static void vfio_platform_put_reset(struct vfio_platform_device *vdev)129{130	if (VFIO_PLATFORM_IS_ACPI(vdev))131		return;132 133	if (vdev->of_reset)134		module_put(vdev->reset_module);135}136 137static int vfio_platform_regions_init(struct vfio_platform_device *vdev)138{139	int cnt = 0, i;140 141	while (vdev->get_resource(vdev, cnt))142		cnt++;143 144	vdev->regions = kcalloc(cnt, sizeof(struct vfio_platform_region),145				GFP_KERNEL_ACCOUNT);146	if (!vdev->regions)147		return -ENOMEM;148 149	for (i = 0; i < cnt;  i++) {150		struct resource *res =151			vdev->get_resource(vdev, i);152 153		vdev->regions[i].addr = res->start;154		vdev->regions[i].size = resource_size(res);155		vdev->regions[i].flags = 0;156 157		switch (resource_type(res)) {158		case IORESOURCE_MEM:159			vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_MMIO;160			vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ;161			if (!(res->flags & IORESOURCE_READONLY))162				vdev->regions[i].flags |=163					VFIO_REGION_INFO_FLAG_WRITE;164 165			/*166			 * Only regions addressed with PAGE granularity may be167			 * MMAPed securely.168			 */169			if (!(vdev->regions[i].addr & ~PAGE_MASK) &&170					!(vdev->regions[i].size & ~PAGE_MASK))171				vdev->regions[i].flags |=172					VFIO_REGION_INFO_FLAG_MMAP;173 174			break;175		case IORESOURCE_IO:176			vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_PIO;177			break;178		default:179			goto err;180		}181	}182 183	vdev->num_regions = cnt;184 185	return 0;186err:187	kfree(vdev->regions);188	return -EINVAL;189}190 191static void vfio_platform_regions_cleanup(struct vfio_platform_device *vdev)192{193	int i;194 195	for (i = 0; i < vdev->num_regions; i++)196		iounmap(vdev->regions[i].ioaddr);197 198	vdev->num_regions = 0;199	kfree(vdev->regions);200}201 202static int vfio_platform_call_reset(struct vfio_platform_device *vdev,203				    const char **extra_dbg)204{205	if (VFIO_PLATFORM_IS_ACPI(vdev)) {206		dev_info(vdev->device, "reset\n");207		return vfio_platform_acpi_call_reset(vdev, extra_dbg);208	} else if (vdev->of_reset) {209		dev_info(vdev->device, "reset\n");210		return vdev->of_reset(vdev);211	}212 213	dev_warn(vdev->device, "no reset function found!\n");214	return -EINVAL;215}216 217void vfio_platform_close_device(struct vfio_device *core_vdev)218{219	struct vfio_platform_device *vdev =220		container_of(core_vdev, struct vfio_platform_device, vdev);221	const char *extra_dbg = NULL;222	int ret;223 224	ret = vfio_platform_call_reset(vdev, &extra_dbg);225	if (WARN_ON(ret && vdev->reset_required)) {226		dev_warn(227			vdev->device,228			"reset driver is required and reset call failed in release (%d) %s\n",229			ret, extra_dbg ? extra_dbg : "");230	}231	pm_runtime_put(vdev->device);232	vfio_platform_regions_cleanup(vdev);233	vfio_platform_irq_cleanup(vdev);234}235EXPORT_SYMBOL_GPL(vfio_platform_close_device);236 237int vfio_platform_open_device(struct vfio_device *core_vdev)238{239	struct vfio_platform_device *vdev =240		container_of(core_vdev, struct vfio_platform_device, vdev);241	const char *extra_dbg = NULL;242	int ret;243 244	ret = vfio_platform_regions_init(vdev);245	if (ret)246		return ret;247 248	ret = vfio_platform_irq_init(vdev);249	if (ret)250		goto err_irq;251 252	ret = pm_runtime_get_sync(vdev->device);253	if (ret < 0)254		goto err_rst;255 256	ret = vfio_platform_call_reset(vdev, &extra_dbg);257	if (ret && vdev->reset_required) {258		dev_warn(259			vdev->device,260			"reset driver is required and reset call failed in open (%d) %s\n",261			ret, extra_dbg ? extra_dbg : "");262		goto err_rst;263	}264	return 0;265 266err_rst:267	pm_runtime_put(vdev->device);268	vfio_platform_irq_cleanup(vdev);269err_irq:270	vfio_platform_regions_cleanup(vdev);271	return ret;272}273EXPORT_SYMBOL_GPL(vfio_platform_open_device);274 275long vfio_platform_ioctl(struct vfio_device *core_vdev,276			 unsigned int cmd, unsigned long arg)277{278	struct vfio_platform_device *vdev =279		container_of(core_vdev, struct vfio_platform_device, vdev);280 281	unsigned long minsz;282 283	if (cmd == VFIO_DEVICE_GET_INFO) {284		struct vfio_device_info info;285 286		minsz = offsetofend(struct vfio_device_info, num_irqs);287 288		if (copy_from_user(&info, (void __user *)arg, minsz))289			return -EFAULT;290 291		if (info.argsz < minsz)292			return -EINVAL;293 294		if (vfio_platform_has_reset(vdev))295			vdev->flags |= VFIO_DEVICE_FLAGS_RESET;296		info.flags = vdev->flags;297		info.num_regions = vdev->num_regions;298		info.num_irqs = vdev->num_irqs;299 300		return copy_to_user((void __user *)arg, &info, minsz) ?301			-EFAULT : 0;302 303	} else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {304		struct vfio_region_info info;305 306		minsz = offsetofend(struct vfio_region_info, offset);307 308		if (copy_from_user(&info, (void __user *)arg, minsz))309			return -EFAULT;310 311		if (info.argsz < minsz)312			return -EINVAL;313 314		if (info.index >= vdev->num_regions)315			return -EINVAL;316 317		/* map offset to the physical address  */318		info.offset = VFIO_PLATFORM_INDEX_TO_OFFSET(info.index);319		info.size = vdev->regions[info.index].size;320		info.flags = vdev->regions[info.index].flags;321 322		return copy_to_user((void __user *)arg, &info, minsz) ?323			-EFAULT : 0;324 325	} else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {326		struct vfio_irq_info info;327 328		minsz = offsetofend(struct vfio_irq_info, count);329 330		if (copy_from_user(&info, (void __user *)arg, minsz))331			return -EFAULT;332 333		if (info.argsz < minsz)334			return -EINVAL;335 336		if (info.index >= vdev->num_irqs)337			return -EINVAL;338 339		info.flags = vdev->irqs[info.index].flags;340		info.count = vdev->irqs[info.index].count;341 342		return copy_to_user((void __user *)arg, &info, minsz) ?343			-EFAULT : 0;344 345	} else if (cmd == VFIO_DEVICE_SET_IRQS) {346		struct vfio_irq_set hdr;347		u8 *data = NULL;348		int ret = 0;349		size_t data_size = 0;350 351		minsz = offsetofend(struct vfio_irq_set, count);352 353		if (copy_from_user(&hdr, (void __user *)arg, minsz))354			return -EFAULT;355 356		ret = vfio_set_irqs_validate_and_prepare(&hdr, vdev->num_irqs,357						 vdev->num_irqs, &data_size);358		if (ret)359			return ret;360 361		if (data_size) {362			data = memdup_user((void __user *)(arg + minsz),363					    data_size);364			if (IS_ERR(data))365				return PTR_ERR(data);366		}367 368		mutex_lock(&vdev->igate);369 370		ret = vfio_platform_set_irqs_ioctl(vdev, hdr.flags, hdr.index,371						   hdr.start, hdr.count, data);372		mutex_unlock(&vdev->igate);373		kfree(data);374 375		return ret;376 377	} else if (cmd == VFIO_DEVICE_RESET) {378		return vfio_platform_call_reset(vdev, NULL);379	}380 381	return -ENOTTY;382}383EXPORT_SYMBOL_GPL(vfio_platform_ioctl);384 385static ssize_t vfio_platform_read_mmio(struct vfio_platform_region *reg,386				       char __user *buf, size_t count,387				       loff_t off)388{389	unsigned int done = 0;390 391	if (!reg->ioaddr) {392		reg->ioaddr =393			ioremap(reg->addr, reg->size);394 395		if (!reg->ioaddr)396			return -ENOMEM;397	}398 399	while (count) {400		size_t filled;401 402		if (count >= 4 && !(off % 4)) {403			u32 val;404 405			val = ioread32(reg->ioaddr + off);406			if (copy_to_user(buf, &val, 4))407				goto err;408 409			filled = 4;410		} else if (count >= 2 && !(off % 2)) {411			u16 val;412 413			val = ioread16(reg->ioaddr + off);414			if (copy_to_user(buf, &val, 2))415				goto err;416 417			filled = 2;418		} else {419			u8 val;420 421			val = ioread8(reg->ioaddr + off);422			if (copy_to_user(buf, &val, 1))423				goto err;424 425			filled = 1;426		}427 428 429		count -= filled;430		done += filled;431		off += filled;432		buf += filled;433	}434 435	return done;436err:437	return -EFAULT;438}439 440ssize_t vfio_platform_read(struct vfio_device *core_vdev,441			   char __user *buf, size_t count, loff_t *ppos)442{443	struct vfio_platform_device *vdev =444		container_of(core_vdev, struct vfio_platform_device, vdev);445	unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos);446	loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK;447 448	if (index >= vdev->num_regions)449		return -EINVAL;450 451	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ))452		return -EINVAL;453 454	if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)455		return vfio_platform_read_mmio(&vdev->regions[index],456							buf, count, off);457	else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)458		return -EINVAL; /* not implemented */459 460	return -EINVAL;461}462EXPORT_SYMBOL_GPL(vfio_platform_read);463 464static ssize_t vfio_platform_write_mmio(struct vfio_platform_region *reg,465					const char __user *buf, size_t count,466					loff_t off)467{468	unsigned int done = 0;469 470	if (!reg->ioaddr) {471		reg->ioaddr =472			ioremap(reg->addr, reg->size);473 474		if (!reg->ioaddr)475			return -ENOMEM;476	}477 478	while (count) {479		size_t filled;480 481		if (count >= 4 && !(off % 4)) {482			u32 val;483 484			if (copy_from_user(&val, buf, 4))485				goto err;486			iowrite32(val, reg->ioaddr + off);487 488			filled = 4;489		} else if (count >= 2 && !(off % 2)) {490			u16 val;491 492			if (copy_from_user(&val, buf, 2))493				goto err;494			iowrite16(val, reg->ioaddr + off);495 496			filled = 2;497		} else {498			u8 val;499 500			if (copy_from_user(&val, buf, 1))501				goto err;502			iowrite8(val, reg->ioaddr + off);503 504			filled = 1;505		}506 507		count -= filled;508		done += filled;509		off += filled;510		buf += filled;511	}512 513	return done;514err:515	return -EFAULT;516}517 518ssize_t vfio_platform_write(struct vfio_device *core_vdev, const char __user *buf,519			    size_t count, loff_t *ppos)520{521	struct vfio_platform_device *vdev =522		container_of(core_vdev, struct vfio_platform_device, vdev);523	unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos);524	loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK;525 526	if (index >= vdev->num_regions)527		return -EINVAL;528 529	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE))530		return -EINVAL;531 532	if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)533		return vfio_platform_write_mmio(&vdev->regions[index],534							buf, count, off);535	else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)536		return -EINVAL; /* not implemented */537 538	return -EINVAL;539}540EXPORT_SYMBOL_GPL(vfio_platform_write);541 542static int vfio_platform_mmap_mmio(struct vfio_platform_region region,543				   struct vm_area_struct *vma)544{545	u64 req_len, pgoff, req_start;546 547	req_len = vma->vm_end - vma->vm_start;548	pgoff = vma->vm_pgoff &549		((1U << (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT)) - 1);550	req_start = pgoff << PAGE_SHIFT;551 552	if (region.size < PAGE_SIZE || req_start + req_len > region.size)553		return -EINVAL;554 555	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);556	vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff;557 558	return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,559			       req_len, vma->vm_page_prot);560}561 562int vfio_platform_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma)563{564	struct vfio_platform_device *vdev =565		container_of(core_vdev, struct vfio_platform_device, vdev);566	unsigned int index;567 568	index = vma->vm_pgoff >> (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT);569 570	if (vma->vm_end < vma->vm_start)571		return -EINVAL;572	if (!(vma->vm_flags & VM_SHARED))573		return -EINVAL;574	if (index >= vdev->num_regions)575		return -EINVAL;576	if (vma->vm_start & ~PAGE_MASK)577		return -EINVAL;578	if (vma->vm_end & ~PAGE_MASK)579		return -EINVAL;580 581	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP))582		return -EINVAL;583 584	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ)585			&& (vma->vm_flags & VM_READ))586		return -EINVAL;587 588	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE)589			&& (vma->vm_flags & VM_WRITE))590		return -EINVAL;591 592	vma->vm_private_data = vdev;593 594	if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)595		return vfio_platform_mmap_mmio(vdev->regions[index], vma);596 597	else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)598		return -EINVAL; /* not implemented */599 600	return -EINVAL;601}602EXPORT_SYMBOL_GPL(vfio_platform_mmap);603 604static int vfio_platform_of_probe(struct vfio_platform_device *vdev,605			   struct device *dev)606{607	int ret;608 609	ret = device_property_read_string(dev, "compatible",610					  &vdev->compat);611	if (ret)612		dev_err(dev, "Cannot retrieve compat for %s\n", vdev->name);613 614	return ret;615}616 617/*618 * There can be two kernel build combinations. One build where619 * ACPI is not selected in Kconfig and another one with the ACPI Kconfig.620 *621 * In the first case, vfio_platform_acpi_probe will return since622 * acpi_disabled is 1. DT user will not see any kind of messages from623 * ACPI.624 *625 * In the second case, both DT and ACPI is compiled in but the system is626 * booting with any of these combinations.627 *628 * If the firmware is DT type, then acpi_disabled is 1. The ACPI probe routine629 * terminates immediately without any messages.630 *631 * If the firmware is ACPI type, then acpi_disabled is 0. All other checks are632 * valid checks. We cannot claim that this system is DT.633 */634int vfio_platform_init_common(struct vfio_platform_device *vdev)635{636	int ret;637	struct device *dev = vdev->vdev.dev;638 639	ret = vfio_platform_acpi_probe(vdev, dev);640	if (ret)641		ret = vfio_platform_of_probe(vdev, dev);642 643	if (ret)644		return ret;645 646	vdev->device = dev;647	mutex_init(&vdev->igate);648 649	ret = vfio_platform_get_reset(vdev);650	if (ret && vdev->reset_required) {651		dev_err(dev, "No reset function found for device %s\n",652			vdev->name);653		return ret;654	}655 656	return 0;657}658EXPORT_SYMBOL_GPL(vfio_platform_init_common);659 660void vfio_platform_release_common(struct vfio_platform_device *vdev)661{662	vfio_platform_put_reset(vdev);663}664EXPORT_SYMBOL_GPL(vfio_platform_release_common);665 666void __vfio_platform_register_reset(struct vfio_platform_reset_node *node)667{668	mutex_lock(&driver_lock);669	list_add(&node->link, &reset_list);670	mutex_unlock(&driver_lock);671}672EXPORT_SYMBOL_GPL(__vfio_platform_register_reset);673 674void vfio_platform_unregister_reset(const char *compat,675				    vfio_platform_reset_fn_t fn)676{677	struct vfio_platform_reset_node *iter, *temp;678 679	mutex_lock(&driver_lock);680	list_for_each_entry_safe(iter, temp, &reset_list, link) {681		if (!strcmp(iter->compat, compat) && (iter->of_reset == fn)) {682			list_del(&iter->link);683			break;684		}685	}686 687	mutex_unlock(&driver_lock);688 689}690EXPORT_SYMBOL_GPL(vfio_platform_unregister_reset);691 692MODULE_VERSION(DRIVER_VERSION);693MODULE_LICENSE("GPL v2");694MODULE_AUTHOR(DRIVER_AUTHOR);695MODULE_DESCRIPTION(DRIVER_DESC);696