linux/drivers/staging/gasket/gasket_ioctl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (C) 2018 Google, Inc. */
   3#include "gasket.h"
   4#include "gasket_ioctl.h"
   5#include "gasket_constants.h"
   6#include "gasket_core.h"
   7#include "gasket_interrupt.h"
   8#include "gasket_page_table.h"
   9#include <linux/compiler.h>
  10#include <linux/device.h>
  11#include <linux/fs.h>
  12#include <linux/uaccess.h>
  13
  14#ifdef GASKET_KERNEL_TRACE_SUPPORT
  15#define CREATE_TRACE_POINTS
  16#include <trace/events/gasket_ioctl.h>
  17#else
  18#define trace_gasket_ioctl_entry(x, ...)
  19#define trace_gasket_ioctl_exit(x)
  20#define trace_gasket_ioctl_integer_data(x)
  21#define trace_gasket_ioctl_eventfd_data(x, ...)
  22#define trace_gasket_ioctl_page_table_data(x, ...)
  23#define trace_gasket_ioctl_config_coherent_allocator(x, ...)
  24#endif
  25
  26/* Associate an eventfd with an interrupt. */
  27static int gasket_set_event_fd(struct gasket_dev *gasket_dev,
  28                               struct gasket_interrupt_eventfd __user *argp)
  29{
  30        struct gasket_interrupt_eventfd die;
  31
  32        if (copy_from_user(&die, argp, sizeof(struct gasket_interrupt_eventfd)))
  33                return -EFAULT;
  34
  35        trace_gasket_ioctl_eventfd_data(die.interrupt, die.event_fd);
  36
  37        return gasket_interrupt_set_eventfd(
  38                gasket_dev->interrupt_data, die.interrupt, die.event_fd);
  39}
  40
  41/* Read the size of the page table. */
  42static int gasket_read_page_table_size(
  43        struct gasket_dev *gasket_dev,
  44        struct gasket_page_table_ioctl __user *argp)
  45{
  46        int ret = 0;
  47        struct gasket_page_table_ioctl ibuf;
  48
  49        if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
  50                return -EFAULT;
  51
  52        if (ibuf.page_table_index >= gasket_dev->num_page_tables)
  53                return -EFAULT;
  54
  55        ibuf.size = gasket_page_table_num_entries(
  56                gasket_dev->page_table[ibuf.page_table_index]);
  57
  58        trace_gasket_ioctl_page_table_data(
  59                ibuf.page_table_index, ibuf.size, ibuf.host_address,
  60                ibuf.device_address);
  61
  62        if (copy_to_user(argp, &ibuf, sizeof(ibuf)))
  63                return -EFAULT;
  64
  65        return ret;
  66}
  67
  68/* Read the size of the simple page table. */
  69static int gasket_read_simple_page_table_size(
  70        struct gasket_dev *gasket_dev,
  71        struct gasket_page_table_ioctl __user *argp)
  72{
  73        int ret = 0;
  74        struct gasket_page_table_ioctl ibuf;
  75
  76        if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
  77                return -EFAULT;
  78
  79        if (ibuf.page_table_index >= gasket_dev->num_page_tables)
  80                return -EFAULT;
  81
  82        ibuf.size =
  83                gasket_page_table_num_simple_entries(gasket_dev->page_table[ibuf.page_table_index]);
  84
  85        trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
  86                                           ibuf.host_address,
  87                                           ibuf.device_address);
  88
  89        if (copy_to_user(argp, &ibuf, sizeof(ibuf)))
  90                return -EFAULT;
  91
  92        return ret;
  93}
  94
  95/* Set the boundary between the simple and extended page tables. */
  96static int gasket_partition_page_table(
  97        struct gasket_dev *gasket_dev,
  98        struct gasket_page_table_ioctl __user *argp)
  99{
 100        int ret;
 101        struct gasket_page_table_ioctl ibuf;
 102        uint max_page_table_size;
 103
 104        if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
 105                return -EFAULT;
 106
 107        trace_gasket_ioctl_page_table_data(
 108                ibuf.page_table_index, ibuf.size, ibuf.host_address,
 109                ibuf.device_address);
 110
 111        if (ibuf.page_table_index >= gasket_dev->num_page_tables)
 112                return -EFAULT;
 113        max_page_table_size = gasket_page_table_max_size(
 114                gasket_dev->page_table[ibuf.page_table_index]);
 115
 116        if (ibuf.size > max_page_table_size) {
 117                dev_dbg(gasket_dev->dev,
 118                        "Partition request 0x%llx too large, max is 0x%x\n",
 119                        ibuf.size, max_page_table_size);
 120                return -EINVAL;
 121        }
 122
 123        mutex_lock(&gasket_dev->mutex);
 124
 125        ret = gasket_page_table_partition(
 126                gasket_dev->page_table[ibuf.page_table_index], ibuf.size);
 127        mutex_unlock(&gasket_dev->mutex);
 128
 129        return ret;
 130}
 131
 132/* Map a userspace buffer to a device virtual address. */
 133static int gasket_map_buffers(struct gasket_dev *gasket_dev,
 134                              struct gasket_page_table_ioctl __user *argp)
 135{
 136        struct gasket_page_table_ioctl ibuf;
 137
 138        if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
 139                return -EFAULT;
 140
 141        trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
 142                                           ibuf.host_address,
 143                                           ibuf.device_address);
 144
 145        if (ibuf.page_table_index >= gasket_dev->num_page_tables)
 146                return -EFAULT;
 147
 148        if (gasket_page_table_are_addrs_bad(gasket_dev->page_table[ibuf.page_table_index],
 149                                            ibuf.host_address,
 150                                            ibuf.device_address, ibuf.size))
 151                return -EINVAL;
 152
 153        return gasket_page_table_map(gasket_dev->page_table[ibuf.page_table_index],
 154                                     ibuf.host_address, ibuf.device_address,
 155                                     ibuf.size / PAGE_SIZE);
 156}
 157
 158/* Unmap a userspace buffer from a device virtual address. */
 159static int gasket_unmap_buffers(struct gasket_dev *gasket_dev,
 160                                struct gasket_page_table_ioctl __user *argp)
 161{
 162        struct gasket_page_table_ioctl ibuf;
 163
 164        if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
 165                return -EFAULT;
 166
 167        trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
 168                                           ibuf.host_address,
 169                                           ibuf.device_address);
 170
 171        if (ibuf.page_table_index >= gasket_dev->num_page_tables)
 172                return -EFAULT;
 173
 174        if (gasket_page_table_is_dev_addr_bad(gasket_dev->page_table[ibuf.page_table_index],
 175                                              ibuf.device_address, ibuf.size))
 176                return -EINVAL;
 177
 178        gasket_page_table_unmap(gasket_dev->page_table[ibuf.page_table_index],
 179                                ibuf.device_address, ibuf.size / PAGE_SIZE);
 180
 181        return 0;
 182}
 183
 184/*
 185 * Reserve structures for coherent allocation, and allocate or free the
 186 * corresponding memory.
 187 */
 188static int gasket_config_coherent_allocator(
 189        struct gasket_dev *gasket_dev,
 190        struct gasket_coherent_alloc_config_ioctl __user *argp)
 191{
 192        int ret;
 193        struct gasket_coherent_alloc_config_ioctl ibuf;
 194
 195        if (copy_from_user(&ibuf, argp,
 196                           sizeof(struct gasket_coherent_alloc_config_ioctl)))
 197                return -EFAULT;
 198
 199        trace_gasket_ioctl_config_coherent_allocator(ibuf.enable, ibuf.size,
 200                                                     ibuf.dma_address);
 201
 202        if (ibuf.page_table_index >= gasket_dev->num_page_tables)
 203                return -EFAULT;
 204
 205        if (ibuf.size > PAGE_SIZE * MAX_NUM_COHERENT_PAGES)
 206                return -ENOMEM;
 207
 208        if (ibuf.enable == 0) {
 209                ret = gasket_free_coherent_memory(gasket_dev, ibuf.size,
 210                                                  ibuf.dma_address,
 211                                                  ibuf.page_table_index);
 212        } else {
 213                ret = gasket_alloc_coherent_memory(gasket_dev, ibuf.size,
 214                                                   &ibuf.dma_address,
 215                                                   ibuf.page_table_index);
 216        }
 217        if (ret)
 218                return ret;
 219        if (copy_to_user(argp, &ibuf, sizeof(ibuf)))
 220                return -EFAULT;
 221
 222        return 0;
 223}
 224
 225/* Check permissions for Gasket ioctls. */
 226static bool gasket_ioctl_check_permissions(struct file *filp, uint cmd)
 227{
 228        bool alive;
 229        bool read, write;
 230        struct gasket_dev *gasket_dev = (struct gasket_dev *)filp->private_data;
 231
 232        alive = (gasket_dev->status == GASKET_STATUS_ALIVE);
 233        if (!alive)
 234                dev_dbg(gasket_dev->dev, "%s alive %d status %d\n",
 235                        __func__, alive, gasket_dev->status);
 236
 237        read = !!(filp->f_mode & FMODE_READ);
 238        write = !!(filp->f_mode & FMODE_WRITE);
 239
 240        switch (cmd) {
 241        case GASKET_IOCTL_RESET:
 242        case GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS:
 243                return write;
 244
 245        case GASKET_IOCTL_PAGE_TABLE_SIZE:
 246        case GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE:
 247        case GASKET_IOCTL_NUMBER_PAGE_TABLES:
 248                return read;
 249
 250        case GASKET_IOCTL_PARTITION_PAGE_TABLE:
 251        case GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR:
 252                return alive && write;
 253
 254        case GASKET_IOCTL_MAP_BUFFER:
 255        case GASKET_IOCTL_UNMAP_BUFFER:
 256                return alive && write;
 257
 258        case GASKET_IOCTL_CLEAR_EVENTFD:
 259        case GASKET_IOCTL_SET_EVENTFD:
 260                return alive && write;
 261        }
 262
 263        return false; /* unknown permissions */
 264}
 265
 266/*
 267 * standard ioctl dispatch function.
 268 * @filp: File structure pointer describing this node usage session.
 269 * @cmd: ioctl number to handle.
 270 * @argp: ioctl-specific data pointer.
 271 *
 272 * Standard ioctl dispatcher; forwards operations to individual handlers.
 273 */
 274long gasket_handle_ioctl(struct file *filp, uint cmd, void __user *argp)
 275{
 276        struct gasket_dev *gasket_dev;
 277        unsigned long arg = (unsigned long)argp;
 278        gasket_ioctl_permissions_cb_t ioctl_permissions_cb;
 279        int retval;
 280
 281        gasket_dev = (struct gasket_dev *)filp->private_data;
 282        trace_gasket_ioctl_entry(gasket_dev->dev_info.name, cmd);
 283
 284        ioctl_permissions_cb = gasket_get_ioctl_permissions_cb(gasket_dev);
 285        if (ioctl_permissions_cb) {
 286                retval = ioctl_permissions_cb(filp, cmd, argp);
 287                if (retval < 0) {
 288                        trace_gasket_ioctl_exit(retval);
 289                        return retval;
 290                } else if (retval == 0) {
 291                        trace_gasket_ioctl_exit(-EPERM);
 292                        return -EPERM;
 293                }
 294        } else if (!gasket_ioctl_check_permissions(filp, cmd)) {
 295                trace_gasket_ioctl_exit(-EPERM);
 296                dev_dbg(gasket_dev->dev, "ioctl cmd=%x noperm\n", cmd);
 297                return -EPERM;
 298        }
 299
 300        /* Tracing happens in this switch statement for all ioctls with
 301         * an integer argrument, but ioctls with a struct argument
 302         * that needs copying and decoding, that tracing is done within
 303         * the handler call.
 304         */
 305        switch (cmd) {
 306        case GASKET_IOCTL_RESET:
 307                retval = gasket_reset(gasket_dev);
 308                break;
 309        case GASKET_IOCTL_SET_EVENTFD:
 310                retval = gasket_set_event_fd(gasket_dev, argp);
 311                break;
 312        case GASKET_IOCTL_CLEAR_EVENTFD:
 313                trace_gasket_ioctl_integer_data(arg);
 314                retval =
 315                        gasket_interrupt_clear_eventfd(gasket_dev->interrupt_data,
 316                                                       (int)arg);
 317                break;
 318        case GASKET_IOCTL_PARTITION_PAGE_TABLE:
 319                trace_gasket_ioctl_integer_data(arg);
 320                retval = gasket_partition_page_table(gasket_dev, argp);
 321                break;
 322        case GASKET_IOCTL_NUMBER_PAGE_TABLES:
 323                trace_gasket_ioctl_integer_data(gasket_dev->num_page_tables);
 324                if (copy_to_user(argp, &gasket_dev->num_page_tables,
 325                                 sizeof(uint64_t)))
 326                        retval = -EFAULT;
 327                else
 328                        retval = 0;
 329                break;
 330        case GASKET_IOCTL_PAGE_TABLE_SIZE:
 331                retval = gasket_read_page_table_size(gasket_dev, argp);
 332                break;
 333        case GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE:
 334                retval = gasket_read_simple_page_table_size(gasket_dev, argp);
 335                break;
 336        case GASKET_IOCTL_MAP_BUFFER:
 337                retval = gasket_map_buffers(gasket_dev, argp);
 338                break;
 339        case GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR:
 340                retval = gasket_config_coherent_allocator(gasket_dev, argp);
 341                break;
 342        case GASKET_IOCTL_UNMAP_BUFFER:
 343                retval = gasket_unmap_buffers(gasket_dev, argp);
 344                break;
 345        case GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS:
 346                /* Clear interrupt counts doesn't take an arg, so use 0. */
 347                trace_gasket_ioctl_integer_data(0);
 348                retval = gasket_interrupt_reset_counts(gasket_dev);
 349                break;
 350        default:
 351                /* If we don't understand the ioctl, the best we can do is trace
 352                 * the arg.
 353                 */
 354                trace_gasket_ioctl_integer_data(arg);
 355                dev_dbg(gasket_dev->dev,
 356                        "Unknown ioctl cmd=0x%x not caught by gasket_is_supported_ioctl\n",
 357                        cmd);
 358                retval = -EINVAL;
 359                break;
 360        }
 361
 362        trace_gasket_ioctl_exit(retval);
 363        return retval;
 364}
 365
 366/*
 367 * Determines if an ioctl is part of the standard Gasket framework.
 368 * @cmd: The ioctl number to handle.
 369 *
 370 * Returns 1 if the ioctl is supported and 0 otherwise.
 371 */
 372long gasket_is_supported_ioctl(uint cmd)
 373{
 374        switch (cmd) {
 375        case GASKET_IOCTL_RESET:
 376        case GASKET_IOCTL_SET_EVENTFD:
 377        case GASKET_IOCTL_CLEAR_EVENTFD:
 378        case GASKET_IOCTL_PARTITION_PAGE_TABLE:
 379        case GASKET_IOCTL_NUMBER_PAGE_TABLES:
 380        case GASKET_IOCTL_PAGE_TABLE_SIZE:
 381        case GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE:
 382        case GASKET_IOCTL_MAP_BUFFER:
 383        case GASKET_IOCTL_UNMAP_BUFFER:
 384        case GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS:
 385        case GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR:
 386                return 1;
 387        default:
 388                return 0;
 389        }
 390}
 391