diff options
Diffstat (limited to 'drivers/uio/uio_pci_generic.c')
| -rw-r--r-- | drivers/uio/uio_pci_generic.c | 102 |
1 files changed, 62 insertions, 40 deletions
diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c index 077ae12269ce..e03f9b532a96 100644 --- a/drivers/uio/uio_pci_generic.c +++ b/drivers/uio/uio_pci_generic.c @@ -1,10 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* uio_pci_generic - generic UIO driver for PCI 2.3 devices * * Copyright (C) 2009 Red Hat, Inc. * Author: Michael S. Tsirkin <mst@redhat.com> * - * This work is licensed under the terms of the GNU GPL, version 2. - * * Since the driver does not declare any device ids, you must allocate * id and bind the device to the driver yourself. For example: * @@ -40,6 +39,22 @@ to_uio_pci_generic_dev(struct uio_info *info) return container_of(info, struct uio_pci_generic_dev, info); } +static int release(struct uio_info *info, struct inode *inode) +{ + struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info); + + /* + * This driver is insecure when used with devices doing DMA, but some + * people (mis)use it with such devices. + * Let's at least make sure DMA isn't left enabled after the userspace + * driver closes the fd. + * Note that there's a non-zero chance doing this will wedge the device + * at least until reset. + */ + pci_clear_master(gdev->pdev); + return 0; +} + /* Interrupt handler. Read/modify/write the command register to disable * the interrupt. */ static irqreturn_t irqhandler(int irq, struct uio_info *info) @@ -57,67 +72,74 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct uio_pci_generic_dev *gdev; + struct uio_mem *uiomem; int err; + int i; - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err) { dev_err(&pdev->dev, "%s: pci_enable_device failed: %d\n", __func__, err); return err; } - if (!pdev->irq) { - dev_warn(&pdev->dev, "No IRQ assigned to device: " - "no support for interrupts?\n"); - pci_disable_device(pdev); + if (pdev->irq && !pci_intx_mask_supported(pdev)) return -ENODEV; - } - - if (!pci_intx_mask_supported(pdev)) { - err = -ENODEV; - goto err_verify; - } - gdev = kzalloc(sizeof(struct uio_pci_generic_dev), GFP_KERNEL); - if (!gdev) { - err = -ENOMEM; - goto err_alloc; - } + gdev = devm_kzalloc(&pdev->dev, sizeof(struct uio_pci_generic_dev), GFP_KERNEL); + if (!gdev) + return -ENOMEM; gdev->info.name = "uio_pci_generic"; gdev->info.version = DRIVER_VERSION; - gdev->info.irq = pdev->irq; - gdev->info.irq_flags = IRQF_SHARED; - gdev->info.handler = irqhandler; + gdev->info.release = release; gdev->pdev = pdev; + if (pdev->irq && (pdev->irq != IRQ_NOTCONNECTED)) { + gdev->info.irq = pdev->irq; + gdev->info.irq_flags = IRQF_SHARED; + gdev->info.handler = irqhandler; + } else { + dev_warn(&pdev->dev, "No IRQ assigned to device: " + "no support for interrupts?\n"); + } - if (uio_register_device(&pdev->dev, &gdev->info)) - goto err_register; - pci_set_drvdata(pdev, gdev); - - return 0; -err_register: - kfree(gdev); -err_alloc: -err_verify: - pci_disable_device(pdev); - return err; -} + uiomem = &gdev->info.mem[0]; + for (i = 0; i < MAX_UIO_MAPS; ++i) { + struct resource *r = &pdev->resource[i]; + + if (r->flags != (IORESOURCE_SIZEALIGN | IORESOURCE_MEM)) + continue; + + if (uiomem >= &gdev->info.mem[MAX_UIO_MAPS]) { + dev_warn( + &pdev->dev, + "device has more than " __stringify( + MAX_UIO_MAPS) " I/O memory resources.\n"); + break; + } + + uiomem->memtype = UIO_MEM_PHYS; + uiomem->addr = r->start & PAGE_MASK; + uiomem->offs = r->start & ~PAGE_MASK; + uiomem->size = + (uiomem->offs + resource_size(r) + PAGE_SIZE - 1) & + PAGE_MASK; + uiomem->name = r->name; + ++uiomem; + } -static void remove(struct pci_dev *pdev) -{ - struct uio_pci_generic_dev *gdev = pci_get_drvdata(pdev); + while (uiomem < &gdev->info.mem[MAX_UIO_MAPS]) { + uiomem->size = 0; + ++uiomem; + } - uio_unregister_device(&gdev->info); - pci_disable_device(pdev); - kfree(gdev); + return devm_uio_register_device(&pdev->dev, &gdev->info); } static struct pci_driver uio_pci_driver = { .name = "uio_pci_generic", .id_table = NULL, /* only dynamic id's */ .probe = probe, - .remove = remove, }; module_pci_driver(uio_pci_driver); |
