diff options
Diffstat (limited to 'goldfish_address_space.c')
-rw-r--r-- | goldfish_address_space.c | 932 |
1 files changed, 0 insertions, 932 deletions
diff --git a/goldfish_address_space.c b/goldfish_address_space.c deleted file mode 100644 index 6eaaebf..0000000 --- a/goldfish_address_space.c +++ /dev/null @@ -1,932 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "defconfig_test.h" - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/wait.h> -#include <linux/fs.h> -#include <linux/io.h> -#include <linux/uaccess.h> -#include <linux/miscdevice.h> - -#include <linux/device.h> -#include <linux/pci_regs.h> -#include <linux/pci_ids.h> -#include <linux/pci.h> - -#include "uapi/goldfish_address_space.h" - -MODULE_DESCRIPTION("A Goldfish driver that allocates address space ranges in " - "the guest to populate them later in the host. This allows " - "sharing host's memory with the guest."); -MODULE_AUTHOR("Roman Kiryanov <rkir@google.com>"); -MODULE_LICENSE("GPL v2"); - -#define AS_DEBUG 0 - -#if AS_DEBUG - #define AS_DPRINT(fmt, ...) \ - printk(KERN_ERR "%s:%d " fmt "\n", \ - __func__, __LINE__, ##__VA_ARGS__); -#else - #define AS_DPRINT(fmt, ...) -#endif - -enum as_register_id { - AS_REGISTER_COMMAND = 0, - AS_REGISTER_STATUS = 4, - AS_REGISTER_GUEST_PAGE_SIZE = 8, - AS_REGISTER_BLOCK_SIZE_LOW = 12, - AS_REGISTER_BLOCK_SIZE_HIGH = 16, - AS_REGISTER_BLOCK_OFFSET_LOW = 20, - AS_REGISTER_BLOCK_OFFSET_HIGH = 24, - AS_REGISTER_PING = 28, - AS_REGISTER_PING_INFO_ADDR_LOW = 32, - AS_REGISTER_PING_INFO_ADDR_HIGH = 36, - AS_REGISTER_HANDLE = 40, - AS_REGISTER_PHYS_START_LOW = 44, - AS_REGISTER_PHYS_START_HIGH = 48, -}; - -enum as_command_id { - AS_COMMAND_ALLOCATE_BLOCK = 1, - AS_COMMAND_DEALLOCATE_BLOCK = 2, - AS_COMMAND_GEN_HANDLE = 3, - AS_COMMAND_DESTROY_HANDLE = 4, - AS_COMMAND_TELL_PING_INFO_ADDR = 5, -}; - -#define AS_PCI_VENDOR_ID 0x607D -#define AS_PCI_DEVICE_ID 0xF153 -#define AS_ALLOCATED_BLOCKS_INITIAL_CAPACITY 32 -#define AS_INVALID_HANDLE (~(0)) - -enum as_pci_bar_id { - AS_PCI_CONTROL_BAR_ID = 0, - AS_PCI_AREA_BAR_ID = 1, -}; - -struct as_device_state { - struct miscdevice miscdevice; - struct pci_dev *dev; - struct as_driver_state *driver_state; - - void __iomem *io_registers; - - void *address_area; /* to claim the address space */ - - /* physical address to allocate from */ - unsigned long address_area_phys_address; - - struct mutex registers_lock; /* protects registers */ -}; - -struct as_block { - u64 offset; - u64 size; -}; - -struct as_allocated_blocks { - struct as_block *blocks; /* a dynamic array of allocated blocks */ - int blocks_size; - int blocks_capacity; - struct mutex blocks_lock; /* protects operations with blocks */ -}; - -struct as_file_state { - struct as_device_state *device_state; - struct as_allocated_blocks allocated_blocks; - struct as_allocated_blocks shared_allocated_blocks; - struct goldfish_address_space_ping *ping_info; - struct mutex ping_info_lock; /* protects ping_info */ - u32 handle; /* handle generated by the host */ -}; - -static void __iomem *as_register_address(void __iomem *base, - int offset) -{ - WARN_ON(!base); - - return ((char __iomem *)base) + offset; -} - -static void as_write_register(void __iomem *registers, - int offset, - u32 value) -{ - writel(value, as_register_address(registers, offset)); -} - -static u32 as_read_register(void __iomem *registers, int offset) -{ - return readl(as_register_address(registers, offset)); -} - -static int as_run_command(struct as_device_state *state, enum as_command_id cmd) -{ - WARN_ON(!state); - - as_write_register(state->io_registers, AS_REGISTER_COMMAND, cmd); - return -as_read_register(state->io_registers, AS_REGISTER_STATUS); -} - -static void as_ping_impl(struct as_device_state *state, u32 handle) -{ - as_write_register(state->io_registers, AS_REGISTER_PING, handle); -} - -static long -as_ioctl_allocate_block_locked_impl(struct as_device_state *state, - u64 *size, u64 *offset) -{ - long res; - - as_write_register(state->io_registers, - AS_REGISTER_BLOCK_SIZE_LOW, - lower_32_bits(*size)); - as_write_register(state->io_registers, - AS_REGISTER_BLOCK_SIZE_HIGH, - upper_32_bits(*size)); - - res = as_run_command(state, AS_COMMAND_ALLOCATE_BLOCK); - if (!res) { - u64 low = as_read_register(state->io_registers, - AS_REGISTER_BLOCK_OFFSET_LOW); - u64 high = as_read_register(state->io_registers, - AS_REGISTER_BLOCK_OFFSET_HIGH); - *offset = low | (high << 32); - - low = as_read_register(state->io_registers, - AS_REGISTER_BLOCK_SIZE_LOW); - high = as_read_register(state->io_registers, - AS_REGISTER_BLOCK_SIZE_HIGH); - *size = low | (high << 32); - } - - return res; -} - -static long -as_ioctl_unallocate_block_locked_impl(struct as_device_state *state, u64 offset) -{ - as_write_register(state->io_registers, - AS_REGISTER_BLOCK_OFFSET_LOW, - lower_32_bits(offset)); - as_write_register(state->io_registers, - AS_REGISTER_BLOCK_OFFSET_HIGH, - upper_32_bits(offset)); - - return as_run_command(state, AS_COMMAND_DEALLOCATE_BLOCK); -} - -static int as_blocks_grow_capacity(int old_capacity) -{ - WARN_ON(old_capacity < 0); - - return old_capacity + old_capacity; -} - -static int -as_blocks_insert(struct as_allocated_blocks *allocated_blocks, - u64 offset, - u64 size) -{ - int blocks_size; - - if (mutex_lock_interruptible(&allocated_blocks->blocks_lock)) - return -ERESTARTSYS; - - blocks_size = allocated_blocks->blocks_size; - - WARN_ON(allocated_blocks->blocks_capacity < 1); - WARN_ON(allocated_blocks->blocks_capacity < - allocated_blocks->blocks_size); - WARN_ON(!allocated_blocks->blocks); - - if (allocated_blocks->blocks_capacity == blocks_size) { - int new_capacity = - as_blocks_grow_capacity( - allocated_blocks->blocks_capacity); - struct as_block *new_blocks = - kcalloc(new_capacity, - sizeof(allocated_blocks->blocks[0]), - GFP_KERNEL); - - if (!new_blocks) { - mutex_unlock(&allocated_blocks->blocks_lock); - return -ENOMEM; - } - - memcpy(new_blocks, allocated_blocks->blocks, - blocks_size * sizeof(allocated_blocks->blocks[0])); - - kfree(allocated_blocks->blocks); - allocated_blocks->blocks = new_blocks; - allocated_blocks->blocks_capacity = new_capacity; - } - - WARN_ON(blocks_size >= allocated_blocks->blocks_capacity); - - allocated_blocks->blocks[blocks_size] = - (struct as_block){ .offset = offset, .size = size }; - allocated_blocks->blocks_size = blocks_size + 1; - - mutex_unlock(&allocated_blocks->blocks_lock); - return 0; -} - -static int -as_blocks_remove(struct as_allocated_blocks *allocated_blocks, u64 offset) -{ - long res = -ENXIO; - struct as_block *blocks; - int blocks_size; - int i; - - if (mutex_lock_interruptible(&allocated_blocks->blocks_lock)) - return -ERESTARTSYS; - - blocks = allocated_blocks->blocks; - WARN_ON(!blocks); - - blocks_size = allocated_blocks->blocks_size; - WARN_ON(blocks_size < 0); - - for (i = 0; i < blocks_size; ++i) { - if (offset == blocks[i].offset) { - int last = blocks_size - 1; - - if (last > i) - blocks[i] = blocks[last]; - - --allocated_blocks->blocks_size; - res = 0; - break; - } - } - - if (res) - pr_err("%s: Block not found atoffset: 0x%llx\n", - __func__, offset); - - mutex_unlock(&allocated_blocks->blocks_lock); - return res; -} - -static int -as_blocks_check_if_mine(struct as_allocated_blocks *allocated_blocks, - u64 offset, - u64 size) -{ - const u64 end = offset + size; - int res = -EPERM; - struct as_block *block; - int blocks_size; - - if (mutex_lock_interruptible(&allocated_blocks->blocks_lock)) - return -ERESTARTSYS; - - block = allocated_blocks->blocks; - WARN_ON(!block); - - blocks_size = allocated_blocks->blocks_size; - WARN_ON(blocks_size < 0); - - for (; blocks_size > 0; --blocks_size, ++block) { - u64 block_offset = block->offset; - u64 block_end = block_offset + block->size; - - if (offset >= block_offset && end <= block_end) { - res = 0; - break; - } - } - - mutex_unlock(&allocated_blocks->blocks_lock); - return res; -} - -static int as_open(struct inode *inode, struct file *filp) -{ - struct as_file_state *file_state; - struct as_device_state *device_state; - struct goldfish_address_space_ping *ping_info; - u64 ping_info_phys; - u64 ping_info_phys_returned; - int err; - - AS_DPRINT("Get free page"); - ping_info = - (struct goldfish_address_space_ping *) - __get_free_page(GFP_KERNEL); - ping_info_phys = virt_to_phys(ping_info); - AS_DPRINT("Got free page: %p 0x%llx", ping_info, - (unsigned long long)ping_info_phys); - - if (!ping_info) { - printk(KERN_ERR "Could not alloc goldfish_address_space command buffer!\n"); - err = -ENOMEM; - goto err_ping_info_alloc_failed; - } - - file_state = kzalloc(sizeof(*file_state), GFP_KERNEL); - if (!file_state) { - err = -ENOMEM; - goto err_file_state_alloc_failed; - } - - file_state->device_state = - container_of(filp->private_data, - struct as_device_state, - miscdevice); - device_state = file_state->device_state; - - file_state->allocated_blocks.blocks = - kcalloc(AS_ALLOCATED_BLOCKS_INITIAL_CAPACITY, - sizeof(file_state->allocated_blocks.blocks[0]), - GFP_KERNEL); - - if (!file_state->allocated_blocks.blocks) { - err = -ENOMEM; - goto err_file_state_blocks_alloc_failed; - } - - file_state->shared_allocated_blocks.blocks = - kcalloc( - AS_ALLOCATED_BLOCKS_INITIAL_CAPACITY, - sizeof(file_state->shared_allocated_blocks.blocks[0]), - GFP_KERNEL); - - if (!file_state->shared_allocated_blocks.blocks) { - err = -ENOMEM; - goto err_file_state_blocks_alloc_failed; - } - - file_state->allocated_blocks.blocks_size = 0; - file_state->allocated_blocks.blocks_capacity = - AS_ALLOCATED_BLOCKS_INITIAL_CAPACITY; - mutex_init(&file_state->allocated_blocks.blocks_lock); - - file_state->shared_allocated_blocks.blocks_size = 0; - file_state->shared_allocated_blocks.blocks_capacity = - AS_ALLOCATED_BLOCKS_INITIAL_CAPACITY; - mutex_init(&file_state->shared_allocated_blocks.blocks_lock); - - mutex_init(&file_state->ping_info_lock); - file_state->ping_info = ping_info; - - AS_DPRINT("Acq regs lock"); - mutex_lock(&device_state->registers_lock); - AS_DPRINT("Got regs lock, gen handle"); - as_run_command(device_state, AS_COMMAND_GEN_HANDLE); - file_state->handle = as_read_register( - device_state->io_registers, - AS_REGISTER_HANDLE); - AS_DPRINT("Got regs lock, read handle: %u", file_state->handle); - mutex_unlock(&device_state->registers_lock); - - if (file_state->handle == AS_INVALID_HANDLE) { - err = -EINVAL; - goto err_gen_handle_failed; - } - - AS_DPRINT("Acq regs lock 2"); - mutex_lock(&device_state->registers_lock); - AS_DPRINT("Acqd regs lock 2, write handle and ping info addr"); - as_write_register( - device_state->io_registers, - AS_REGISTER_HANDLE, - file_state->handle); - as_write_register( - device_state->io_registers, - AS_REGISTER_PING_INFO_ADDR_LOW, - lower_32_bits(ping_info_phys)); - as_write_register( - device_state->io_registers, - AS_REGISTER_PING_INFO_ADDR_HIGH, - upper_32_bits(ping_info_phys)); - AS_DPRINT("Do tell ping info addr"); - as_run_command(device_state, AS_COMMAND_TELL_PING_INFO_ADDR); - ping_info_phys_returned = - ((u64)as_read_register(device_state->io_registers, - AS_REGISTER_PING_INFO_ADDR_LOW)) | - ((u64)as_read_register(device_state->io_registers, - AS_REGISTER_PING_INFO_ADDR_HIGH) << 32); - AS_DPRINT("Read back"); - - if (ping_info_phys != ping_info_phys_returned) { - printk(KERN_ERR "%s: Invalid result for ping info phys addr: expected 0x%llx, got 0x%llx\n", - __func__, - ping_info_phys, ping_info_phys_returned); - err = -EINVAL; - goto err_ping_info_failed; - } - - mutex_unlock(&device_state->registers_lock); - - filp->private_data = file_state; - return 0; - -err_ping_info_failed: -err_gen_handle_failed: - kfree(file_state->allocated_blocks.blocks); - kfree(file_state->shared_allocated_blocks.blocks); -err_file_state_blocks_alloc_failed: - kfree(file_state); -err_file_state_alloc_failed: - free_page((unsigned long)ping_info); -err_ping_info_alloc_failed: - return err; -} - -static int as_release(struct inode *inode, struct file *filp) -{ - struct as_file_state *file_state = filp->private_data; - struct as_allocated_blocks *allocated_blocks = - &file_state->allocated_blocks; - struct as_allocated_blocks *shared_allocated_blocks = - &file_state->shared_allocated_blocks; - struct goldfish_address_space_ping *ping_info = file_state->ping_info; - struct as_device_state *state = file_state->device_state; - int blocks_size, shared_blocks_size; - int i; - - WARN_ON(!state); - WARN_ON(!allocated_blocks); - WARN_ON(!allocated_blocks->blocks); - WARN_ON(allocated_blocks->blocks_size < 0); - WARN_ON(!shared_allocated_blocks); - WARN_ON(!shared_allocated_blocks->blocks); - WARN_ON(shared_allocated_blocks->blocks_size < 0); - WARN_ON(!ping_info); - - blocks_size = allocated_blocks->blocks_size; - shared_blocks_size = shared_allocated_blocks->blocks_size; - - mutex_lock(&state->registers_lock); - - as_write_register(state->io_registers, AS_REGISTER_HANDLE, - file_state->handle); - as_run_command(state, AS_COMMAND_DESTROY_HANDLE); - - for (i = 0; i < blocks_size; ++i) { - WARN_ON(as_ioctl_unallocate_block_locked_impl( - state, allocated_blocks->blocks[i].offset)); - } - - // Do not unalloc shared blocks as they are host-owned - - mutex_unlock(&state->registers_lock); - - kfree(allocated_blocks->blocks); - kfree(shared_allocated_blocks->blocks); - free_page((unsigned long)ping_info); - kfree(file_state); - return 0; -} - -static int as_mmap_impl(struct as_device_state *state, - size_t size, - struct vm_area_struct *vma) -{ - unsigned long pfn = (state->address_area_phys_address >> PAGE_SHIFT) + - vma->vm_pgoff; - - return remap_pfn_range(vma, - vma->vm_start, - pfn, - size, - vma->vm_page_prot); -} - -static int as_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct as_file_state *file_state = filp->private_data; - struct as_allocated_blocks *allocated_blocks = - &file_state->allocated_blocks; - struct as_allocated_blocks *shared_allocated_blocks = - &file_state->shared_allocated_blocks; - size_t size = PAGE_ALIGN(vma->vm_end - vma->vm_start); - int res_check_nonshared, res_check_shared; - - WARN_ON(!allocated_blocks); - - res_check_nonshared = - as_blocks_check_if_mine(allocated_blocks, - vma->vm_pgoff << PAGE_SHIFT, - size); - - res_check_shared = - as_blocks_check_if_mine(shared_allocated_blocks, - vma->vm_pgoff << PAGE_SHIFT, - size); - - if (res_check_nonshared && res_check_shared) - return res_check_nonshared; - else - return as_mmap_impl(file_state->device_state, size, vma); -} - -static long as_ioctl_allocate_block_impl( - struct as_device_state *state, - struct goldfish_address_space_allocate_block *request) -{ - long res; - - if (mutex_lock_interruptible(&state->registers_lock)) - return -ERESTARTSYS; - - res = as_ioctl_allocate_block_locked_impl(state, - &request->size, - &request->offset); - if (!res) { - request->phys_addr = - state->address_area_phys_address + request->offset; - } - - mutex_unlock(&state->registers_lock); - return res; -} - -static void -as_ioctl_unallocate_block_impl(struct as_device_state *state, u64 offset) -{ - mutex_lock(&state->registers_lock); - WARN_ON(as_ioctl_unallocate_block_locked_impl(state, offset)); - mutex_unlock(&state->registers_lock); -} - -static long -as_ioctl_allocate_block(struct as_allocated_blocks *allocated_blocks, - struct as_device_state *state, - void __user *ptr) -{ - long res; - struct goldfish_address_space_allocate_block request; - - if (copy_from_user(&request, ptr, sizeof(request))) - return -EFAULT; - - res = as_ioctl_allocate_block_impl(state, &request); - if (!res) { - res = as_blocks_insert(allocated_blocks, - request.offset, - request.size); - - if (res) { - as_ioctl_unallocate_block_impl(state, request.offset); - } else if (copy_to_user(ptr, &request, sizeof(request))) { - as_ioctl_unallocate_block_impl(state, request.offset); - res = -EFAULT; - } - } - - return res; -} - -static long -as_ioctl_unallocate_block(struct as_allocated_blocks *allocated_blocks, - struct as_device_state *state, - void __user *ptr) -{ - long res; - u64 offset; - - if (copy_from_user(&offset, ptr, sizeof(offset))) - return -EFAULT; - - res = as_blocks_remove(allocated_blocks, offset); - if (!res) - as_ioctl_unallocate_block_impl(state, offset); - - return res; -} - -static long -as_ioctl_claim_block(struct as_allocated_blocks *allocated_blocks, - struct as_device_state *state, - void __user *ptr) -{ - long res; - struct goldfish_address_space_claim_shared request; - - if (copy_from_user(&request, ptr, sizeof(request))) - return -EFAULT; - - res = as_blocks_insert(allocated_blocks, - request.offset, - request.size); - - if (res) - return res; - else if (copy_to_user(ptr, &request, sizeof(request))) - return -EFAULT; - - return 0; -} - -static long -as_ioctl_unclaim_block(struct as_allocated_blocks *allocated_blocks, - struct as_device_state *state, - void __user *ptr) -{ - long res; - u64 offset; - - if (copy_from_user(&offset, ptr, sizeof(offset))) - return -EFAULT; - - res = as_blocks_remove(allocated_blocks, offset); - if (res) - pr_err("%s: as_blocks_remove failed (%ld)\n", __func__, res); - - return res; -} - -static long -as_ioctl_ping_impl(struct goldfish_address_space_ping *ping_info, - struct as_device_state *state, - u32 handle, - void __user *ptr) -{ - struct goldfish_address_space_ping user_copy; - - if (copy_from_user(&user_copy, ptr, sizeof(user_copy))) - return -EFAULT; - - *ping_info = user_copy; - - // Convert to phys addrs - ping_info->offset += state->address_area_phys_address; - - mutex_lock(&state->registers_lock); - as_ping_impl(state, handle); - mutex_unlock(&state->registers_lock); - - memcpy(&user_copy, ping_info, sizeof(user_copy)); - if (copy_to_user(ptr, &user_copy, sizeof(user_copy))) - return -EFAULT; - - return 0; -} - -static long as_ioctl_ping(struct as_file_state *file_state, void __user *ptr) -{ - long ret; - - mutex_lock(&file_state->ping_info_lock); - ret = as_ioctl_ping_impl(file_state->ping_info, - file_state->device_state, - file_state->handle, - ptr); - mutex_unlock(&file_state->ping_info_lock); - - return ret; -} - -static long as_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct as_file_state *file_state = filp->private_data; - long res = -ENOTTY; - - switch (cmd) { - case GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK: - res = as_ioctl_allocate_block(&file_state->allocated_blocks, - file_state->device_state, - (void __user *)arg); - break; - - case GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK: - res = as_ioctl_unallocate_block(&file_state->allocated_blocks, - file_state->device_state, - (void __user *)arg); - break; - - case GOLDFISH_ADDRESS_SPACE_IOCTL_PING: - res = as_ioctl_ping(file_state, (void __user *)arg); - break; - - case GOLDFISH_ADDRESS_SPACE_IOCTL_CLAIM_SHARED: - res = as_ioctl_claim_block( - &file_state->shared_allocated_blocks, - file_state->device_state, - (void __user *)arg); - break; - - case GOLDFISH_ADDRESS_SPACE_IOCTL_UNCLAIM_SHARED: - res = as_ioctl_unclaim_block( - &file_state->shared_allocated_blocks, - file_state->device_state, - (void __user *)arg); - break; - - default: - res = -ENOTTY; - } - - return res; -} - -static const struct file_operations userspace_file_operations = { - .owner = THIS_MODULE, - .open = as_open, - .release = as_release, - .mmap = as_mmap, - .unlocked_ioctl = as_ioctl, - .compat_ioctl = as_ioctl, -}; - -static void __iomem __must_check *ioremap_pci_bar(struct pci_dev *dev, - int bar_id) -{ - void __iomem *io; - unsigned long size = pci_resource_len(dev, bar_id); - - if (!size) - return IOMEM_ERR_PTR(-ENXIO); - - io = ioremap(pci_resource_start(dev, bar_id), size); - if (!io) - return IOMEM_ERR_PTR(-ENOMEM); - - return io; -} - -static void __must_check *memremap_pci_bar(struct pci_dev *dev, - int bar_id, - unsigned long flags) -{ - void *mem; - unsigned long size = pci_resource_len(dev, bar_id); - - if (!size) - return ERR_PTR(-ENXIO); - - mem = memremap(pci_resource_start(dev, bar_id), size, flags); - if (!mem) - return ERR_PTR(-ENOMEM); - - return mem; -} - - -static void fill_miscdevice(struct miscdevice *miscdev) -{ - memset(miscdev, 0, sizeof(*miscdev)); - - miscdev->minor = MISC_DYNAMIC_MINOR; - miscdev->name = GOLDFISH_ADDRESS_SPACE_DEVICE_NAME; - miscdev->fops = &userspace_file_operations; -} - -static int __must_check -create_as_device(struct pci_dev *dev, const struct pci_device_id *id) -{ - int res; - struct as_device_state *state; - - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) - return -ENOMEM; - - res = pci_request_region(dev, - AS_PCI_CONTROL_BAR_ID, - "Address space control"); - if (res) { - pr_err("(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR%d", - dev->bus->number, - dev->devfn, - AS_PCI_CONTROL_BAR_ID); - goto out_free_device_state; - } - - res = pci_request_region(dev, - AS_PCI_AREA_BAR_ID, - "Address space area"); - if (res) { - pr_err("(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR%d", - dev->bus->number, - dev->devfn, - AS_PCI_AREA_BAR_ID); - goto out_release_control_bar; - } - - fill_miscdevice(&state->miscdevice); - res = misc_register(&state->miscdevice); - if (res) - goto out_release_area_bar; - - state->io_registers = ioremap_pci_bar(dev, - AS_PCI_CONTROL_BAR_ID); - if (IS_ERR(state->io_registers)) { - res = PTR_ERR(state->io_registers); - goto out_misc_deregister; - } - - state->address_area = memremap_pci_bar(dev, - AS_PCI_AREA_BAR_ID, - MEMREMAP_WB); - if (IS_ERR(state->address_area)) { - res = PTR_ERR(state->address_area); - goto out_iounmap; - } - - state->address_area_phys_address = - pci_resource_start(dev, AS_PCI_AREA_BAR_ID); - - as_write_register(state->io_registers, - AS_REGISTER_GUEST_PAGE_SIZE, - PAGE_SIZE); - as_write_register(state->io_registers, - AS_REGISTER_PHYS_START_LOW, - lower_32_bits(state->address_area_phys_address)); - as_write_register(state->io_registers, - AS_REGISTER_PHYS_START_HIGH, - upper_32_bits(state->address_area_phys_address)); - - state->dev = dev; - mutex_init(&state->registers_lock); - - pci_set_drvdata(dev, state); - return 0; - -out_iounmap: - iounmap(state->io_registers); -out_misc_deregister: - misc_deregister(&state->miscdevice); -out_release_area_bar: - pci_release_region(dev, AS_PCI_AREA_BAR_ID); -out_release_control_bar: - pci_release_region(dev, AS_PCI_CONTROL_BAR_ID); -out_free_device_state: - kzfree(state); - - return res; -} - -static void as_pci_destroy_device(struct as_device_state *state) -{ - memunmap(state->address_area); - iounmap(state->io_registers); - misc_deregister(&state->miscdevice); - pci_release_region(state->dev, AS_PCI_AREA_BAR_ID); - pci_release_region(state->dev, AS_PCI_CONTROL_BAR_ID); - kfree(state); -} - -static int __must_check -as_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - int res; - u8 hardware_revision; - - res = pci_enable_device(dev); - if (res) - return res; - - res = pci_read_config_byte(dev, PCI_REVISION_ID, &hardware_revision); - if (res) - goto out_disable_pci; - - switch (hardware_revision) { - case 1: - res = create_as_device(dev, id); - break; - - default: - res = -ENODEV; - goto out_disable_pci; - } - - return 0; - -out_disable_pci: - pci_disable_device(dev); - - return res; -} - -static void as_pci_remove(struct pci_dev *dev) -{ - struct as_device_state *state = pci_get_drvdata(dev); - - as_pci_destroy_device(state); - pci_disable_device(dev); -} - -static const struct pci_device_id as_pci_tbl[] = { - { PCI_DEVICE(AS_PCI_VENDOR_ID, AS_PCI_DEVICE_ID), }, - { } -}; -MODULE_DEVICE_TABLE(pci, as_pci_tbl); - -static struct pci_driver goldfish_address_space_driver = { - .name = GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, - .id_table = as_pci_tbl, - .probe = as_pci_probe, - .remove = as_pci_remove, -}; - -module_pci_driver(goldfish_address_space_driver); |