summaryrefslogtreecommitdiff
path: root/drivers/staging/gasket/gasket_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/gasket/gasket_core.c')
-rw-r--r--drivers/staging/gasket/gasket_core.c1815
1 files changed, 0 insertions, 1815 deletions
diff --git a/drivers/staging/gasket/gasket_core.c b/drivers/staging/gasket/gasket_core.c
deleted file mode 100644
index 28dab302183b..000000000000
--- a/drivers/staging/gasket/gasket_core.c
+++ /dev/null
@@ -1,1815 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Gasket generic driver framework. This file contains the implementation
- * for the Gasket generic driver framework - the functionality that is common
- * across Gasket devices.
- *
- * Copyright (C) 2018 Google, Inc.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include "gasket_core.h"
-
-#include "gasket_interrupt.h"
-#include "gasket_ioctl.h"
-#include "gasket_page_table.h"
-#include "gasket_sysfs.h"
-
-#include <linux/capability.h>
-#include <linux/compiler.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/of.h>
-#include <linux/pid_namespace.h>
-#include <linux/printk.h>
-#include <linux/sched.h>
-
-#ifdef GASKET_KERNEL_TRACE_SUPPORT
-#define CREATE_TRACE_POINTS
-#include <trace/events/gasket_mmap.h>
-#else
-#define trace_gasket_mmap_exit(x)
-#define trace_gasket_mmap_entry(x, ...)
-#endif
-
-/*
- * "Private" members of gasket_driver_desc.
- *
- * Contains internal per-device type tracking data, i.e., data not appropriate
- * as part of the public interface for the generic framework.
- */
-struct gasket_internal_desc {
- /* Device-specific-driver-provided configuration information. */
- const struct gasket_driver_desc *driver_desc;
-
- /* Protects access to per-driver data (i.e. this structure). */
- struct mutex mutex;
-
- /* Kernel-internal device class. */
- struct class *class;
-
- /* Instantiated / present devices of this type. */
- struct gasket_dev *devs[GASKET_DEV_MAX];
-};
-
-/* do_map_region() needs be able to return more than just true/false. */
-enum do_map_region_status {
- /* The region was successfully mapped. */
- DO_MAP_REGION_SUCCESS,
-
- /* Attempted to map region and failed. */
- DO_MAP_REGION_FAILURE,
-
- /* The requested region to map was not part of a mappable region. */
- DO_MAP_REGION_INVALID,
-};
-
-/* Global data definitions. */
-/* Mutex - only for framework-wide data. Other data should be protected by
- * finer-grained locks.
- */
-static DEFINE_MUTEX(g_mutex);
-
-/* List of all registered device descriptions & their supporting data. */
-static struct gasket_internal_desc g_descs[GASKET_FRAMEWORK_DESC_MAX];
-
-/* Mapping of statuses to human-readable strings. Must end with {0,NULL}. */
-static const struct gasket_num_name gasket_status_name_table[] = {
- { GASKET_STATUS_DEAD, "DEAD" },
- { GASKET_STATUS_ALIVE, "ALIVE" },
- { GASKET_STATUS_LAMED, "LAMED" },
- { GASKET_STATUS_DRIVER_EXIT, "DRIVER_EXITING" },
- { 0, NULL },
-};
-
-/* Enumeration of the automatic Gasket framework sysfs nodes. */
-enum gasket_sysfs_attribute_type {
- ATTR_BAR_OFFSETS,
- ATTR_BAR_SIZES,
- ATTR_DRIVER_VERSION,
- ATTR_FRAMEWORK_VERSION,
- ATTR_DEVICE_TYPE,
- ATTR_HARDWARE_REVISION,
- ATTR_PCI_ADDRESS,
- ATTR_STATUS,
- ATTR_IS_DEVICE_OWNED,
- ATTR_DEVICE_OWNER,
- ATTR_WRITE_OPEN_COUNT,
- ATTR_RESET_COUNT,
- ATTR_USER_MEM_RANGES
-};
-
-/* Perform a standard Gasket callback. */
-static inline int
-check_and_invoke_callback(struct gasket_dev *gasket_dev,
- int (*cb_function)(struct gasket_dev *))
-{
- int ret = 0;
-
- if (cb_function) {
- mutex_lock(&gasket_dev->mutex);
- ret = cb_function(gasket_dev);
- mutex_unlock(&gasket_dev->mutex);
- }
- return ret;
-}
-
-/* Perform a standard Gasket callback without grabbing gasket_dev->mutex. */
-static inline int
-gasket_check_and_invoke_callback_nolock(struct gasket_dev *gasket_dev,
- int (*cb_function)(struct gasket_dev *))
-{
- int ret = 0;
-
- if (cb_function)
- ret = cb_function(gasket_dev);
- return ret;
-}
-
-/*
- * Return nonzero if the gasket_cdev_info is owned by the current thread group
- * ID.
- */
-static int gasket_owned_by_current_tgid(struct gasket_cdev_info *info)
-{
- return (info->ownership.is_owned &&
- (info->ownership.owner == current->tgid));
-}
-
-/*
- * Find the next free gasket_internal_dev slot.
- *
- * Returns the located slot number on success or a negative number on failure.
- */
-static int gasket_find_dev_slot(struct gasket_internal_desc *internal_desc,
- const char *kobj_name)
-{
- int i;
-
- mutex_lock(&internal_desc->mutex);
-
- /* Search for a previous instance of this device. */
- for (i = 0; i < GASKET_DEV_MAX; i++) {
- if (internal_desc->devs[i] &&
- strcmp(internal_desc->devs[i]->kobj_name, kobj_name) == 0) {
- pr_err("Duplicate device %s\n", kobj_name);
- mutex_unlock(&internal_desc->mutex);
- return -EBUSY;
- }
- }
-
- /* Find a free device slot. */
- for (i = 0; i < GASKET_DEV_MAX; i++) {
- if (!internal_desc->devs[i])
- break;
- }
-
- if (i == GASKET_DEV_MAX) {
- pr_err("Too many registered devices; max %d\n", GASKET_DEV_MAX);
- mutex_unlock(&internal_desc->mutex);
- return -EBUSY;
- }
-
- mutex_unlock(&internal_desc->mutex);
- return i;
-}
-
-/*
- * Allocate and initialize a Gasket device structure, add the device to the
- * device list.
- *
- * Returns 0 if successful, a negative error code otherwise.
- */
-static int gasket_alloc_dev(struct gasket_internal_desc *internal_desc,
- struct device *parent, struct gasket_dev **pdev)
-{
- int dev_idx;
- const struct gasket_driver_desc *driver_desc =
- internal_desc->driver_desc;
- struct gasket_dev *gasket_dev;
- struct gasket_cdev_info *dev_info;
- const char *parent_name = dev_name(parent);
-
- pr_debug("Allocating a Gasket device, parent %s.\n", parent_name);
-
- *pdev = NULL;
-
- dev_idx = gasket_find_dev_slot(internal_desc, parent_name);
- if (dev_idx < 0)
- return dev_idx;
-
- gasket_dev = *pdev = kzalloc(sizeof(*gasket_dev), GFP_KERNEL);
- if (!gasket_dev) {
- pr_err("no memory for device, parent %s\n", parent_name);
- return -ENOMEM;
- }
- internal_desc->devs[dev_idx] = gasket_dev;
-
- mutex_init(&gasket_dev->mutex);
-
- gasket_dev->internal_desc = internal_desc;
- gasket_dev->dev_idx = dev_idx;
- snprintf(gasket_dev->kobj_name, GASKET_NAME_MAX, "%s", parent_name);
- gasket_dev->dev = get_device(parent);
- /* gasket_bar_data is uninitialized. */
- gasket_dev->num_page_tables = driver_desc->num_page_tables;
- /* max_page_table_size and *page table are uninit'ed */
- /* interrupt_data is not initialized. */
- /* status is 0, or GASKET_STATUS_DEAD */
-
- dev_info = &gasket_dev->dev_info;
- snprintf(dev_info->name, GASKET_NAME_MAX, "%s_%u", driver_desc->name,
- gasket_dev->dev_idx);
- dev_info->devt =
- MKDEV(driver_desc->major, driver_desc->minor +
- gasket_dev->dev_idx);
- dev_info->device =
- device_create(internal_desc->class, parent, dev_info->devt,
- gasket_dev, dev_info->name);
-
- /* cdev has not yet been added; cdev_added is 0 */
- dev_info->gasket_dev_ptr = gasket_dev;
- /* ownership is all 0, indicating no owner or opens. */
-
- return 0;
-}
-
-/* Free a Gasket device. */
-static void gasket_free_dev(struct gasket_dev *gasket_dev)
-{
- struct gasket_internal_desc *internal_desc = gasket_dev->internal_desc;
-
- mutex_lock(&internal_desc->mutex);
- internal_desc->devs[gasket_dev->dev_idx] = NULL;
- mutex_unlock(&internal_desc->mutex);
- put_device(gasket_dev->dev);
- kfree(gasket_dev);
-}
-
-/*
- * Maps the specified bar into kernel space.
- *
- * Returns 0 on success, a negative error code otherwise.
- * A zero-sized BAR will not be mapped, but is not an error.
- */
-static int gasket_map_pci_bar(struct gasket_dev *gasket_dev, int bar_num)
-{
- struct gasket_internal_desc *internal_desc = gasket_dev->internal_desc;
- const struct gasket_driver_desc *driver_desc =
- internal_desc->driver_desc;
- ulong desc_bytes = driver_desc->bar_descriptions[bar_num].size;
- struct gasket_bar_data *data;
- int ret;
-
- if (desc_bytes == 0)
- return 0;
-
- if (driver_desc->bar_descriptions[bar_num].type != PCI_BAR) {
- /* not PCI: skip this entry */
- return 0;
- }
-
- data = &gasket_dev->bar_data[bar_num];
-
- /*
- * pci_resource_start and pci_resource_len return a "resource_size_t",
- * which is safely castable to ulong (which itself is the arg to
- * request_mem_region).
- */
- data->phys_base =
- (ulong)pci_resource_start(gasket_dev->pci_dev, bar_num);
- if (!data->phys_base) {
- dev_err(gasket_dev->dev, "Cannot get BAR%u base address\n",
- bar_num);
- return -EINVAL;
- }
-
- data->length_bytes =
- (ulong)pci_resource_len(gasket_dev->pci_dev, bar_num);
- if (data->length_bytes < desc_bytes) {
- dev_err(gasket_dev->dev,
- "PCI BAR %u space is too small: %lu; expected >= %lu\n",
- bar_num, data->length_bytes, desc_bytes);
- return -ENOMEM;
- }
-
- if (!request_mem_region(data->phys_base, data->length_bytes,
- gasket_dev->dev_info.name)) {
- dev_err(gasket_dev->dev,
- "Cannot get BAR %d memory region %p\n",
- bar_num, &gasket_dev->pci_dev->resource[bar_num]);
- return -EINVAL;
- }
-
- data->virt_base = ioremap(data->phys_base, data->length_bytes);
- if (!data->virt_base) {
- dev_err(gasket_dev->dev,
- "Cannot remap BAR %d memory region %p\n",
- bar_num, &gasket_dev->pci_dev->resource[bar_num]);
- ret = -ENOMEM;
- goto fail;
- }
-
- dma_set_mask(&gasket_dev->pci_dev->dev, DMA_BIT_MASK(64));
- dma_set_coherent_mask(&gasket_dev->pci_dev->dev, DMA_BIT_MASK(64));
-
- return 0;
-
-fail:
- iounmap(data->virt_base);
- release_mem_region(data->phys_base, data->length_bytes);
- return ret;
-}
-
-/*
- * Releases PCI BAR mapping.
- *
- * A zero-sized or not-mapped BAR will not be unmapped, but is not an error.
- */
-static void gasket_unmap_pci_bar(struct gasket_dev *dev, int bar_num)
-{
- ulong base, bytes;
- struct gasket_internal_desc *internal_desc = dev->internal_desc;
- const struct gasket_driver_desc *driver_desc =
- internal_desc->driver_desc;
-
- if (driver_desc->bar_descriptions[bar_num].size == 0 ||
- !dev->bar_data[bar_num].virt_base)
- return;
-
- if (driver_desc->bar_descriptions[bar_num].type != PCI_BAR)
- return;
-
- iounmap(dev->bar_data[bar_num].virt_base);
- dev->bar_data[bar_num].virt_base = NULL;
-
- base = pci_resource_start(dev->pci_dev, bar_num);
- if (!base) {
- dev_err(dev->dev, "cannot get PCI BAR%u base address\n",
- bar_num);
- return;
- }
-
- bytes = pci_resource_len(dev->pci_dev, bar_num);
- release_mem_region(base, bytes);
-}
-
-/*
- * Setup PCI memory mapping for the specified device.
- *
- * Reads the BAR registers and sets up pointers to the device's memory mapped
- * IO space.
- *
- * Returns 0 on success and a negative value otherwise.
- */
-static int gasket_setup_pci(struct pci_dev *pci_dev,
- struct gasket_dev *gasket_dev)
-{
- int i, mapped_bars, ret;
-
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
- ret = gasket_map_pci_bar(gasket_dev, i);
- if (ret) {
- mapped_bars = i;
- goto fail;
- }
- }
-
- return 0;
-
-fail:
- for (i = 0; i < mapped_bars; i++)
- gasket_unmap_pci_bar(gasket_dev, i);
-
- return -ENOMEM;
-}
-
-/* Unmaps memory for the specified device. */
-static void gasket_cleanup_pci(struct gasket_dev *gasket_dev)
-{
- int i;
-
- for (i = 0; i < PCI_STD_NUM_BARS; i++)
- gasket_unmap_pci_bar(gasket_dev, i);
-}
-
-/* Determine the health of the Gasket device. */
-static int gasket_get_hw_status(struct gasket_dev *gasket_dev)
-{
- int status;
- int i;
- const struct gasket_driver_desc *driver_desc =
- gasket_dev->internal_desc->driver_desc;
-
- status = gasket_check_and_invoke_callback_nolock(gasket_dev,
- driver_desc->device_status_cb);
- if (status != GASKET_STATUS_ALIVE) {
- dev_dbg(gasket_dev->dev, "Hardware reported status %d.\n",
- status);
- return status;
- }
-
- status = gasket_interrupt_system_status(gasket_dev);
- if (status != GASKET_STATUS_ALIVE) {
- dev_dbg(gasket_dev->dev,
- "Interrupt system reported status %d.\n", status);
- return status;
- }
-
- for (i = 0; i < driver_desc->num_page_tables; ++i) {
- status = gasket_page_table_system_status(gasket_dev->page_table[i]);
- if (status != GASKET_STATUS_ALIVE) {
- dev_dbg(gasket_dev->dev,
- "Page table %d reported status %d.\n",
- i, status);
- return status;
- }
- }
-
- return GASKET_STATUS_ALIVE;
-}
-
-static ssize_t
-gasket_write_mappable_regions(char *buf,
- const struct gasket_driver_desc *driver_desc,
- int bar_index)
-{
- int i;
- ssize_t written;
- ssize_t total_written = 0;
- ulong min_addr, max_addr;
- struct gasket_bar_desc bar_desc =
- driver_desc->bar_descriptions[bar_index];
-
- if (bar_desc.permissions == GASKET_NOMAP)
- return 0;
- for (i = 0;
- i < bar_desc.num_mappable_regions && total_written < PAGE_SIZE;
- i++) {
- min_addr = bar_desc.mappable_regions[i].start -
- driver_desc->legacy_mmap_address_offset;
- max_addr = bar_desc.mappable_regions[i].start -
- driver_desc->legacy_mmap_address_offset +
- bar_desc.mappable_regions[i].length_bytes;
- written = scnprintf(buf, PAGE_SIZE - total_written,
- "0x%08lx-0x%08lx\n", min_addr, max_addr);
- total_written += written;
- buf += written;
- }
- return total_written;
-}
-
-static ssize_t gasket_sysfs_data_show(struct device *device,
- struct device_attribute *attr, char *buf)
-{
- int i, ret = 0;
- ssize_t current_written = 0;
- const struct gasket_driver_desc *driver_desc;
- struct gasket_dev *gasket_dev;
- struct gasket_sysfs_attribute *gasket_attr;
- const struct gasket_bar_desc *bar_desc;
- enum gasket_sysfs_attribute_type sysfs_type;
-
- gasket_dev = gasket_sysfs_get_device_data(device);
- if (!gasket_dev) {
- dev_err(device, "No sysfs mapping found for device\n");
- return 0;
- }
-
- gasket_attr = gasket_sysfs_get_attr(device, attr);
- if (!gasket_attr) {
- dev_err(device, "No sysfs attr found for device\n");
- gasket_sysfs_put_device_data(device, gasket_dev);
- return 0;
- }
-
- driver_desc = gasket_dev->internal_desc->driver_desc;
-
- sysfs_type =
- (enum gasket_sysfs_attribute_type)gasket_attr->data.attr_type;
- switch (sysfs_type) {
- case ATTR_BAR_OFFSETS:
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
- bar_desc = &driver_desc->bar_descriptions[i];
- if (bar_desc->size == 0)
- continue;
- current_written =
- snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
- (ulong)bar_desc->base);
- buf += current_written;
- ret += current_written;
- }
- break;
- case ATTR_BAR_SIZES:
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
- bar_desc = &driver_desc->bar_descriptions[i];
- if (bar_desc->size == 0)
- continue;
- current_written =
- snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
- (ulong)bar_desc->size);
- buf += current_written;
- ret += current_written;
- }
- break;
- case ATTR_DRIVER_VERSION:
- ret = snprintf(buf, PAGE_SIZE, "%s\n",
- gasket_dev->internal_desc->driver_desc->driver_version);
- break;
- case ATTR_FRAMEWORK_VERSION:
- ret = snprintf(buf, PAGE_SIZE, "%s\n",
- GASKET_FRAMEWORK_VERSION);
- break;
- case ATTR_DEVICE_TYPE:
- ret = snprintf(buf, PAGE_SIZE, "%s\n",
- gasket_dev->internal_desc->driver_desc->name);
- break;
- case ATTR_HARDWARE_REVISION:
- ret = snprintf(buf, PAGE_SIZE, "%d\n",
- gasket_dev->hardware_revision);
- break;
- case ATTR_PCI_ADDRESS:
- ret = snprintf(buf, PAGE_SIZE, "%s\n", gasket_dev->kobj_name);
- break;
- case ATTR_STATUS:
- ret = snprintf(buf, PAGE_SIZE, "%s\n",
- gasket_num_name_lookup(gasket_dev->status,
- gasket_status_name_table));
- break;
- case ATTR_IS_DEVICE_OWNED:
- ret = snprintf(buf, PAGE_SIZE, "%d\n",
- gasket_dev->dev_info.ownership.is_owned);
- break;
- case ATTR_DEVICE_OWNER:
- ret = snprintf(buf, PAGE_SIZE, "%d\n",
- gasket_dev->dev_info.ownership.owner);
- break;
- case ATTR_WRITE_OPEN_COUNT:
- ret = snprintf(buf, PAGE_SIZE, "%d\n",
- gasket_dev->dev_info.ownership.write_open_count);
- break;
- case ATTR_RESET_COUNT:
- ret = snprintf(buf, PAGE_SIZE, "%d\n", gasket_dev->reset_count);
- break;
- case ATTR_USER_MEM_RANGES:
- for (i = 0; i < PCI_STD_NUM_BARS; ++i) {
- current_written =
- gasket_write_mappable_regions(buf, driver_desc,
- i);
- buf += current_written;
- ret += current_written;
- }
- break;
- default:
- dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n",
- attr->attr.name);
- ret = 0;
- break;
- }
-
- gasket_sysfs_put_attr(device, gasket_attr);
- gasket_sysfs_put_device_data(device, gasket_dev);
- return ret;
-}
-
-/* These attributes apply to all Gasket driver instances. */
-static const struct gasket_sysfs_attribute gasket_sysfs_generic_attrs[] = {
- GASKET_SYSFS_RO(bar_offsets, gasket_sysfs_data_show, ATTR_BAR_OFFSETS),
- GASKET_SYSFS_RO(bar_sizes, gasket_sysfs_data_show, ATTR_BAR_SIZES),
- GASKET_SYSFS_RO(driver_version, gasket_sysfs_data_show,
- ATTR_DRIVER_VERSION),
- GASKET_SYSFS_RO(framework_version, gasket_sysfs_data_show,
- ATTR_FRAMEWORK_VERSION),
- GASKET_SYSFS_RO(device_type, gasket_sysfs_data_show, ATTR_DEVICE_TYPE),
- GASKET_SYSFS_RO(revision, gasket_sysfs_data_show,
- ATTR_HARDWARE_REVISION),
- GASKET_SYSFS_RO(pci_address, gasket_sysfs_data_show, ATTR_PCI_ADDRESS),
- GASKET_SYSFS_RO(status, gasket_sysfs_data_show, ATTR_STATUS),
- GASKET_SYSFS_RO(is_device_owned, gasket_sysfs_data_show,
- ATTR_IS_DEVICE_OWNED),
- GASKET_SYSFS_RO(device_owner, gasket_sysfs_data_show,
- ATTR_DEVICE_OWNER),
- GASKET_SYSFS_RO(write_open_count, gasket_sysfs_data_show,
- ATTR_WRITE_OPEN_COUNT),
- GASKET_SYSFS_RO(reset_count, gasket_sysfs_data_show, ATTR_RESET_COUNT),
- GASKET_SYSFS_RO(user_mem_ranges, gasket_sysfs_data_show,
- ATTR_USER_MEM_RANGES),
- GASKET_END_OF_ATTR_ARRAY
-};
-
-/* Add a char device and related info. */
-static int gasket_add_cdev(struct gasket_cdev_info *dev_info,
- const struct file_operations *file_ops,
- struct module *owner)
-{
- int ret;
-
- cdev_init(&dev_info->cdev, file_ops);
- dev_info->cdev.owner = owner;
- ret = cdev_add(&dev_info->cdev, dev_info->devt, 1);
- if (ret) {
- dev_err(dev_info->gasket_dev_ptr->dev,
- "cannot add char device [ret=%d]\n", ret);
- return ret;
- }
- dev_info->cdev_added = 1;
-
- return 0;
-}
-
-/* Disable device operations. */
-void gasket_disable_device(struct gasket_dev *gasket_dev)
-{
- const struct gasket_driver_desc *driver_desc =
- gasket_dev->internal_desc->driver_desc;
- int i;
-
- /* Only delete the device if it has been successfully added. */
- if (gasket_dev->dev_info.cdev_added)
- cdev_del(&gasket_dev->dev_info.cdev);
-
- gasket_dev->status = GASKET_STATUS_DEAD;
-
- gasket_interrupt_cleanup(gasket_dev);
-
- for (i = 0; i < driver_desc->num_page_tables; ++i) {
- if (gasket_dev->page_table[i]) {
- gasket_page_table_reset(gasket_dev->page_table[i]);
- gasket_page_table_cleanup(gasket_dev->page_table[i]);
- }
- }
-}
-EXPORT_SYMBOL(gasket_disable_device);
-
-/*
- * Registered driver descriptor lookup for PCI devices.
- *
- * Precondition: Called with g_mutex held (to avoid a race on return).
- * Returns NULL if no matching device was found.
- */
-static struct gasket_internal_desc *
-lookup_pci_internal_desc(struct pci_dev *pci_dev)
-{
- int i;
-
- __must_hold(&g_mutex);
- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- if (g_descs[i].driver_desc &&
- g_descs[i].driver_desc->pci_id_table &&
- pci_match_id(g_descs[i].driver_desc->pci_id_table, pci_dev))
- return &g_descs[i];
- }
-
- return NULL;
-}
-
-/*
- * Verifies that the user has permissions to perform the requested mapping and
- * that the provided descriptor/range is of adequate size to hold the range to
- * be mapped.
- */
-static bool gasket_mmap_has_permissions(struct gasket_dev *gasket_dev,
- struct vm_area_struct *vma,
- int bar_permissions)
-{
- int requested_permissions;
- /* Always allow sysadmin to access. */
- if (capable(CAP_SYS_ADMIN))
- return true;
-
- /* Never allow non-sysadmins to access to a dead device. */
- if (gasket_dev->status != GASKET_STATUS_ALIVE) {
- dev_dbg(gasket_dev->dev, "Device is dead.\n");
- return false;
- }
-
- /* Make sure that no wrong flags are set. */
- requested_permissions =
- (vma->vm_flags & VM_ACCESS_FLAGS);
- if (requested_permissions & ~(bar_permissions)) {
- dev_dbg(gasket_dev->dev,
- "Attempting to map a region with requested permissions 0x%x, but region has permissions 0x%x.\n",
- requested_permissions, bar_permissions);
- return false;
- }
-
- /* Do not allow a non-owner to write. */
- if ((vma->vm_flags & VM_WRITE) &&
- !gasket_owned_by_current_tgid(&gasket_dev->dev_info)) {
- dev_dbg(gasket_dev->dev,
- "Attempting to mmap a region for write without owning device.\n");
- return false;
- }
-
- return true;
-}
-
-/*
- * Verifies that the input address is within the region allocated to coherent
- * buffer.
- */
-static bool
-gasket_is_coherent_region(const struct gasket_driver_desc *driver_desc,
- ulong address)
-{
- struct gasket_coherent_buffer_desc coh_buff_desc =
- driver_desc->coherent_buffer_description;
-
- if (coh_buff_desc.permissions != GASKET_NOMAP) {
- if ((address >= coh_buff_desc.base) &&
- (address < coh_buff_desc.base + coh_buff_desc.size)) {
- return true;
- }
- }
- return false;
-}
-
-static int gasket_get_bar_index(const struct gasket_dev *gasket_dev,
- ulong phys_addr)
-{
- int i;
- const struct gasket_driver_desc *driver_desc;
-
- driver_desc = gasket_dev->internal_desc->driver_desc;
- for (i = 0; i < PCI_STD_NUM_BARS; ++i) {
- struct gasket_bar_desc bar_desc =
- driver_desc->bar_descriptions[i];
-
- if (bar_desc.permissions != GASKET_NOMAP) {
- if (phys_addr >= bar_desc.base &&
- phys_addr < (bar_desc.base + bar_desc.size)) {
- return i;
- }
- }
- }
- /* If we haven't found the address by now, it is invalid. */
- return -EINVAL;
-}
-
-/*
- * Sets the actual bounds to map, given the device's mappable region.
- *
- * Given the device's mappable region, along with the user-requested mapping
- * start offset and length of the user region, determine how much of this
- * mappable region can be mapped into the user's region (start/end offsets),
- * and the physical offset (phys_offset) into the BAR where the mapping should
- * begin (either the VMA's or region lower bound).
- *
- * In other words, this calculates the overlap between the VMA
- * (bar_offset, requested_length) and the given gasket_mappable_region.
- *
- * Returns true if there's anything to map, and false otherwise.
- */
-static bool
-gasket_mm_get_mapping_addrs(const struct gasket_mappable_region *region,
- ulong bar_offset, ulong requested_length,
- struct gasket_mappable_region *mappable_region,
- ulong *virt_offset)
-{
- ulong range_start = region->start;
- ulong range_length = region->length_bytes;
- ulong range_end = range_start + range_length;
-
- *virt_offset = 0;
- if (bar_offset + requested_length < range_start) {
- /*
- * If the requested region is completely below the range,
- * there is nothing to map.
- */
- return false;
- } else if (bar_offset <= range_start) {
- /* If the bar offset is below this range's start
- * but the requested length continues into it:
- * 1) Only map starting from the beginning of this
- * range's phys. offset, so we don't map unmappable
- * memory.
- * 2) The length of the virtual memory to not map is the
- * delta between the bar offset and the
- * mappable start (and since the mappable start is
- * bigger, start - req.)
- * 3) The map length is the minimum of the mappable
- * requested length (requested_length - virt_offset)
- * and the actual mappable length of the range.
- */
- mappable_region->start = range_start;
- *virt_offset = range_start - bar_offset;
- mappable_region->length_bytes =
- min(requested_length - *virt_offset, range_length);
- return true;
- } else if (bar_offset > range_start &&
- bar_offset < range_end) {
- /*
- * If the bar offset is within this range:
- * 1) Map starting from the bar offset.
- * 2) Because there is no forbidden memory between the
- * bar offset and the range start,
- * virt_offset is 0.
- * 3) The map length is the minimum of the requested
- * length and the remaining length in the buffer
- * (range_end - bar_offset)
- */
- mappable_region->start = bar_offset;
- *virt_offset = 0;
- mappable_region->length_bytes =
- min(requested_length, range_end - bar_offset);
- return true;
- }
-
- /*
- * If the requested [start] offset is above range_end,
- * there's nothing to map.
- */
- return false;
-}
-
-/*
- * Calculates the offset where the VMA range begins in its containing BAR.
- * The offset is written into bar_offset on success.
- * Returns zero on success, anything else on error.
- */
-static int gasket_mm_vma_bar_offset(const struct gasket_dev *gasket_dev,
- const struct vm_area_struct *vma,
- ulong *bar_offset)
-{
- ulong raw_offset;
- int bar_index;
- const struct gasket_driver_desc *driver_desc =
- gasket_dev->internal_desc->driver_desc;
-
- raw_offset = (vma->vm_pgoff << PAGE_SHIFT) +
- driver_desc->legacy_mmap_address_offset;
- bar_index = gasket_get_bar_index(gasket_dev, raw_offset);
- if (bar_index < 0) {
- dev_err(gasket_dev->dev,
- "Unable to find matching bar for address 0x%lx\n",
- raw_offset);
- trace_gasket_mmap_exit(bar_index);
- return bar_index;
- }
- *bar_offset =
- raw_offset - driver_desc->bar_descriptions[bar_index].base;
-
- return 0;
-}
-
-int gasket_mm_unmap_region(const struct gasket_dev *gasket_dev,
- struct vm_area_struct *vma,
- const struct gasket_mappable_region *map_region)
-{
- ulong bar_offset;
- ulong virt_offset;
- struct gasket_mappable_region mappable_region;
- int ret;
-
- if (map_region->length_bytes == 0)
- return 0;
-
- ret = gasket_mm_vma_bar_offset(gasket_dev, vma, &bar_offset);
- if (ret)
- return ret;
-
- if (!gasket_mm_get_mapping_addrs(map_region, bar_offset,
- vma->vm_end - vma->vm_start,
- &mappable_region, &virt_offset))
- return 1;
-
- /*
- * The length passed to zap_vma_ptes MUST BE A MULTIPLE OF
- * PAGE_SIZE! Trust me. I have the scars.
- *
- * Next multiple of y: ceil_div(x, y) * y
- */
- zap_vma_ptes(vma, vma->vm_start + virt_offset,
- DIV_ROUND_UP(mappable_region.length_bytes, PAGE_SIZE) *
- PAGE_SIZE);
- return 0;
-}
-EXPORT_SYMBOL(gasket_mm_unmap_region);
-
-/* Maps a virtual address + range to a physical offset of a BAR. */
-static enum do_map_region_status
-do_map_region(const struct gasket_dev *gasket_dev, struct vm_area_struct *vma,
- struct gasket_mappable_region *mappable_region)
-{
- /* Maximum size of a single call to io_remap_pfn_range. */
- /* I pulled this number out of thin air. */
- const ulong max_chunk_size = 64 * 1024 * 1024;
- ulong chunk_size, mapped_bytes = 0;
-
- const struct gasket_driver_desc *driver_desc =
- gasket_dev->internal_desc->driver_desc;
-
- ulong bar_offset, virt_offset;
- struct gasket_mappable_region region_to_map;
- ulong phys_offset, map_length;
- ulong virt_base, phys_base;
- int bar_index, ret;
-
- ret = gasket_mm_vma_bar_offset(gasket_dev, vma, &bar_offset);
- if (ret)
- return DO_MAP_REGION_INVALID;
-
- if (!gasket_mm_get_mapping_addrs(mappable_region, bar_offset,
- vma->vm_end - vma->vm_start,
- &region_to_map, &virt_offset))
- return DO_MAP_REGION_INVALID;
- phys_offset = region_to_map.start;
- map_length = region_to_map.length_bytes;
-
- virt_base = vma->vm_start + virt_offset;
- bar_index =
- gasket_get_bar_index(gasket_dev,
- (vma->vm_pgoff << PAGE_SHIFT) +
- driver_desc->legacy_mmap_address_offset);
-
- if (bar_index < 0)
- return DO_MAP_REGION_INVALID;
-
- phys_base = gasket_dev->bar_data[bar_index].phys_base + phys_offset;
- while (mapped_bytes < map_length) {
- /*
- * io_remap_pfn_range can take a while, so we chunk its
- * calls and call cond_resched between each.
- */
- chunk_size = min(max_chunk_size, map_length - mapped_bytes);
-
- cond_resched();
- ret = io_remap_pfn_range(vma, virt_base + mapped_bytes,
- (phys_base + mapped_bytes) >>
- PAGE_SHIFT, chunk_size,
- vma->vm_page_prot);
- if (ret) {
- dev_err(gasket_dev->dev,
- "Error remapping PFN range.\n");
- goto fail;
- }
- mapped_bytes += chunk_size;
- }
-
- return DO_MAP_REGION_SUCCESS;
-
-fail:
- /* Unmap the partial chunk we mapped. */
- mappable_region->length_bytes = mapped_bytes;
- if (gasket_mm_unmap_region(gasket_dev, vma, mappable_region))
- dev_err(gasket_dev->dev,
- "Error unmapping partial region 0x%lx (0x%lx bytes)\n",
- (ulong)virt_offset,
- (ulong)mapped_bytes);
-
- return DO_MAP_REGION_FAILURE;
-}
-
-/* Map a region of coherent memory. */
-static int gasket_mmap_coherent(struct gasket_dev *gasket_dev,
- struct vm_area_struct *vma)
-{
- const struct gasket_driver_desc *driver_desc =
- gasket_dev->internal_desc->driver_desc;
- const ulong requested_length = vma->vm_end - vma->vm_start;
- int ret;
- ulong permissions;
-
- if (requested_length == 0 || requested_length >
- gasket_dev->coherent_buffer.length_bytes) {
- trace_gasket_mmap_exit(-EINVAL);
- return -EINVAL;
- }
-
- permissions = driver_desc->coherent_buffer_description.permissions;
- if (!gasket_mmap_has_permissions(gasket_dev, vma, permissions)) {
- dev_err(gasket_dev->dev, "Permission checking failed.\n");
- trace_gasket_mmap_exit(-EPERM);
- return -EPERM;
- }
-
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- ret = remap_pfn_range(vma, vma->vm_start,
- (gasket_dev->coherent_buffer.phys_base) >>
- PAGE_SHIFT, requested_length, vma->vm_page_prot);
- if (ret) {
- dev_err(gasket_dev->dev, "Error remapping PFN range err=%d.\n",
- ret);
- trace_gasket_mmap_exit(ret);
- return ret;
- }
-
- /* Record the user virtual to dma_address mapping that was
- * created by the kernel.
- */
- gasket_set_user_virt(gasket_dev, requested_length,
- gasket_dev->coherent_buffer.phys_base,
- vma->vm_start);
- return 0;
-}
-
-/* Map a device's BARs into user space. */
-static int gasket_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- int i, ret;
- int bar_index;
- int has_mapped_anything = 0;
- ulong permissions;
- ulong raw_offset, vma_size;
- bool is_coherent_region;
- const struct gasket_driver_desc *driver_desc;
- struct gasket_dev *gasket_dev = (struct gasket_dev *)filp->private_data;
- const struct gasket_bar_desc *bar_desc;
- struct gasket_mappable_region *map_regions = NULL;
- int num_map_regions = 0;
- enum do_map_region_status map_status;
-
- driver_desc = gasket_dev->internal_desc->driver_desc;
-
- if (vma->vm_start & ~PAGE_MASK) {
- dev_err(gasket_dev->dev,
- "Base address not page-aligned: 0x%lx\n",
- vma->vm_start);
- trace_gasket_mmap_exit(-EINVAL);
- return -EINVAL;
- }
-
- /* Calculate the offset of this range into physical mem. */
- raw_offset = (vma->vm_pgoff << PAGE_SHIFT) +
- driver_desc->legacy_mmap_address_offset;
- vma_size = vma->vm_end - vma->vm_start;
- trace_gasket_mmap_entry(gasket_dev->dev_info.name, raw_offset,
- vma_size);
-
- /*
- * Check if the raw offset is within a bar region. If not, check if it
- * is a coherent region.
- */
- bar_index = gasket_get_bar_index(gasket_dev, raw_offset);
- is_coherent_region = gasket_is_coherent_region(driver_desc, raw_offset);
- if (bar_index < 0 && !is_coherent_region) {
- dev_err(gasket_dev->dev,
- "Unable to find matching bar for address 0x%lx\n",
- raw_offset);
- trace_gasket_mmap_exit(bar_index);
- return bar_index;
- }
- if (bar_index > 0 && is_coherent_region) {
- dev_err(gasket_dev->dev,
- "double matching bar and coherent buffers for address 0x%lx\n",
- raw_offset);
- trace_gasket_mmap_exit(bar_index);
- return -EINVAL;
- }
-
- vma->vm_private_data = gasket_dev;
-
- if (is_coherent_region)
- return gasket_mmap_coherent(gasket_dev, vma);
-
- /* Everything in the rest of this function is for normal BAR mapping. */
-
- /*
- * Subtract the base of the bar from the raw offset to get the
- * memory location within the bar to map.
- */
- bar_desc = &driver_desc->bar_descriptions[bar_index];
- permissions = bar_desc->permissions;
- if (!gasket_mmap_has_permissions(gasket_dev, vma, permissions)) {
- dev_err(gasket_dev->dev, "Permission checking failed.\n");
- trace_gasket_mmap_exit(-EPERM);
- return -EPERM;
- }
-
- if (driver_desc->get_mappable_regions_cb) {
- ret = driver_desc->get_mappable_regions_cb(gasket_dev,
- bar_index,
- &map_regions,
- &num_map_regions);
- if (ret)
- return ret;
- } else {
- if (!gasket_mmap_has_permissions(gasket_dev, vma,
- bar_desc->permissions)) {
- dev_err(gasket_dev->dev,
- "Permission checking failed.\n");
- trace_gasket_mmap_exit(-EPERM);
- return -EPERM;
- }
- num_map_regions = bar_desc->num_mappable_regions;
- map_regions = kcalloc(num_map_regions,
- sizeof(*bar_desc->mappable_regions),
- GFP_KERNEL);
- if (map_regions) {
- memcpy(map_regions, bar_desc->mappable_regions,
- num_map_regions *
- sizeof(*bar_desc->mappable_regions));
- }
- }
-
- if (!map_regions || num_map_regions == 0) {
- dev_err(gasket_dev->dev, "No mappable regions returned!\n");
- return -EINVAL;
- }
-
- /* Marks the VMA's pages as uncacheable. */
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- for (i = 0; i < num_map_regions; i++) {
- map_status = do_map_region(gasket_dev, vma, &map_regions[i]);
- /* Try the next region if this one was not mappable. */
- if (map_status == DO_MAP_REGION_INVALID)
- continue;
- if (map_status == DO_MAP_REGION_FAILURE) {
- ret = -ENOMEM;
- goto fail;
- }
-
- has_mapped_anything = 1;
- }
-
- kfree(map_regions);
-
- /* If we could not map any memory, the request was invalid. */
- if (!has_mapped_anything) {
- dev_err(gasket_dev->dev,
- "Map request did not contain a valid region.\n");
- trace_gasket_mmap_exit(-EINVAL);
- return -EINVAL;
- }
-
- trace_gasket_mmap_exit(0);
- return 0;
-
-fail:
- /* Need to unmap any mapped ranges. */
- num_map_regions = i;
- for (i = 0; i < num_map_regions; i++)
- if (gasket_mm_unmap_region(gasket_dev, vma,
- &bar_desc->mappable_regions[i]))
- dev_err(gasket_dev->dev, "Error unmapping range %d.\n",
- i);
- kfree(map_regions);
-
- return ret;
-}
-
-/*
- * Open the char device file.
- *
- * If the open is for writing, and the device is not owned, this process becomes
- * the owner. If the open is for writing and the device is already owned by
- * some other process, it is an error. If this process is the owner, increment
- * the open count.
- *
- * Returns 0 if successful, a negative error number otherwise.
- */
-static int gasket_open(struct inode *inode, struct file *filp)
-{
- int ret;
- struct gasket_dev *gasket_dev;
- const struct gasket_driver_desc *driver_desc;
- struct gasket_ownership *ownership;
- char task_name[TASK_COMM_LEN];
- struct gasket_cdev_info *dev_info =
- container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
- struct pid_namespace *pid_ns = task_active_pid_ns(current);
- bool is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);
-
- gasket_dev = dev_info->gasket_dev_ptr;
- driver_desc = gasket_dev->internal_desc->driver_desc;
- ownership = &dev_info->ownership;
- get_task_comm(task_name, current);
- filp->private_data = gasket_dev;
- inode->i_size = 0;
-
- dev_dbg(gasket_dev->dev,
- "Attempting to open with tgid %u (%s) (f_mode: 0%03o, fmode_write: %d is_root: %u)\n",
- current->tgid, task_name, filp->f_mode,
- (filp->f_mode & FMODE_WRITE), is_root);
-
- /* Always allow non-writing accesses. */
- if (!(filp->f_mode & FMODE_WRITE)) {
- dev_dbg(gasket_dev->dev, "Allowing read-only opening.\n");
- return 0;
- }
-
- mutex_lock(&gasket_dev->mutex);
-
- dev_dbg(gasket_dev->dev,
- "Current owner open count (owning tgid %u): %d.\n",
- ownership->owner, ownership->write_open_count);
-
- /* Opening a node owned by another TGID is an error (unless root) */
- if (ownership->is_owned && ownership->owner != current->tgid &&
- !is_root) {
- dev_err(gasket_dev->dev,
- "Process %u is opening a node held by %u.\n",
- current->tgid, ownership->owner);
- mutex_unlock(&gasket_dev->mutex);
- return -EPERM;
- }
-
- /* If the node is not owned, assign it to the current TGID. */
- if (!ownership->is_owned) {
- ret = gasket_check_and_invoke_callback_nolock(gasket_dev,
- driver_desc->device_open_cb);
- if (ret) {
- dev_err(gasket_dev->dev,
- "Error in device open cb: %d\n", ret);
- mutex_unlock(&gasket_dev->mutex);
- return ret;
- }
- ownership->is_owned = 1;
- ownership->owner = current->tgid;
- dev_dbg(gasket_dev->dev, "Device owner is now tgid %u\n",
- ownership->owner);
- }
-
- ownership->write_open_count++;
-
- dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
- ownership->owner, ownership->write_open_count);
-
- mutex_unlock(&gasket_dev->mutex);
- return 0;
-}
-
-/*
- * Called on a close of the device file. If this process is the owner,
- * decrement the open count. On last close by the owner, free up buffers and
- * eventfd contexts, and release ownership.
- *
- * Returns 0 if successful, a negative error number otherwise.
- */
-static int gasket_release(struct inode *inode, struct file *file)
-{
- int i;
- struct gasket_dev *gasket_dev;
- struct gasket_ownership *ownership;
- const struct gasket_driver_desc *driver_desc;
- char task_name[TASK_COMM_LEN];
- struct gasket_cdev_info *dev_info =
- container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
- struct pid_namespace *pid_ns = task_active_pid_ns(current);
- bool is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);
-
- gasket_dev = dev_info->gasket_dev_ptr;
- driver_desc = gasket_dev->internal_desc->driver_desc;
- ownership = &dev_info->ownership;
- get_task_comm(task_name, current);
- mutex_lock(&gasket_dev->mutex);
-
- dev_dbg(gasket_dev->dev,
- "Releasing device node. Call origin: tgid %u (%s) (f_mode: 0%03o, fmode_write: %d, is_root: %u)\n",
- current->tgid, task_name, file->f_mode,
- (file->f_mode & FMODE_WRITE), is_root);
- dev_dbg(gasket_dev->dev, "Current open count (owning tgid %u): %d\n",
- ownership->owner, ownership->write_open_count);
-
- if (file->f_mode & FMODE_WRITE) {
- ownership->write_open_count--;
- if (ownership->write_open_count == 0) {
- dev_dbg(gasket_dev->dev, "Device is now free\n");
- ownership->is_owned = 0;
- ownership->owner = 0;
-
- /* Forces chip reset before we unmap the page tables. */
- driver_desc->device_reset_cb(gasket_dev);
-
- for (i = 0; i < driver_desc->num_page_tables; ++i) {
- gasket_page_table_unmap_all(gasket_dev->page_table[i]);
- gasket_page_table_garbage_collect(gasket_dev->page_table[i]);
- gasket_free_coherent_memory_all(gasket_dev, i);
- }
-
- /* Closes device, enters power save. */
- gasket_check_and_invoke_callback_nolock(gasket_dev,
- driver_desc->device_close_cb);
- }
- }
-
- dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
- ownership->owner, ownership->write_open_count);
- mutex_unlock(&gasket_dev->mutex);
- return 0;
-}
-
-/*
- * Gasket ioctl dispatch function.
- *
- * Check if the ioctl is a generic ioctl. If not, pass the ioctl to the
- * ioctl_handler_cb registered in the driver description.
- * If the ioctl is a generic ioctl, pass it to gasket_ioctl_handler.
- */
-static long gasket_ioctl(struct file *filp, uint cmd, ulong arg)
-{
- struct gasket_dev *gasket_dev;
- const struct gasket_driver_desc *driver_desc;
- void __user *argp = (void __user *)arg;
- char path[256];
-
- gasket_dev = (struct gasket_dev *)filp->private_data;
- driver_desc = gasket_dev->internal_desc->driver_desc;
- if (!driver_desc) {
- dev_dbg(gasket_dev->dev,
- "Unable to find device descriptor for file %s\n",
- d_path(&filp->f_path, path, 256));
- return -ENODEV;
- }
-
- if (!gasket_is_supported_ioctl(cmd)) {
- /*
- * The ioctl handler is not a standard Gasket callback, since
- * it requires different arguments. This means we can't use
- * check_and_invoke_callback.
- */
- if (driver_desc->ioctl_handler_cb)
- return driver_desc->ioctl_handler_cb(filp, cmd, argp);
-
- dev_dbg(gasket_dev->dev, "Received unknown ioctl 0x%x\n", cmd);
- return -EINVAL;
- }
-
- return gasket_handle_ioctl(filp, cmd, argp);
-}
-
-/* File operations for all Gasket devices. */
-static const struct file_operations gasket_file_ops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .mmap = gasket_mmap,
- .open = gasket_open,
- .release = gasket_release,
- .unlocked_ioctl = gasket_ioctl,
-};
-
-/* Perform final init and marks the device as active. */
-int gasket_enable_device(struct gasket_dev *gasket_dev)
-{
- int tbl_idx;
- int ret;
- const struct gasket_driver_desc *driver_desc =
- gasket_dev->internal_desc->driver_desc;
-
- ret = gasket_interrupt_init(gasket_dev);
- if (ret) {
- dev_err(gasket_dev->dev,
- "Critical failure to allocate interrupts: %d\n", ret);
- gasket_interrupt_cleanup(gasket_dev);
- return ret;
- }
-
- for (tbl_idx = 0; tbl_idx < driver_desc->num_page_tables; tbl_idx++) {
- dev_dbg(gasket_dev->dev, "Initializing page table %d.\n",
- tbl_idx);
- ret = gasket_page_table_init(&gasket_dev->page_table[tbl_idx],
- &gasket_dev->bar_data[driver_desc->page_table_bar_index],
- &driver_desc->page_table_configs[tbl_idx],
- gasket_dev->dev,
- gasket_dev->pci_dev);
- if (ret) {
- dev_err(gasket_dev->dev,
- "Couldn't init page table %d: %d\n",
- tbl_idx, ret);
- return ret;
- }
- /*
- * Make sure that the page table is clear and set to simple
- * addresses.
- */
- gasket_page_table_reset(gasket_dev->page_table[tbl_idx]);
- }
-
- /*
- * hardware_revision_cb returns a positive integer (the rev) if
- * successful.)
- */
- ret = check_and_invoke_callback(gasket_dev,
- driver_desc->hardware_revision_cb);
- if (ret < 0) {
- dev_err(gasket_dev->dev,
- "Error getting hardware revision: %d\n", ret);
- return ret;
- }
- gasket_dev->hardware_revision = ret;
-
- /* device_status_cb returns a device status, not an error code. */
- gasket_dev->status = gasket_get_hw_status(gasket_dev);
- if (gasket_dev->status == GASKET_STATUS_DEAD)
- dev_err(gasket_dev->dev, "Device reported as unhealthy.\n");
-
- ret = gasket_add_cdev(&gasket_dev->dev_info, &gasket_file_ops,
- driver_desc->module);
- if (ret)
- return ret;
-
- return 0;
-}
-EXPORT_SYMBOL(gasket_enable_device);
-
-static int __gasket_add_device(struct device *parent_dev,
- struct gasket_internal_desc *internal_desc,
- struct gasket_dev **gasket_devp)
-{
- int ret;
- struct gasket_dev *gasket_dev;
- const struct gasket_driver_desc *driver_desc =
- internal_desc->driver_desc;
-
- ret = gasket_alloc_dev(internal_desc, parent_dev, &gasket_dev);
- if (ret)
- return ret;
- if (IS_ERR(gasket_dev->dev_info.device)) {
- dev_err(parent_dev, "Cannot create %s device %s [ret = %ld]\n",
- driver_desc->name, gasket_dev->dev_info.name,
- PTR_ERR(gasket_dev->dev_info.device));
- ret = -ENODEV;
- goto free_gasket_dev;
- }
-
- ret = gasket_sysfs_create_mapping(gasket_dev->dev_info.device,
- gasket_dev);
- if (ret)
- goto remove_device;
-
- ret = gasket_sysfs_create_entries(gasket_dev->dev_info.device,
- gasket_sysfs_generic_attrs);
- if (ret)
- goto remove_sysfs_mapping;
-
- *gasket_devp = gasket_dev;
- return 0;
-
-remove_sysfs_mapping:
- gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
-remove_device:
- device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
-free_gasket_dev:
- gasket_free_dev(gasket_dev);
- return ret;
-}
-
-static void __gasket_remove_device(struct gasket_internal_desc *internal_desc,
- struct gasket_dev *gasket_dev)
-{
- gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
- device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
- gasket_free_dev(gasket_dev);
-}
-
-/*
- * Add PCI gasket device.
- *
- * Called by Gasket device probe function.
- * Allocates device metadata and maps device memory. The device driver must
- * call gasket_enable_device after driver init is complete to place the device
- * in active use.
- */
-int gasket_pci_add_device(struct pci_dev *pci_dev,
- struct gasket_dev **gasket_devp)
-{
- int ret;
- struct gasket_internal_desc *internal_desc;
- struct gasket_dev *gasket_dev;
- struct device *parent;
-
- dev_dbg(&pci_dev->dev, "add PCI gasket device\n");
-
- mutex_lock(&g_mutex);
- internal_desc = lookup_pci_internal_desc(pci_dev);
- mutex_unlock(&g_mutex);
- if (!internal_desc) {
- dev_err(&pci_dev->dev,
- "PCI add device called for unknown driver type\n");
- return -ENODEV;
- }
-
- parent = &pci_dev->dev;
- ret = __gasket_add_device(parent, internal_desc, &gasket_dev);
- if (ret)
- return ret;
-
- gasket_dev->pci_dev = pci_dev;
- ret = gasket_setup_pci(pci_dev, gasket_dev);
- if (ret)
- goto cleanup_pci;
-
- /*
- * Once we've created the mapping structures successfully, attempt to
- * create a symlink to the pci directory of this object.
- */
- ret = sysfs_create_link(&gasket_dev->dev_info.device->kobj,
- &pci_dev->dev.kobj, dev_name(&pci_dev->dev));
- if (ret) {
- dev_err(gasket_dev->dev,
- "Cannot create sysfs pci link: %d\n", ret);
- goto cleanup_pci;
- }
-
- *gasket_devp = gasket_dev;
- return 0;
-
-cleanup_pci:
- gasket_cleanup_pci(gasket_dev);
- __gasket_remove_device(internal_desc, gasket_dev);
- return ret;
-}
-EXPORT_SYMBOL(gasket_pci_add_device);
-
-/* Remove a PCI gasket device. */
-void gasket_pci_remove_device(struct pci_dev *pci_dev)
-{
- int i;
- struct gasket_internal_desc *internal_desc;
- struct gasket_dev *gasket_dev = NULL;
- /* Find the device desc. */
- mutex_lock(&g_mutex);
- internal_desc = lookup_pci_internal_desc(pci_dev);
- if (!internal_desc) {
- mutex_unlock(&g_mutex);
- return;
- }
- mutex_unlock(&g_mutex);
-
- /* Now find the specific device */
- mutex_lock(&internal_desc->mutex);
- for (i = 0; i < GASKET_DEV_MAX; i++) {
- if (internal_desc->devs[i] &&
- internal_desc->devs[i]->pci_dev == pci_dev) {
- gasket_dev = internal_desc->devs[i];
- break;
- }
- }
- mutex_unlock(&internal_desc->mutex);
-
- if (!gasket_dev)
- return;
-
- dev_dbg(gasket_dev->dev, "remove %s PCI gasket device\n",
- internal_desc->driver_desc->name);
-
- gasket_cleanup_pci(gasket_dev);
- __gasket_remove_device(internal_desc, gasket_dev);
-}
-EXPORT_SYMBOL(gasket_pci_remove_device);
-
-/**
- * Lookup a name by number in a num_name table.
- * @num: Number to lookup.
- * @table: Array of num_name structures, the table for the lookup.
- *
- * Description: Searches for num in the table. If found, the
- * corresponding name is returned; otherwise NULL
- * is returned.
- *
- * The table must have a NULL name pointer at the end.
- */
-const char *gasket_num_name_lookup(uint num,
- const struct gasket_num_name *table)
-{
- uint i = 0;
-
- while (table[i].snn_name) {
- if (num == table[i].snn_num)
- break;
- ++i;
- }
-
- return table[i].snn_name;
-}
-EXPORT_SYMBOL(gasket_num_name_lookup);
-
-int gasket_reset(struct gasket_dev *gasket_dev)
-{
- int ret;
-
- mutex_lock(&gasket_dev->mutex);
- ret = gasket_reset_nolock(gasket_dev);
- mutex_unlock(&gasket_dev->mutex);
- return ret;
-}
-EXPORT_SYMBOL(gasket_reset);
-
-int gasket_reset_nolock(struct gasket_dev *gasket_dev)
-{
- int ret;
- int i;
- const struct gasket_driver_desc *driver_desc;
-
- driver_desc = gasket_dev->internal_desc->driver_desc;
- if (!driver_desc->device_reset_cb)
- return 0;
-
- ret = driver_desc->device_reset_cb(gasket_dev);
- if (ret) {
- dev_dbg(gasket_dev->dev, "Device reset cb returned %d.\n",
- ret);
- return ret;
- }
-
- /* Reinitialize the page tables and interrupt framework. */
- for (i = 0; i < driver_desc->num_page_tables; ++i)
- gasket_page_table_reset(gasket_dev->page_table[i]);
-
- ret = gasket_interrupt_reinit(gasket_dev);
- if (ret) {
- dev_dbg(gasket_dev->dev, "Unable to reinit interrupts: %d.\n",
- ret);
- return ret;
- }
-
- /* Get current device health. */
- gasket_dev->status = gasket_get_hw_status(gasket_dev);
- if (gasket_dev->status == GASKET_STATUS_DEAD) {
- dev_dbg(gasket_dev->dev, "Device reported as dead.\n");
- return -EINVAL;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(gasket_reset_nolock);
-
-gasket_ioctl_permissions_cb_t
-gasket_get_ioctl_permissions_cb(struct gasket_dev *gasket_dev)
-{
- return gasket_dev->internal_desc->driver_desc->ioctl_permissions_cb;
-}
-EXPORT_SYMBOL(gasket_get_ioctl_permissions_cb);
-
-/* Get the driver structure for a given gasket_dev.
- * @dev: pointer to gasket_dev, implementing the requested driver.
- */
-const struct gasket_driver_desc *gasket_get_driver_desc(struct gasket_dev *dev)
-{
- return dev->internal_desc->driver_desc;
-}
-
-/* Get the device structure for a given gasket_dev.
- * @dev: pointer to gasket_dev, implementing the requested driver.
- */
-struct device *gasket_get_device(struct gasket_dev *dev)
-{
- return dev->dev;
-}
-
-/**
- * Asynchronously waits on device.
- * @gasket_dev: Device struct.
- * @bar: Bar
- * @offset: Register offset
- * @mask: Register mask
- * @val: Expected value
- * @max_retries: number of sleep periods
- * @delay_ms: Timeout in milliseconds
- *
- * Description: Busy waits for a specific combination of bits to be set on a
- * Gasket register.
- **/
-int gasket_wait_with_reschedule(struct gasket_dev *gasket_dev, int bar,
- u64 offset, u64 mask, u64 val,
- uint max_retries, u64 delay_ms)
-{
- uint retries = 0;
- u64 tmp;
-
- while (retries < max_retries) {
- tmp = gasket_dev_read_64(gasket_dev, bar, offset);
- if ((tmp & mask) == val)
- return 0;
- msleep(delay_ms);
- retries++;
- }
- dev_dbg(gasket_dev->dev, "%s timeout: reg %llx timeout (%llu ms)\n",
- __func__, offset, max_retries * delay_ms);
- return -ETIMEDOUT;
-}
-EXPORT_SYMBOL(gasket_wait_with_reschedule);
-
-/* See gasket_core.h for description. */
-int gasket_register_device(const struct gasket_driver_desc *driver_desc)
-{
- int i, ret;
- int desc_idx = -1;
- struct gasket_internal_desc *internal;
-
- pr_debug("Loading %s driver version %s\n", driver_desc->name,
- driver_desc->driver_version);
- /* Check for duplicates and find a free slot. */
- mutex_lock(&g_mutex);
-
- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- if (g_descs[i].driver_desc == driver_desc) {
- pr_err("%s driver already loaded/registered\n",
- driver_desc->name);
- mutex_unlock(&g_mutex);
- return -EBUSY;
- }
- }
-
- /* This and the above loop could be combined, but this reads easier. */
- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- if (!g_descs[i].driver_desc) {
- g_descs[i].driver_desc = driver_desc;
- desc_idx = i;
- break;
- }
- }
- mutex_unlock(&g_mutex);
-
- if (desc_idx == -1) {
- pr_err("too many drivers loaded, max %d\n",
- GASKET_FRAMEWORK_DESC_MAX);
- return -EBUSY;
- }
-
- internal = &g_descs[desc_idx];
- mutex_init(&internal->mutex);
- memset(internal->devs, 0, sizeof(struct gasket_dev *) * GASKET_DEV_MAX);
- internal->class =
- class_create(driver_desc->module, driver_desc->name);
-
- if (IS_ERR(internal->class)) {
- pr_err("Cannot register %s class [ret=%ld]\n",
- driver_desc->name, PTR_ERR(internal->class));
- ret = PTR_ERR(internal->class);
- goto unregister_gasket_driver;
- }
-
- ret = register_chrdev_region(MKDEV(driver_desc->major,
- driver_desc->minor), GASKET_DEV_MAX,
- driver_desc->name);
- if (ret) {
- pr_err("cannot register %s char driver [ret=%d]\n",
- driver_desc->name, ret);
- goto destroy_class;
- }
-
- return 0;
-
-destroy_class:
- class_destroy(internal->class);
-
-unregister_gasket_driver:
- mutex_lock(&g_mutex);
- g_descs[desc_idx].driver_desc = NULL;
- mutex_unlock(&g_mutex);
- return ret;
-}
-EXPORT_SYMBOL(gasket_register_device);
-
-/* See gasket_core.h for description. */
-void gasket_unregister_device(const struct gasket_driver_desc *driver_desc)
-{
- int i, desc_idx;
- struct gasket_internal_desc *internal_desc = NULL;
-
- mutex_lock(&g_mutex);
- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- if (g_descs[i].driver_desc == driver_desc) {
- internal_desc = &g_descs[i];
- desc_idx = i;
- break;
- }
- }
-
- if (!internal_desc) {
- mutex_unlock(&g_mutex);
- pr_err("request to unregister unknown desc: %s, %d:%d\n",
- driver_desc->name, driver_desc->major,
- driver_desc->minor);
- return;
- }
-
- unregister_chrdev_region(MKDEV(driver_desc->major, driver_desc->minor),
- GASKET_DEV_MAX);
-
- class_destroy(internal_desc->class);
-
- /* Finally, effectively "remove" the driver. */
- g_descs[desc_idx].driver_desc = NULL;
- mutex_unlock(&g_mutex);
-
- pr_debug("removed %s driver\n", driver_desc->name);
-}
-EXPORT_SYMBOL(gasket_unregister_device);
-
-static int __init gasket_init(void)
-{
- int i;
-
- mutex_lock(&g_mutex);
- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- g_descs[i].driver_desc = NULL;
- mutex_init(&g_descs[i].mutex);
- }
-
- gasket_sysfs_init();
-
- mutex_unlock(&g_mutex);
- return 0;
-}
-
-MODULE_DESCRIPTION("Google Gasket driver framework");
-MODULE_VERSION(GASKET_FRAMEWORK_VERSION);
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Rob Springer <rspringer@google.com>");
-module_init(gasket_init);