// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #include #include #include #include #include #include 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 "); 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);