Commit e5553ac7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'staging-5.6-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging driver fixes from Greg KH:
 "Here are some small staging driver fixes for 5.6-rc3, along with the
  removal of an unused/unneeded driver as well.

  The android vsoc driver is not needed anymore by anyone, so it was
  removed.

  The other driver fixes are:
   - ashmem bugfixes
   - greybus audio driver bugfix
   - wireless driver bugfixes and tiny cleanups to error paths

  All of these have been in linux-next for a while now with no reported
  issues"

* tag 'staging-5.6-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging:
  staging: rtl8723bs: Remove unneeded goto statements
  staging: rtl8188eu: Remove some unneeded goto statements
  staging: rtl8723bs: Fix potential overuse of kernel memory
  staging: rtl8188eu: Fix potential overuse of kernel memory
  staging: rtl8723bs: Fix potential security hole
  staging: rtl8188eu: Fix potential security hole
  staging: greybus: use after free in gb_audio_manager_remove_all()
  staging: android: Delete the 'vsoc' driver
  staging: rtl8723bs: fix copy of overlapping memory
  staging: android: ashmem: Disallow ashmem memory from being remapped
  staging: vt6656: fix sign of rx_dbm to bb_pre_ed_rssi.
parents ef11f1b7 9a4556bd
......@@ -14,14 +14,6 @@ config ASHMEM
It is, in theory, a good memory allocator for low-memory devices,
because it can discard shared memory units when under memory pressure.
config ANDROID_VSOC
tristate "Android Virtual SoC support"
depends on PCI_MSI
help
This option adds support for the Virtual SoC driver needed to boot
a 'cuttlefish' Android image inside QEmu. The driver interacts with
a QEmu ivshmem device. If built as a module, it will be called vsoc.
source "drivers/staging/android/ion/Kconfig"
endif # if ANDROID
......
......@@ -4,4 +4,3 @@ ccflags-y += -I$(src) # needed for trace events
obj-y += ion/
obj-$(CONFIG_ASHMEM) += ashmem.o
obj-$(CONFIG_ANDROID_VSOC) += vsoc.o
......@@ -9,14 +9,5 @@ ion/
- Split /dev/ion up into multiple nodes (e.g. /dev/ion/heap0)
- Better test framework (integration with VGEM was suggested)
vsoc.c, uapi/vsoc_shm.h
- The current driver uses the same wait queue for all of the futexes in a
region. This will cause false wakeups in regions with a large number of
waiting threads. We should eventually use multiple queues and select the
queue based on the region.
- Add debugfs support for examining the permissions of regions.
- Remove VSOC_WAIT_FOR_INCOMING_INTERRUPT ioctl. This functionality has been
superseded by the futex and is there for legacy reasons.
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
Arve Hjønnevåg <arve@android.com> and Riley Andrews <riandrews@android.com>
......@@ -351,8 +351,23 @@ static inline vm_flags_t calc_vm_may_flags(unsigned long prot)
_calc_vm_trans(prot, PROT_EXEC, VM_MAYEXEC);
}
static int ashmem_vmfile_mmap(struct file *file, struct vm_area_struct *vma)
{
/* do not allow to mmap ashmem backing shmem file directly */
return -EPERM;
}
static unsigned long
ashmem_vmfile_get_unmapped_area(struct file *file, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags)
{
return current->mm->get_unmapped_area(file, addr, len, pgoff, flags);
}
static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
{
static struct file_operations vmfile_fops;
struct ashmem_area *asma = file->private_data;
int ret = 0;
......@@ -393,6 +408,19 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
}
vmfile->f_mode |= FMODE_LSEEK;
asma->file = vmfile;
/*
* override mmap operation of the vmfile so that it can't be
* remapped which would lead to creation of a new vma with no
* asma permission checks. Have to override get_unmapped_area
* as well to prevent VM_BUG_ON check for f_ops modification.
*/
if (!vmfile_fops.mmap) {
vmfile_fops = *vmfile->f_op;
vmfile_fops.mmap = ashmem_vmfile_mmap;
vmfile_fops.get_unmapped_area =
ashmem_vmfile_get_unmapped_area;
}
vmfile->f_op = &vmfile_fops;
}
get_file(asma->file);
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2017 Google, Inc.
*
*/
#ifndef _UAPI_LINUX_VSOC_SHM_H
#define _UAPI_LINUX_VSOC_SHM_H
#include <linux/types.h>
/**
* A permission is a token that permits a receiver to read and/or write an area
* of memory within a Vsoc region.
*
* An fd_scoped permission grants both read and write access, and can be
* attached to a file description (see open(2)).
* Ownership of the area can then be shared by passing a file descriptor
* among processes.
*
* begin_offset and end_offset define the area of memory that is controlled by
* the permission. owner_offset points to a word, also in shared memory, that
* controls ownership of the area.
*
* ownership of the region expires when the associated file description is
* released.
*
* At most one permission can be attached to each file description.
*
* This is useful when implementing HALs like gralloc that scope and pass
* ownership of shared resources via file descriptors.
*
* The caller is responsibe for doing any fencing.
*
* The calling process will normally identify a currently free area of
* memory. It will construct a proposed fd_scoped_permission_arg structure:
*
* begin_offset and end_offset describe the area being claimed
*
* owner_offset points to the location in shared memory that indicates the
* owner of the area.
*
* owned_value is the value that will be stored in owner_offset iff the
* permission can be granted. It must be different than VSOC_REGION_FREE.
*
* Two fd_scoped_permission structures are compatible if they vary only by
* their owned_value fields.
*
* The driver ensures that, for any group of simultaneous callers proposing
* compatible fd_scoped_permissions, it will accept exactly one of the
* propopsals. The other callers will get a failure with errno of EAGAIN.
*
* A process receiving a file descriptor can identify the region being
* granted using the VSOC_GET_FD_SCOPED_PERMISSION ioctl.
*/
struct fd_scoped_permission {
__u32 begin_offset;
__u32 end_offset;
__u32 owner_offset;
__u32 owned_value;
};
/*
* This value represents a free area of memory. The driver expects to see this
* value at owner_offset when creating a permission otherwise it will not do it,
* and will write this value back once the permission is no longer needed.
*/
#define VSOC_REGION_FREE ((__u32)0)
/**
* ioctl argument for VSOC_CREATE_FD_SCOPE_PERMISSION
*/
struct fd_scoped_permission_arg {
struct fd_scoped_permission perm;
__s32 managed_region_fd;
};
#define VSOC_NODE_FREE ((__u32)0)
/*
* Describes a signal table in shared memory. Each non-zero entry in the
* table indicates that the receiver should signal the futex at the given
* offset. Offsets are relative to the region, not the shared memory window.
*
* interrupt_signalled_offset is used to reliably signal interrupts across the
* vmm boundary. There are two roles: transmitter and receiver. For example,
* in the host_to_guest_signal_table the host is the transmitter and the
* guest is the receiver. The protocol is as follows:
*
* 1. The transmitter should convert the offset of the futex to an offset
* in the signal table [0, (1 << num_nodes_lg2))
* The transmitter can choose any appropriate hashing algorithm, including
* hash = futex_offset & ((1 << num_nodes_lg2) - 1)
*
* 3. The transmitter should atomically compare and swap futex_offset with 0
* at hash. There are 3 possible outcomes
* a. The swap fails because the futex_offset is already in the table.
* The transmitter should stop.
* b. Some other offset is in the table. This is a hash collision. The
* transmitter should move to another table slot and try again. One
* possible algorithm:
* hash = (hash + 1) & ((1 << num_nodes_lg2) - 1)
* c. The swap worked. Continue below.
*
* 3. The transmitter atomically swaps 1 with the value at the
* interrupt_signalled_offset. There are two outcomes:
* a. The prior value was 1. In this case an interrupt has already been
* posted. The transmitter is done.
* b. The prior value was 0, indicating that the receiver may be sleeping.
* The transmitter will issue an interrupt.
*
* 4. On waking the receiver immediately exchanges a 0 with the
* interrupt_signalled_offset. If it receives a 0 then this a spurious
* interrupt. That may occasionally happen in the current protocol, but
* should be rare.
*
* 5. The receiver scans the signal table by atomicaly exchanging 0 at each
* location. If a non-zero offset is returned from the exchange the
* receiver wakes all sleepers at the given offset:
* futex((int*)(region_base + old_value), FUTEX_WAKE, MAX_INT);
*
* 6. The receiver thread then does a conditional wait, waking immediately
* if the value at interrupt_signalled_offset is non-zero. This catches cases
* here additional signals were posted while the table was being scanned.
* On the guest the wait is handled via the VSOC_WAIT_FOR_INCOMING_INTERRUPT
* ioctl.
*/
struct vsoc_signal_table_layout {
/* log_2(Number of signal table entries) */
__u32 num_nodes_lg2;
/*
* Offset to the first signal table entry relative to the start of the
* region
*/
__u32 futex_uaddr_table_offset;
/*
* Offset to an atomic_t / atomic uint32_t. A non-zero value indicates
* that one or more offsets are currently posted in the table.
* semi-unique access to an entry in the table
*/
__u32 interrupt_signalled_offset;
};
#define VSOC_REGION_WHOLE ((__s32)0)
#define VSOC_DEVICE_NAME_SZ 16
/**
* Each HAL would (usually) talk to a single device region
* Mulitple entities care about these regions:
* - The ivshmem_server will populate the regions in shared memory
* - The guest kernel will read the region, create minor device nodes, and
* allow interested parties to register for FUTEX_WAKE events in the region
* - HALs will access via the minor device nodes published by the guest kernel
* - Host side processes will access the region via the ivshmem_server:
* 1. Pass name to ivshmem_server at a UNIX socket
* 2. ivshmemserver will reply with 2 fds:
* - host->guest doorbell fd
* - guest->host doorbell fd
* - fd for the shared memory region
* - region offset
* 3. Start a futex receiver thread on the doorbell fd pointed at the
* signal_nodes
*/
struct vsoc_device_region {
__u16 current_version;
__u16 min_compatible_version;
__u32 region_begin_offset;
__u32 region_end_offset;
__u32 offset_of_region_data;
struct vsoc_signal_table_layout guest_to_host_signal_table;
struct vsoc_signal_table_layout host_to_guest_signal_table;
/* Name of the device. Must always be terminated with a '\0', so
* the longest supported device name is 15 characters.
*/
char device_name[VSOC_DEVICE_NAME_SZ];
/* There are two ways that permissions to access regions are handled:
* - When subdivided_by is VSOC_REGION_WHOLE, any process that can
* open the device node for the region gains complete access to it.
* - When subdivided is set processes that open the region cannot
* access it. Access to a sub-region must be established by invoking
* the VSOC_CREATE_FD_SCOPE_PERMISSION ioctl on the region
* referenced in subdivided_by, providing a fileinstance
* (represented by a fd) opened on this region.
*/
__u32 managed_by;
};
/*
* The vsoc layout descriptor.
* The first 4K should be reserved for the shm header and region descriptors.
* The regions should be page aligned.
*/
struct vsoc_shm_layout_descriptor {
__u16 major_version;
__u16 minor_version;
/* size of the shm. This may be redundant but nice to have */
__u32 size;
/* number of shared memory regions */
__u32 region_count;
/* The offset to the start of region descriptors */
__u32 vsoc_region_desc_offset;
};
/*
* This specifies the current version that should be stored in
* vsoc_shm_layout_descriptor.major_version and
* vsoc_shm_layout_descriptor.minor_version.
* It should be updated only if the vsoc_device_region and
* vsoc_shm_layout_descriptor structures have changed.
* Versioning within each region is transferred
* via the min_compatible_version and current_version fields in
* vsoc_device_region. The driver does not consult these fields: they are left
* for the HALs and host processes and will change independently of the layout
* version.
*/
#define CURRENT_VSOC_LAYOUT_MAJOR_VERSION 2
#define CURRENT_VSOC_LAYOUT_MINOR_VERSION 0
#define VSOC_CREATE_FD_SCOPED_PERMISSION \
_IOW(0xF5, 0, struct fd_scoped_permission)
#define VSOC_GET_FD_SCOPED_PERMISSION _IOR(0xF5, 1, struct fd_scoped_permission)
/*
* This is used to signal the host to scan the guest_to_host_signal_table
* for new futexes to wake. This sends an interrupt if one is not already
* in flight.
*/
#define VSOC_MAYBE_SEND_INTERRUPT_TO_HOST _IO(0xF5, 2)
/*
* When this returns the guest will scan host_to_guest_signal_table to
* check for new futexes to wake.
*/
/* TODO(ghartman): Consider moving this to the bottom half */
#define VSOC_WAIT_FOR_INCOMING_INTERRUPT _IO(0xF5, 3)
/*
* Guest HALs will use this to retrieve the region description after
* opening their device node.
*/
#define VSOC_DESCRIBE_REGION _IOR(0xF5, 4, struct vsoc_device_region)
/*
* Wake any threads that may be waiting for a host interrupt on this region.
* This is mostly used during shutdown.
*/
#define VSOC_SELF_INTERRUPT _IO(0xF5, 5)
/*
* This is used to signal the host to scan the guest_to_host_signal_table
* for new futexes to wake. This sends an interrupt unconditionally.
*/
#define VSOC_SEND_INTERRUPT_TO_HOST _IO(0xF5, 6)
enum wait_types {
VSOC_WAIT_UNDEFINED = 0,
VSOC_WAIT_IF_EQUAL = 1,
VSOC_WAIT_IF_EQUAL_TIMEOUT = 2
};
/*
* Wait for a condition to be true
*
* Note, this is sized and aligned so the 32 bit and 64 bit layouts are
* identical.
*/
struct vsoc_cond_wait {
/* Input: Offset of the 32 bit word to check */
__u32 offset;
/* Input: Value that will be compared with the offset */
__u32 value;
/* Monotonic time to wake at in seconds */
__u64 wake_time_sec;
/* Input: Monotonic time to wait in nanoseconds */
__u32 wake_time_nsec;
/* Input: Type of wait */
__u32 wait_type;
/* Output: Number of times the thread woke before returning. */
__u32 wakes;
/* Ensure that we're 8-byte aligned and 8 byte length for 32/64 bit
* compatibility.
*/
__u32 reserved_1;
};
#define VSOC_COND_WAIT _IOWR(0xF5, 7, struct vsoc_cond_wait)
/* Wake any local threads waiting at the offset given in arg */
#define VSOC_COND_WAKE _IO(0xF5, 8)
#endif /* _UAPI_LINUX_VSOC_SHM_H */
// SPDX-License-Identifier: GPL-2.0
/*
* drivers/android/staging/vsoc.c
*
* Android Virtual System on a Chip (VSoC) driver
*
* Copyright (C) 2017 Google, Inc.
*
* Author: ghartman@google.com
*
* Based on drivers/char/kvm_ivshmem.c - driver for KVM Inter-VM shared memory
* Copyright 2009 Cam Macdonell <cam@cs.ualberta.ca>
*
* Based on cirrusfb.c and 8139cp.c:
* Copyright 1999-2001 Jeff Garzik
* Copyright 2001-2004 Jeff Garzik
*/
#include <linux/dma-mapping.h>
#include <linux/freezer.h>
#include <linux/futex.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/file.h>
#include "uapi/vsoc_shm.h"
#define VSOC_DEV_NAME "vsoc"
/*
* Description of the ivshmem-doorbell PCI device used by QEmu. These
* constants follow docs/specs/ivshmem-spec.txt, which can be found in
* the QEmu repository. This was last reconciled with the version that
* came out with 2.8
*/
/*
* These constants are determined KVM Inter-VM shared memory device
* register offsets
*/
enum {
INTR_MASK = 0x00, /* Interrupt Mask */
INTR_STATUS = 0x04, /* Interrupt Status */
IV_POSITION = 0x08, /* VM ID */
DOORBELL = 0x0c, /* Doorbell */
};
static const int REGISTER_BAR; /* Equal to 0 */
static const int MAX_REGISTER_BAR_LEN = 0x100;
/*
* The MSI-x BAR is not used directly.
*
* static const int MSI_X_BAR = 1;
*/
static const int SHARED_MEMORY_BAR = 2;
struct vsoc_region_data {
char name[VSOC_DEVICE_NAME_SZ + 1];
wait_queue_head_t interrupt_wait_queue;
/* TODO(b/73664181): Use multiple futex wait queues */
wait_queue_head_t futex_wait_queue;
/* Flag indicating that an interrupt has been signalled by the host. */
atomic_t *incoming_signalled;
/* Flag indicating the guest has signalled the host. */
atomic_t *outgoing_signalled;
bool irq_requested;
bool device_created;
};
struct vsoc_device {
/* Kernel virtual address of REGISTER_BAR. */
void __iomem *regs;
/* Physical address of SHARED_MEMORY_BAR. */
phys_addr_t shm_phys_start;
/* Kernel virtual address of SHARED_MEMORY_BAR. */
void __iomem *kernel_mapped_shm;
/* Size of the entire shared memory window in bytes. */
size_t shm_size;
/*
* Pointer to the virtual address of the shared memory layout structure.
* This is probably identical to kernel_mapped_shm, but saving this
* here saves a lot of annoying casts.
*/
struct vsoc_shm_layout_descriptor *layout;
/*
* Points to a table of region descriptors in the kernel's virtual
* address space. Calculated from
* vsoc_shm_layout_descriptor.vsoc_region_desc_offset
*/
struct vsoc_device_region *regions;
/* Head of a list of permissions that have been granted. */
struct list_head permissions;
struct pci_dev *dev;
/* Per-region (and therefore per-interrupt) information. */
struct vsoc_region_data *regions_data;
/*
* Table of msi-x entries. This has to be separated from struct
* vsoc_region_data because the kernel deals with them as an array.
*/
struct msix_entry *msix_entries;
/* Mutex that protectes the permission list */
struct mutex mtx;
/* Major number assigned by the kernel */
int major;
/* Character device assigned by the kernel */
struct cdev cdev;
/* Device class assigned by the kernel */
struct class *class;
/*
* Flags that indicate what we've initialized. These are used to do an
* orderly cleanup of the device.
*/
bool enabled_device;
bool requested_regions;
bool cdev_added;
bool class_added;
bool msix_enabled;
};
static struct vsoc_device vsoc_dev;
/*
* TODO(ghartman): Add a /sys filesystem entry that summarizes the permissions.
*/
struct fd_scoped_permission_node {
struct fd_scoped_permission permission;
struct list_head list;
};
struct vsoc_private_data {
struct fd_scoped_permission_node *fd_scoped_permission_node;
};
static long vsoc_ioctl(struct file *, unsigned int, unsigned long);
static int vsoc_mmap(struct file *, struct vm_area_struct *);
static int vsoc_open(struct inode *, struct file *);
static int vsoc_release(struct inode *, struct file *);
static ssize_t vsoc_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t vsoc_write(struct file *, const char __user *, size_t, loff_t *);
static loff_t vsoc_lseek(struct file *filp, loff_t offset, int origin);
static int
do_create_fd_scoped_permission(struct vsoc_device_region *region_p,
struct fd_scoped_permission_node *np,
struct fd_scoped_permission_arg __user *arg);
static void
do_destroy_fd_scoped_permission(struct vsoc_device_region *owner_region_p,
struct fd_scoped_permission *perm);
static long do_vsoc_describe_region(struct file *,
struct vsoc_device_region __user *);
static ssize_t vsoc_get_area(struct file *filp, __u32 *perm_off);
/**
* Validate arguments on entry points to the driver.
*/
inline int vsoc_validate_inode(struct inode *inode)
{
if (iminor(inode) >= vsoc_dev.layout->region_count) {
dev_err(&vsoc_dev.dev->dev,
"describe_region: invalid region %d\n", iminor(inode));
return -ENODEV;
}
return 0;
}
inline int vsoc_validate_filep(struct file *filp)
{
int ret = vsoc_validate_inode(file_inode(filp));
if (ret)
return ret;
if (!filp->private_data) {
dev_err(&vsoc_dev.dev->dev,
"No private data on fd, region %d\n",
iminor(file_inode(filp)));
return -EBADFD;
}
return 0;
}
/* Converts from shared memory offset to virtual address */
static inline void *shm_off_to_virtual_addr(__u32 offset)
{
return (void __force *)vsoc_dev.kernel_mapped_shm + offset;
}
/* Converts from shared memory offset to physical address */
static inline phys_addr_t shm_off_to_phys_addr(__u32 offset)
{
return vsoc_dev.shm_phys_start + offset;
}
/**
* Convenience functions to obtain the region from the inode or file.
* Dangerous to call before validating the inode/file.
*/
static
inline struct vsoc_device_region *vsoc_region_from_inode(struct inode *inode)
{
return &vsoc_dev.regions[iminor(inode)];
}
static
inline struct vsoc_device_region *vsoc_region_from_filep(struct file *inode)
{
return vsoc_region_from_inode(file_inode(inode));
}
static inline uint32_t vsoc_device_region_size(struct vsoc_device_region *r)
{
return r->region_end_offset - r->region_begin_offset;
}
static const struct file_operations vsoc_ops = {
.owner = THIS_MODULE,
.open = vsoc_open,
.mmap = vsoc_mmap,
.read = vsoc_read,
.unlocked_ioctl = vsoc_ioctl,
.compat_ioctl = vsoc_ioctl,
.write = vsoc_write,
.llseek = vsoc_lseek,
.release = vsoc_release,
};
static struct pci_device_id vsoc_id_table[] = {
{0x1af4, 0x1110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0},
};
MODULE_DEVICE_TABLE(pci, vsoc_id_table);
static void vsoc_remove_device(struct pci_dev *pdev);
static int vsoc_probe_device(struct pci_dev *pdev,
const struct pci_device_id *ent);
static struct pci_driver vsoc_pci_driver = {
.name = "vsoc",
.id_table = vsoc_id_table,
.probe = vsoc_probe_device,
.remove = vsoc_remove_device,
};
static int
do_create_fd_scoped_permission(struct vsoc_device_region *region_p,
struct fd_scoped_permission_node *np,
struct fd_scoped_permission_arg __user *arg)
{
struct file *managed_filp;
s32 managed_fd;
atomic_t *owner_ptr = NULL;
struct vsoc_device_region *managed_region_p;
if (copy_from_user(&np->permission,
&arg->perm, sizeof(np->permission)) ||
copy_from_user(&managed_fd,
&arg->managed_region_fd, sizeof(managed_fd))) {
return -EFAULT;
}
managed_filp = fdget(managed_fd).file;
/* Check that it's a valid fd, */
if (!managed_filp || vsoc_validate_filep(managed_filp))
return -EPERM;
/* EEXIST if the given fd already has a permission. */
if (((struct vsoc_private_data *)managed_filp->private_data)->
fd_scoped_permission_node)
return -EEXIST;
managed_region_p = vsoc_region_from_filep(managed_filp);
/* Check that the provided region is managed by this one */
if (&vsoc_dev.regions[managed_region_p->managed_by] != region_p)
return -EPERM;
/* The area must be well formed and have non-zero size */
if (np->permission.begin_offset >= np->permission.end_offset)
return -EINVAL;
/* The area must fit in the memory window */
if (np->permission.end_offset >
vsoc_device_region_size(managed_region_p))
return -ERANGE;
/* The area must be in the region data section */
if (np->permission.begin_offset <
managed_region_p->offset_of_region_data)
return -ERANGE;
/* The area must be page aligned */
if (!PAGE_ALIGNED(np->permission.begin_offset) ||
!PAGE_ALIGNED(np->permission.end_offset))
return -EINVAL;
/* Owner offset must be naturally aligned in the window */
if (np->permission.owner_offset &
(sizeof(np->permission.owner_offset) - 1))
return -EINVAL;
/* The owner flag must reside in the owner memory */
if (np->permission.owner_offset + sizeof(np->permission.owner_offset) >
vsoc_device_region_size(region_p))
return -ERANGE;
/* The owner flag must reside in the data section */
if (np->permission.owner_offset < region_p->offset_of_region_data)
return -EINVAL;
/* The owner value must change to claim the memory */
if (np->permission.owned_value == VSOC_REGION_FREE)
return -EINVAL;
owner_ptr =
(atomic_t *)shm_off_to_virtual_addr(region_p->region_begin_offset +
np->permission.owner_offset);
/* We've already verified that this is in the shared memory window, so
* it should be safe to write to this address.
*/
if (atomic_cmpxchg(owner_ptr,
VSOC_REGION_FREE,
np->permission.owned_value) != VSOC_REGION_FREE) {
return -EBUSY;
}
((struct vsoc_private_data *)managed_filp->private_data)->
fd_scoped_permission_node = np;
/* The file offset needs to be adjusted if the calling
* process did any read/write operations on the fd
* before creating the permission.
*/
if (managed_filp->f_pos) {
if (managed_filp->f_pos > np->permission.end_offset) {
/* If the offset is beyond the permission end, set it
* to the end.
*/
managed_filp->f_pos = np->permission.end_offset;
} else {
/* If the offset is within the permission interval
* keep it there otherwise reset it to zero.
*/
if (managed_filp->f_pos < np->permission.begin_offset) {
managed_filp->f_pos = 0;
} else {
managed_filp->f_pos -=
np->permission.begin_offset;
}
}
}
return 0;
}
static void
do_destroy_fd_scoped_permission_node(struct vsoc_device_region *owner_region_p,
struct fd_scoped_permission_node *node)
{
if (node) {
do_destroy_fd_scoped_permission(owner_region_p,
&node->permission);
mutex_lock(&vsoc_dev.mtx);
list_del(&node->list);
mutex_unlock(&vsoc_dev.mtx);
kfree(node);
}
}
static void
do_destroy_fd_scoped_permission(struct vsoc_device_region *owner_region_p,
struct fd_scoped_permission *perm)
{
atomic_t *owner_ptr = NULL;
int prev = 0;
if (!perm)
return;
owner_ptr = (atomic_t *)shm_off_to_virtual_addr
(owner_region_p->region_begin_offset + perm->owner_offset);
prev = atomic_xchg(owner_ptr, VSOC_REGION_FREE);
if (prev != perm->owned_value)
dev_err(&vsoc_dev.dev->dev,
"%x-%x: owner (%s) %x: expected to be %x was %x",
perm->begin_offset, perm->end_offset,
owner_region_p->device_name, perm->owner_offset,
perm->owned_value, prev);
}
static long do_vsoc_describe_region(struct file *filp,
struct vsoc_device_region __user *dest)
{
struct vsoc_device_region *region_p;
int retval = vsoc_validate_filep(filp);
if (retval)
return retval;
region_p = vsoc_region_from_filep(filp);
if (copy_to_user(dest, region_p, sizeof(*region_p)))
return -EFAULT;
return 0;
}
/**
* Implements the inner logic of cond_wait. Copies to and from userspace are
* done in the helper function below.
*/
static int handle_vsoc_cond_wait(struct file *filp, struct vsoc_cond_wait *arg)
{
DEFINE_WAIT(wait);
u32 region_number = iminor(file_inode(filp));
struct vsoc_region_data *data = vsoc_dev.regions_data + region_number;
struct hrtimer_sleeper timeout, *to = NULL;
int ret = 0;
struct vsoc_device_region *region_p = vsoc_region_from_filep(filp);
atomic_t *address = NULL;
ktime_t wake_time;
/* Ensure that the offset is aligned */
if (arg->offset & (sizeof(uint32_t) - 1))
return -EADDRNOTAVAIL;
/* Ensure that the offset is within shared memory */
if (((uint64_t)arg->offset) + region_p->region_begin_offset +
sizeof(uint32_t) > region_p->region_end_offset)
return -E2BIG;
address = shm_off_to_virtual_addr(region_p->region_begin_offset +
arg->offset);
/* Ensure that the type of wait is valid */
switch (arg->wait_type) {
case VSOC_WAIT_IF_EQUAL:
break;
case VSOC_WAIT_IF_EQUAL_TIMEOUT:
to = &timeout;
break;
default:
return -EINVAL;
}
if (to) {
/* Copy the user-supplied timesec into the kernel structure.
* We do things this way to flatten differences between 32 bit
* and 64 bit timespecs.
*/
if (arg->wake_time_nsec >= NSEC_PER_SEC)
return -EINVAL;
wake_time = ktime_set(arg->wake_time_sec, arg->wake_time_nsec);
hrtimer_init_sleeper_on_stack(to, CLOCK_MONOTONIC,
HRTIMER_MODE_ABS);
hrtimer_set_expires_range_ns(&to->timer, wake_time,
current->timer_slack_ns);
}
while (1) {
prepare_to_wait(&data->futex_wait_queue, &wait,
TASK_INTERRUPTIBLE);
/*
* Check the sentinel value after prepare_to_wait. If the value
* changes after this check the writer will call signal,
* changing the task state from INTERRUPTIBLE to RUNNING. That
* will ensure that schedule() will eventually schedule this
* task.
*/
if (atomic_read(address) != arg->value) {
ret = 0;
break;
}
if (to) {
hrtimer_sleeper_start_expires(to, HRTIMER_MODE_ABS);
if (likely(to->task))
freezable_schedule();
hrtimer_cancel(&to->timer);
if (!to->task) {
ret = -ETIMEDOUT;
break;
}
} else {
freezable_schedule();
}
/* Count the number of times that we woke up. This is useful
* for unit testing.
*/
++arg->wakes;
if (signal_pending(current)) {
ret = -EINTR;
break;
}
}
finish_wait(&data->futex_wait_queue, &wait);
if (to)
destroy_hrtimer_on_stack(&to->timer);
return ret;
}
/**
* Handles the details of copying from/to userspace to ensure that the copies
* happen on all of the return paths of cond_wait.
*/
static int do_vsoc_cond_wait(struct file *filp,
struct vsoc_cond_wait __user *untrusted_in)
{
struct vsoc_cond_wait arg;
int rval = 0;
if (copy_from_user(&arg, untrusted_in, sizeof(arg)))
return -EFAULT;
/* wakes is an out parameter. Initialize it to something sensible. */
arg.wakes = 0;
rval = handle_vsoc_cond_wait(filp, &arg);
if (copy_to_user(untrusted_in, &arg, sizeof(arg)))
return -EFAULT;
return rval;
}
static int do_vsoc_cond_wake(struct file *filp, uint32_t offset)
{
struct vsoc_device_region *region_p = vsoc_region_from_filep(filp);
u32 region_number = iminor(file_inode(filp));
struct vsoc_region_data *data = vsoc_dev.regions_data + region_number;
/* Ensure that the offset is aligned */
if (offset & (sizeof(uint32_t) - 1))
return -EADDRNOTAVAIL;
/* Ensure that the offset is within shared memory */
if (((uint64_t)offset) + region_p->region_begin_offset +
sizeof(uint32_t) > region_p->region_end_offset)
return -E2BIG;
/*
* TODO(b/73664181): Use multiple futex wait queues.
* We need to wake every sleeper when the condition changes. Typically
* only a single thread will be waiting on the condition, but there
* are exceptions. The worst case is about 10 threads.
*/
wake_up_interruptible_all(&data->futex_wait_queue);
return 0;
}
static long vsoc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int rv = 0;
struct vsoc_device_region *region_p;
u32 reg_num;
struct vsoc_region_data *reg_data;
int retval = vsoc_validate_filep(filp);
if (retval)
return retval;
region_p = vsoc_region_from_filep(filp);
reg_num = iminor(file_inode(filp));
reg_data = vsoc_dev.regions_data + reg_num;
switch (cmd) {
case VSOC_CREATE_FD_SCOPED_PERMISSION:
{
struct fd_scoped_permission_node *node = NULL;
node = kzalloc(sizeof(*node), GFP_KERNEL);
/* We can't allocate memory for the permission */
if (!node)
return -ENOMEM;
INIT_LIST_HEAD(&node->list);
rv = do_create_fd_scoped_permission
(region_p,
node,
(struct fd_scoped_permission_arg __user *)arg);
if (!rv) {
mutex_lock(&vsoc_dev.mtx);
list_add(&node->list, &vsoc_dev.permissions);
mutex_unlock(&vsoc_dev.mtx);
} else {
kfree(node);
return rv;
}
}
break;
case VSOC_GET_FD_SCOPED_PERMISSION:
{
struct fd_scoped_permission_node *node =
((struct vsoc_private_data *)filp->private_data)->
fd_scoped_permission_node;
if (!node)
return -ENOENT;
if (copy_to_user
((struct fd_scoped_permission __user *)arg,
&node->permission, sizeof(node->permission)))
return -EFAULT;
}
break;
case VSOC_MAYBE_SEND_INTERRUPT_TO_HOST:
if (!atomic_xchg(reg_data->outgoing_signalled, 1)) {
writel(reg_num, vsoc_dev.regs + DOORBELL);
return 0;
} else {
return -EBUSY;
}
break;
case VSOC_SEND_INTERRUPT_TO_HOST:
writel(reg_num, vsoc_dev.regs + DOORBELL);
return 0;
case VSOC_WAIT_FOR_INCOMING_INTERRUPT:
wait_event_interruptible
(reg_data->interrupt_wait_queue,
(atomic_read(reg_data->incoming_signalled) != 0));
break;
case VSOC_DESCRIBE_REGION:
return do_vsoc_describe_region
(filp,
(struct vsoc_device_region __user *)arg);
case VSOC_SELF_INTERRUPT:
atomic_set(reg_data->incoming_signalled, 1);
wake_up_interruptible(&reg_data->interrupt_wait_queue);
break;
case VSOC_COND_WAIT:
return do_vsoc_cond_wait(filp,
(struct vsoc_cond_wait __user *)arg);
case VSOC_COND_WAKE:
return do_vsoc_cond_wake(filp, arg);
default:
return -EINVAL;
}
return 0;
}
static ssize_t vsoc_read(struct file *filp, char __user *buffer, size_t len,
loff_t *poffset)
{
__u32 area_off;
const void *area_p;
ssize_t area_len;
int retval = vsoc_validate_filep(filp);
if (retval)
return retval;
area_len = vsoc_get_area(filp, &area_off);
area_p = shm_off_to_virtual_addr(area_off);
area_p += *poffset;
area_len -= *poffset;
if (area_len <= 0)
return 0;
if (area_len < len)
len = area_len;
if (copy_to_user(buffer, area_p, len))
return -EFAULT;
*poffset += len;
return len;
}
static loff_t vsoc_lseek(struct file *filp, loff_t offset, int origin)
{
ssize_t area_len = 0;
int retval = vsoc_validate_filep(filp);
if (retval)
return retval;
area_len = vsoc_get_area(filp, NULL);
switch (origin) {
case SEEK_SET:
break;
case SEEK_CUR:
if (offset > 0 && offset + filp->f_pos < 0)
return -EOVERFLOW;
offset += filp->f_pos;
break;
case SEEK_END:
if (offset > 0 && offset + area_len < 0)
return -EOVERFLOW;
offset += area_len;
break;
case SEEK_DATA:
if (offset >= area_len)
return -EINVAL;
if (offset < 0)
offset = 0;
break;
case SEEK_HOLE:
/* Next hole is always the end of the region, unless offset is
* beyond that
*/
if (offset < area_len)
offset = area_len;
break;
default:
return -EINVAL;
}
if (offset < 0 || offset > area_len)
return -EINVAL;
filp->f_pos = offset;
return offset;
}
static ssize_t vsoc_write(struct file *filp, const char __user *buffer,
size_t len, loff_t *poffset)
{
__u32 area_off;
void *area_p;
ssize_t area_len;
int retval = vsoc_validate_filep(filp);
if (retval)
return retval;
area_len = vsoc_get_area(filp, &area_off);
area_p = shm_off_to_virtual_addr(area_off);
area_p += *poffset;
area_len -= *poffset;
if (area_len <= 0)
return 0;
if (area_len < len)
len = area_len;
if (copy_from_user(area_p, buffer, len))
return -EFAULT;
*poffset += len;
return len;
}
static irqreturn_t vsoc_interrupt(int irq, void *region_data_v)
{
struct vsoc_region_data *region_data =
(struct vsoc_region_data *)region_data_v;
int reg_num = region_data - vsoc_dev.regions_data;
if (unlikely(!region_data))
return IRQ_NONE;
if (unlikely(reg_num < 0 ||
reg_num >= vsoc_dev.layout->region_count)) {
dev_err(&vsoc_dev.dev->dev,
"invalid irq @%p reg_num=0x%04x\n",
region_data, reg_num);
return IRQ_NONE;
}
if (unlikely(vsoc_dev.regions_data + reg_num != region_data)) {
dev_err(&vsoc_dev.dev->dev,
"irq not aligned @%p reg_num=0x%04x\n",
region_data, reg_num);
return IRQ_NONE;
}
wake_up_interruptible(&region_data->interrupt_wait_queue);
return IRQ_HANDLED;
}
static int vsoc_probe_device(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int result;
int i;
resource_size_t reg_size;
dev_t devt;
vsoc_dev.dev = pdev;
result = pci_enable_device(pdev);
if (result) {
dev_err(&pdev->dev,
"pci_enable_device failed %s: error %d\n",
pci_name(pdev), result);
return result;
}
vsoc_dev.enabled_device = true;
result = pci_request_regions(pdev, "vsoc");
if (result < 0) {
dev_err(&pdev->dev, "pci_request_regions failed\n");
vsoc_remove_device(pdev);
return -EBUSY;
}
vsoc_dev.requested_regions = true;
/* Set up the control registers in BAR 0 */
reg_size = pci_resource_len(pdev, REGISTER_BAR);
if (reg_size > MAX_REGISTER_BAR_LEN)
vsoc_dev.regs =
pci_iomap(pdev, REGISTER_BAR, MAX_REGISTER_BAR_LEN);
else
vsoc_dev.regs = pci_iomap(pdev, REGISTER_BAR, reg_size);
if (!vsoc_dev.regs) {
dev_err(&pdev->dev,
"cannot map registers of size %zu\n",
(size_t)reg_size);
vsoc_remove_device(pdev);
return -EBUSY;
}
/* Map the shared memory in BAR 2 */
vsoc_dev.shm_phys_start = pci_resource_start(pdev, SHARED_MEMORY_BAR);
vsoc_dev.shm_size = pci_resource_len(pdev, SHARED_MEMORY_BAR);
dev_info(&pdev->dev, "shared memory @ DMA %pa size=0x%zx\n",
&vsoc_dev.shm_phys_start, vsoc_dev.shm_size);
vsoc_dev.kernel_mapped_shm = pci_iomap_wc(pdev, SHARED_MEMORY_BAR, 0);
if (!vsoc_dev.kernel_mapped_shm) {
dev_err(&vsoc_dev.dev->dev, "cannot iomap region\n");
vsoc_remove_device(pdev);
return -EBUSY;
}
vsoc_dev.layout = (struct vsoc_shm_layout_descriptor __force *)
vsoc_dev.kernel_mapped_shm;
dev_info(&pdev->dev, "major_version: %d\n",
vsoc_dev.layout->major_version);
dev_info(&pdev->dev, "minor_version: %d\n",
vsoc_dev.layout->minor_version);
dev_info(&pdev->dev, "size: 0x%x\n", vsoc_dev.layout->size);
dev_info(&pdev->dev, "regions: %d\n", vsoc_dev.layout->region_count);
if (vsoc_dev.layout->major_version !=
CURRENT_VSOC_LAYOUT_MAJOR_VERSION) {
dev_err(&vsoc_dev.dev->dev,
"driver supports only major_version %d\n",
CURRENT_VSOC_LAYOUT_MAJOR_VERSION);
vsoc_remove_device(pdev);
return -EBUSY;
}
result = alloc_chrdev_region(&devt, 0, vsoc_dev.layout->region_count,
VSOC_DEV_NAME);
if (result) {
dev_err(&vsoc_dev.dev->dev, "alloc_chrdev_region failed\n");
vsoc_remove_device(pdev);
return -EBUSY;
}
vsoc_dev.major = MAJOR(devt);
cdev_init(&vsoc_dev.cdev, &vsoc_ops);
vsoc_dev.cdev.owner = THIS_MODULE;
result = cdev_add(&vsoc_dev.cdev, devt, vsoc_dev.layout->region_count);
if (result) {
dev_err(&vsoc_dev.dev->dev, "cdev_add error\n");
vsoc_remove_device(pdev);
return -EBUSY;
}
vsoc_dev.cdev_added = true;
vsoc_dev.class = class_create(THIS_MODULE, VSOC_DEV_NAME);
if (IS_ERR(vsoc_dev.class)) {
dev_err(&vsoc_dev.dev->dev, "class_create failed\n");
vsoc_remove_device(pdev);
return PTR_ERR(vsoc_dev.class);
}
vsoc_dev.class_added = true;
vsoc_dev.regions = (struct vsoc_device_region __force *)
((void *)vsoc_dev.layout +
vsoc_dev.layout->vsoc_region_desc_offset);
vsoc_dev.msix_entries =
kcalloc(vsoc_dev.layout->region_count,
sizeof(vsoc_dev.msix_entries[0]), GFP_KERNEL);
if (!vsoc_dev.msix_entries) {
dev_err(&vsoc_dev.dev->dev,
"unable to allocate msix_entries\n");
vsoc_remove_device(pdev);
return -ENOSPC;
}
vsoc_dev.regions_data =
kcalloc(vsoc_dev.layout->region_count,
sizeof(vsoc_dev.regions_data[0]), GFP_KERNEL);
if (!vsoc_dev.regions_data) {
dev_err(&vsoc_dev.dev->dev,
"unable to allocate regions' data\n");
vsoc_remove_device(pdev);
return -ENOSPC;
}
for (i = 0; i < vsoc_dev.layout->region_count; ++i)
vsoc_dev.msix_entries[i].entry = i;
result = pci_enable_msix_exact(vsoc_dev.dev, vsoc_dev.msix_entries,
vsoc_dev.layout->region_count);
if (result) {
dev_info(&pdev->dev, "pci_enable_msix failed: %d\n", result);
vsoc_remove_device(pdev);
return -ENOSPC;
}
/* Check that all regions are well formed */
for (i = 0; i < vsoc_dev.layout->region_count; ++i) {
const struct vsoc_device_region *region = vsoc_dev.regions + i;
if (!PAGE_ALIGNED(region->region_begin_offset) ||
!PAGE_ALIGNED(region->region_end_offset)) {
dev_err(&vsoc_dev.dev->dev,
"region %d not aligned (%x:%x)", i,
region->region_begin_offset,
region->region_end_offset);
vsoc_remove_device(pdev);
return -EFAULT;
}
if (region->region_begin_offset >= region->region_end_offset ||
region->region_end_offset > vsoc_dev.shm_size) {
dev_err(&vsoc_dev.dev->dev,
"region %d offsets are wrong: %x %x %zx",
i, region->region_begin_offset,
region->region_end_offset, vsoc_dev.shm_size);
vsoc_remove_device(pdev);
return -EFAULT;
}
if (region->managed_by >= vsoc_dev.layout->region_count) {
dev_err(&vsoc_dev.dev->dev,
"region %d has invalid owner: %u",
i, region->managed_by);
vsoc_remove_device(pdev);
return -EFAULT;
}
}
vsoc_dev.msix_enabled = true;
for (i = 0; i < vsoc_dev.layout->region_count; ++i) {
const struct vsoc_device_region *region = vsoc_dev.regions + i;
size_t name_sz = sizeof(vsoc_dev.regions_data[i].name) - 1;
const struct vsoc_signal_table_layout *h_to_g_signal_table =
&region->host_to_guest_signal_table;
const struct vsoc_signal_table_layout *g_to_h_signal_table =
&region->guest_to_host_signal_table;
vsoc_dev.regions_data[i].name[name_sz] = '\0';
memcpy(vsoc_dev.regions_data[i].name, region->device_name,
name_sz);
dev_info(&pdev->dev, "region %d name=%s\n",
i, vsoc_dev.regions_data[i].name);
init_waitqueue_head
(&vsoc_dev.regions_data[i].interrupt_wait_queue);
init_waitqueue_head(&vsoc_dev.regions_data[i].futex_wait_queue);
vsoc_dev.regions_data[i].incoming_signalled =
shm_off_to_virtual_addr(region->region_begin_offset) +
h_to_g_signal_table->interrupt_signalled_offset;
vsoc_dev.regions_data[i].outgoing_signalled =
shm_off_to_virtual_addr(region->region_begin_offset) +
g_to_h_signal_table->interrupt_signalled_offset;
result = request_irq(vsoc_dev.msix_entries[i].vector,
vsoc_interrupt, 0,
vsoc_dev.regions_data[i].name,
vsoc_dev.regions_data + i);
if (result) {
dev_info(&pdev->dev,
"request_irq failed irq=%d vector=%d\n",
i, vsoc_dev.msix_entries[i].vector);
vsoc_remove_device(pdev);
return -ENOSPC;
}
vsoc_dev.regions_data[i].irq_requested = true;
if (!device_create(vsoc_dev.class, NULL,
MKDEV(vsoc_dev.major, i),
NULL, vsoc_dev.regions_data[i].name)) {
dev_err(&vsoc_dev.dev->dev, "device_create failed\n");
vsoc_remove_device(pdev);
return -EBUSY;
}
vsoc_dev.regions_data[i].device_created = true;
}
return 0;
}
/*
* This should undo all of the allocations in the probe function in reverse
* order.
*
* Notes:
*
* The device may have been partially initialized, so double check
* that the allocations happened.
*
* This function may be called multiple times, so mark resources as freed
* as they are deallocated.
*/
static void vsoc_remove_device(struct pci_dev *pdev)
{
int i;
/*
* pdev is the first thing to be set on probe and the last thing
* to be cleared here. If it's NULL then there is no cleanup.
*/
if (!pdev || !vsoc_dev.dev)
return;
dev_info(&pdev->dev, "remove_device\n");
if (vsoc_dev.regions_data) {
for (i = 0; i < vsoc_dev.layout->region_count; ++i) {
if (vsoc_dev.regions_data[i].device_created) {
device_destroy(vsoc_dev.class,
MKDEV(vsoc_dev.major, i));
vsoc_dev.regions_data[i].device_created = false;
}
if (vsoc_dev.regions_data[i].irq_requested)
free_irq(vsoc_dev.msix_entries[i].vector, NULL);
vsoc_dev.regions_data[i].irq_requested = false;
}
kfree(vsoc_dev.regions_data);
vsoc_dev.regions_data = NULL;
}
if (vsoc_dev.msix_enabled) {
pci_disable_msix(pdev);
vsoc_dev.msix_enabled = false;
}
kfree(vsoc_dev.msix_entries);
vsoc_dev.msix_entries = NULL;
vsoc_dev.regions = NULL;
if (vsoc_dev.class_added) {
class_destroy(vsoc_dev.class);
vsoc_dev.class_added = false;
}
if (vsoc_dev.cdev_added) {
cdev_del(&vsoc_dev.cdev);
vsoc_dev.cdev_added = false;
}
if (vsoc_dev.major && vsoc_dev.layout) {
unregister_chrdev_region(MKDEV(vsoc_dev.major, 0),
vsoc_dev.layout->region_count);
vsoc_dev.major = 0;
}
vsoc_dev.layout = NULL;
if (vsoc_dev.kernel_mapped_shm) {
pci_iounmap(pdev, vsoc_dev.kernel_mapped_shm);
vsoc_dev.kernel_mapped_shm = NULL;
}
if (vsoc_dev.regs) {
pci_iounmap(pdev, vsoc_dev.regs);
vsoc_dev.regs = NULL;
}
if (vsoc_dev.requested_regions) {
pci_release_regions(pdev);
vsoc_dev.requested_regions = false;
}
if (vsoc_dev.enabled_device) {
pci_disable_device(pdev);
vsoc_dev.enabled_device = false;
}
/* Do this last: it indicates that the device is not initialized. */
vsoc_dev.dev = NULL;
}
static void __exit vsoc_cleanup_module(void)
{
vsoc_remove_device(vsoc_dev.dev);
pci_unregister_driver(&vsoc_pci_driver);
}
static int __init vsoc_init_module(void)
{
int err = -ENOMEM;
INIT_LIST_HEAD(&vsoc_dev.permissions);
mutex_init(&vsoc_dev.mtx);
err = pci_register_driver(&vsoc_pci_driver);
if (err < 0)
return err;
return 0;
}
static int vsoc_open(struct inode *inode, struct file *filp)
{
/* Can't use vsoc_validate_filep because filp is still incomplete */
int ret = vsoc_validate_inode(inode);
if (ret)
return ret;
filp->private_data =
kzalloc(sizeof(struct vsoc_private_data), GFP_KERNEL);
if (!filp->private_data)
return -ENOMEM;
return 0;
}
static int vsoc_release(struct inode *inode, struct file *filp)
{
struct vsoc_private_data *private_data = NULL;
struct fd_scoped_permission_node *node = NULL;
struct vsoc_device_region *owner_region_p = NULL;
int retval = vsoc_validate_filep(filp);
if (retval)
return retval;
private_data = (struct vsoc_private_data *)filp->private_data;
if (!private_data)
return 0;
node = private_data->fd_scoped_permission_node;
if (node) {
owner_region_p = vsoc_region_from_inode(inode);
if (owner_region_p->managed_by != VSOC_REGION_WHOLE) {
owner_region_p =
&vsoc_dev.regions[owner_region_p->managed_by];
}
do_destroy_fd_scoped_permission_node(owner_region_p, node);
private_data->fd_scoped_permission_node = NULL;
}
kfree(private_data);
filp->private_data = NULL;
return 0;
}
/*
* Returns the device relative offset and length of the area specified by the
* fd scoped permission. If there is no fd scoped permission set, a default
* permission covering the entire region is assumed, unless the region is owned
* by another one, in which case the default is a permission with zero size.
*/
static ssize_t vsoc_get_area(struct file *filp, __u32 *area_offset)
{
__u32 off = 0;
ssize_t length = 0;
struct vsoc_device_region *region_p;
struct fd_scoped_permission *perm;
region_p = vsoc_region_from_filep(filp);
off = region_p->region_begin_offset;
perm = &((struct vsoc_private_data *)filp->private_data)->
fd_scoped_permission_node->permission;
if (perm) {
off += perm->begin_offset;
length = perm->end_offset - perm->begin_offset;
} else if (region_p->managed_by == VSOC_REGION_WHOLE) {
/* No permission set and the regions is not owned by another,
* default to full region access.
*/
length = vsoc_device_region_size(region_p);
} else {
/* return zero length, access is denied. */
length = 0;
}
if (area_offset)
*area_offset = off;
return length;
}
static int vsoc_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long len = vma->vm_end - vma->vm_start;
__u32 area_off;
phys_addr_t mem_off;
ssize_t area_len;
int retval = vsoc_validate_filep(filp);
if (retval)
return retval;
area_len = vsoc_get_area(filp, &area_off);
/* Add the requested offset */
area_off += (vma->vm_pgoff << PAGE_SHIFT);
area_len -= (vma->vm_pgoff << PAGE_SHIFT);
if (area_len < len)
return -EINVAL;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
mem_off = shm_off_to_phys_addr(area_off);
if (io_remap_pfn_range(vma, vma->vm_start, mem_off >> PAGE_SHIFT,
len, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
module_init(vsoc_init_module);
module_exit(vsoc_cleanup_module);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Hartman <ghartman@google.com>");
MODULE_DESCRIPTION("VSoC interpretation of QEmu's ivshmem device");
MODULE_VERSION("1.0");
......@@ -92,8 +92,8 @@ void gb_audio_manager_remove_all(void)
list_for_each_entry_safe(module, next, &modules_list, list) {
list_del(&module->list);
kobject_put(&module->kobj);
ida_simple_remove(&module_id, module->id);
kobject_put(&module->kobj);
}
is_empty = list_empty(&modules_list);
......
......@@ -2009,21 +2009,16 @@ static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p)
struct ieee_param *param;
uint ret = 0;
if (p->length < sizeof(struct ieee_param) || !p->pointer) {
ret = -EINVAL;
goto out;
}
if (!p->pointer || p->length != sizeof(struct ieee_param))
return -EINVAL;
param = (struct ieee_param *)rtw_malloc(p->length);
if (!param) {
ret = -ENOMEM;
goto out;
}
if (!param)
return -ENOMEM;
if (copy_from_user(param, p->pointer, p->length)) {
kfree(param);
ret = -EFAULT;
goto out;
return -EFAULT;
}
switch (param->cmd) {
......@@ -2054,9 +2049,6 @@ static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p)
ret = -EFAULT;
kfree(param);
out:
return ret;
}
......@@ -2791,26 +2783,19 @@ static int rtw_hostapd_ioctl(struct net_device *dev, struct iw_point *p)
* so, we just check hw_init_completed
*/
if (!padapter->hw_init_completed) {
ret = -EPERM;
goto out;
}
if (!padapter->hw_init_completed)
return -EPERM;
if (!p->pointer) {
ret = -EINVAL;
goto out;
}
if (!p->pointer || p->length != sizeof(struct ieee_param))
return -EINVAL;
param = (struct ieee_param *)rtw_malloc(p->length);
if (!param) {
ret = -ENOMEM;
goto out;
}
if (!param)
return -ENOMEM;
if (copy_from_user(param, p->pointer, p->length)) {
kfree(param);
ret = -EFAULT;
goto out;
return -EFAULT;
}
switch (param->cmd) {
......@@ -2865,7 +2850,6 @@ static int rtw_hostapd_ioctl(struct net_device *dev, struct iw_point *p)
if (ret == 0 && copy_to_user(p->pointer, param, p->length))
ret = -EFAULT;
kfree(param);
out:
return ret;
}
#endif
......
......@@ -476,14 +476,13 @@ int rtl8723bs_xmit_thread(void *context)
s32 ret;
struct adapter *padapter;
struct xmit_priv *pxmitpriv;
u8 thread_name[20] = "RTWHALXT";
u8 thread_name[20];
ret = _SUCCESS;
padapter = context;
pxmitpriv = &padapter->xmitpriv;
rtw_sprintf(thread_name, 20, "%s-"ADPT_FMT, thread_name, ADPT_ARG(padapter));
rtw_sprintf(thread_name, 20, "RTWHALXT-" ADPT_FMT, ADPT_ARG(padapter));
thread_enter(thread_name);
DBG_871X("start "FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(padapter));
......
......@@ -3373,21 +3373,16 @@ static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p)
/* down(&ieee->wx_sem); */
if (p->length < sizeof(struct ieee_param) || !p->pointer) {
ret = -EINVAL;
goto out;
}
if (!p->pointer || p->length != sizeof(struct ieee_param))
return -EINVAL;
param = rtw_malloc(p->length);
if (param == NULL) {
ret = -ENOMEM;
goto out;
}
if (param == NULL)
return -ENOMEM;
if (copy_from_user(param, p->pointer, p->length)) {
kfree(param);
ret = -EFAULT;
goto out;
return -EFAULT;
}
switch (param->cmd) {
......@@ -3421,12 +3416,8 @@ static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p)
kfree(param);
out:
/* up(&ieee->wx_sem); */
return ret;
}
static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, u32 param_len)
......@@ -4200,28 +4191,19 @@ static int rtw_hostapd_ioctl(struct net_device *dev, struct iw_point *p)
* so, we just check hw_init_completed
*/
if (!padapter->hw_init_completed) {
ret = -EPERM;
goto out;
}
if (!padapter->hw_init_completed)
return -EPERM;
/* if (p->length < sizeof(struct ieee_param) || !p->pointer) { */
if (!p->pointer) {
ret = -EINVAL;
goto out;
}
if (!p->pointer || p->length != sizeof(*param))
return -EINVAL;
param = rtw_malloc(p->length);
if (param == NULL) {
ret = -ENOMEM;
goto out;
}
if (param == NULL)
return -ENOMEM;
if (copy_from_user(param, p->pointer, p->length)) {
kfree(param);
ret = -EFAULT;
goto out;
return -EFAULT;
}
/* DBG_871X("%s, cmd =%d\n", __func__, param->cmd); */
......@@ -4321,13 +4303,8 @@ static int rtw_hostapd_ioctl(struct net_device *dev, struct iw_point *p)
if (ret == 0 && copy_to_user(p->pointer, param, p->length))
ret = -EFAULT;
kfree(param);
out:
return ret;
}
static int rtw_wx_set_priv(struct net_device *dev,
......
......@@ -98,7 +98,7 @@ int vnt_rx_data(struct vnt_private *priv, struct vnt_rcb *ptr_rcb,
vnt_rf_rssi_to_dbm(priv, tail->rssi, &rx_dbm);
priv->bb_pre_ed_rssi = (u8)rx_dbm + 1;
priv->bb_pre_ed_rssi = (u8)-rx_dbm + 1;
priv->current_rssi = priv->bb_pre_ed_rssi;
skb_pull(skb, sizeof(*head));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment