162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Physical device callbacks for vfio_ccw 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2017 662306a36Sopenharmony_ci * Copyright Red Hat, Inc. 2019 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> 962306a36Sopenharmony_ci * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com> 1062306a36Sopenharmony_ci * Cornelia Huck <cohuck@redhat.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/vfio.h> 1462306a36Sopenharmony_ci#include <linux/nospec.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "vfio_ccw_private.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic const struct vfio_device_ops vfio_ccw_dev_ops; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic int vfio_ccw_mdev_reset(struct vfio_ccw_private *private) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci /* 2462306a36Sopenharmony_ci * If the FSM state is seen as Not Operational after closing 2562306a36Sopenharmony_ci * and re-opening the mdev, return an error. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE); 2862306a36Sopenharmony_ci vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_OPEN); 2962306a36Sopenharmony_ci if (private->state == VFIO_CCW_STATE_NOT_OPER) 3062306a36Sopenharmony_ci return -EINVAL; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return 0; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void vfio_ccw_dma_unmap(struct vfio_device *vdev, u64 iova, u64 length) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct vfio_ccw_private *private = 3862306a36Sopenharmony_ci container_of(vdev, struct vfio_ccw_private, vdev); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* Drivers MUST unpin pages in response to an invalidation. */ 4162306a36Sopenharmony_ci if (!cp_iova_pinned(&private->cp, iova, length)) 4262306a36Sopenharmony_ci return; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci vfio_ccw_mdev_reset(private); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int vfio_ccw_mdev_init_dev(struct vfio_device *vdev) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct vfio_ccw_private *private = 5062306a36Sopenharmony_ci container_of(vdev, struct vfio_ccw_private, vdev); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci mutex_init(&private->io_mutex); 5362306a36Sopenharmony_ci private->state = VFIO_CCW_STATE_STANDBY; 5462306a36Sopenharmony_ci INIT_LIST_HEAD(&private->crw); 5562306a36Sopenharmony_ci INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo); 5662306a36Sopenharmony_ci INIT_WORK(&private->crw_work, vfio_ccw_crw_todo); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci private->cp.guest_cp = kcalloc(CCWCHAIN_LEN_MAX, sizeof(struct ccw1), 5962306a36Sopenharmony_ci GFP_KERNEL); 6062306a36Sopenharmony_ci if (!private->cp.guest_cp) 6162306a36Sopenharmony_ci goto out_free_private; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci private->io_region = kmem_cache_zalloc(vfio_ccw_io_region, 6462306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 6562306a36Sopenharmony_ci if (!private->io_region) 6662306a36Sopenharmony_ci goto out_free_cp; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci private->cmd_region = kmem_cache_zalloc(vfio_ccw_cmd_region, 6962306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 7062306a36Sopenharmony_ci if (!private->cmd_region) 7162306a36Sopenharmony_ci goto out_free_io; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci private->schib_region = kmem_cache_zalloc(vfio_ccw_schib_region, 7462306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 7562306a36Sopenharmony_ci if (!private->schib_region) 7662306a36Sopenharmony_ci goto out_free_cmd; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci private->crw_region = kmem_cache_zalloc(vfio_ccw_crw_region, 7962306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 8062306a36Sopenharmony_ci if (!private->crw_region) 8162306a36Sopenharmony_ci goto out_free_schib; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciout_free_schib: 8662306a36Sopenharmony_ci kmem_cache_free(vfio_ccw_schib_region, private->schib_region); 8762306a36Sopenharmony_ciout_free_cmd: 8862306a36Sopenharmony_ci kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region); 8962306a36Sopenharmony_ciout_free_io: 9062306a36Sopenharmony_ci kmem_cache_free(vfio_ccw_io_region, private->io_region); 9162306a36Sopenharmony_ciout_free_cp: 9262306a36Sopenharmony_ci kfree(private->cp.guest_cp); 9362306a36Sopenharmony_ciout_free_private: 9462306a36Sopenharmony_ci mutex_destroy(&private->io_mutex); 9562306a36Sopenharmony_ci return -ENOMEM; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int vfio_ccw_mdev_probe(struct mdev_device *mdev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(mdev->dev.parent); 10162306a36Sopenharmony_ci struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev); 10262306a36Sopenharmony_ci struct vfio_ccw_private *private; 10362306a36Sopenharmony_ci int ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci private = vfio_alloc_device(vfio_ccw_private, vdev, &mdev->dev, 10662306a36Sopenharmony_ci &vfio_ccw_dev_ops); 10762306a36Sopenharmony_ci if (IS_ERR(private)) 10862306a36Sopenharmony_ci return PTR_ERR(private); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci dev_set_drvdata(&parent->dev, private); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: create\n", 11362306a36Sopenharmony_ci sch->schid.cssid, 11462306a36Sopenharmony_ci sch->schid.ssid, 11562306a36Sopenharmony_ci sch->schid.sch_no); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ret = vfio_register_emulated_iommu_dev(&private->vdev); 11862306a36Sopenharmony_ci if (ret) 11962306a36Sopenharmony_ci goto err_put_vdev; 12062306a36Sopenharmony_ci dev_set_drvdata(&mdev->dev, private); 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cierr_put_vdev: 12462306a36Sopenharmony_ci dev_set_drvdata(&parent->dev, NULL); 12562306a36Sopenharmony_ci vfio_put_device(&private->vdev); 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void vfio_ccw_mdev_release_dev(struct vfio_device *vdev) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct vfio_ccw_private *private = 13262306a36Sopenharmony_ci container_of(vdev, struct vfio_ccw_private, vdev); 13362306a36Sopenharmony_ci struct vfio_ccw_crw *crw, *temp; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci list_for_each_entry_safe(crw, temp, &private->crw, next) { 13662306a36Sopenharmony_ci list_del(&crw->next); 13762306a36Sopenharmony_ci kfree(crw); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci kmem_cache_free(vfio_ccw_crw_region, private->crw_region); 14162306a36Sopenharmony_ci kmem_cache_free(vfio_ccw_schib_region, private->schib_region); 14262306a36Sopenharmony_ci kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region); 14362306a36Sopenharmony_ci kmem_cache_free(vfio_ccw_io_region, private->io_region); 14462306a36Sopenharmony_ci kfree(private->cp.guest_cp); 14562306a36Sopenharmony_ci mutex_destroy(&private->io_mutex); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void vfio_ccw_mdev_remove(struct mdev_device *mdev) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(mdev->dev.parent); 15162306a36Sopenharmony_ci struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev); 15262306a36Sopenharmony_ci struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: remove\n", 15562306a36Sopenharmony_ci sch->schid.cssid, 15662306a36Sopenharmony_ci sch->schid.ssid, 15762306a36Sopenharmony_ci sch->schid.sch_no); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci vfio_unregister_group_dev(&private->vdev); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci dev_set_drvdata(&parent->dev, NULL); 16262306a36Sopenharmony_ci vfio_put_device(&private->vdev); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int vfio_ccw_mdev_open_device(struct vfio_device *vdev) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct vfio_ccw_private *private = 16862306a36Sopenharmony_ci container_of(vdev, struct vfio_ccw_private, vdev); 16962306a36Sopenharmony_ci int ret; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Device cannot simply be opened again from this state */ 17262306a36Sopenharmony_ci if (private->state == VFIO_CCW_STATE_NOT_OPER) 17362306a36Sopenharmony_ci return -EINVAL; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci ret = vfio_ccw_register_async_dev_regions(private); 17662306a36Sopenharmony_ci if (ret) 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci ret = vfio_ccw_register_schib_dev_regions(private); 18062306a36Sopenharmony_ci if (ret) 18162306a36Sopenharmony_ci goto out_unregister; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci ret = vfio_ccw_register_crw_dev_regions(private); 18462306a36Sopenharmony_ci if (ret) 18562306a36Sopenharmony_ci goto out_unregister; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_OPEN); 18862306a36Sopenharmony_ci if (private->state == VFIO_CCW_STATE_NOT_OPER) { 18962306a36Sopenharmony_ci ret = -EINVAL; 19062306a36Sopenharmony_ci goto out_unregister; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return ret; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciout_unregister: 19662306a36Sopenharmony_ci vfio_ccw_unregister_dev_regions(private); 19762306a36Sopenharmony_ci return ret; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void vfio_ccw_mdev_close_device(struct vfio_device *vdev) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct vfio_ccw_private *private = 20362306a36Sopenharmony_ci container_of(vdev, struct vfio_ccw_private, vdev); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE); 20662306a36Sopenharmony_ci vfio_ccw_unregister_dev_regions(private); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private, 21062306a36Sopenharmony_ci char __user *buf, size_t count, 21162306a36Sopenharmony_ci loff_t *ppos) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; 21462306a36Sopenharmony_ci struct ccw_io_region *region; 21562306a36Sopenharmony_ci int ret; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (pos + count > sizeof(*region)) 21862306a36Sopenharmony_ci return -EINVAL; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci mutex_lock(&private->io_mutex); 22162306a36Sopenharmony_ci region = private->io_region; 22262306a36Sopenharmony_ci if (copy_to_user(buf, (void *)region + pos, count)) 22362306a36Sopenharmony_ci ret = -EFAULT; 22462306a36Sopenharmony_ci else 22562306a36Sopenharmony_ci ret = count; 22662306a36Sopenharmony_ci mutex_unlock(&private->io_mutex); 22762306a36Sopenharmony_ci return ret; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic ssize_t vfio_ccw_mdev_read(struct vfio_device *vdev, 23162306a36Sopenharmony_ci char __user *buf, 23262306a36Sopenharmony_ci size_t count, 23362306a36Sopenharmony_ci loff_t *ppos) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct vfio_ccw_private *private = 23662306a36Sopenharmony_ci container_of(vdev, struct vfio_ccw_private, vdev); 23762306a36Sopenharmony_ci unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions) 24062306a36Sopenharmony_ci return -EINVAL; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci switch (index) { 24362306a36Sopenharmony_ci case VFIO_CCW_CONFIG_REGION_INDEX: 24462306a36Sopenharmony_ci return vfio_ccw_mdev_read_io_region(private, buf, count, ppos); 24562306a36Sopenharmony_ci default: 24662306a36Sopenharmony_ci index -= VFIO_CCW_NUM_REGIONS; 24762306a36Sopenharmony_ci return private->region[index].ops->read(private, buf, count, 24862306a36Sopenharmony_ci ppos); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic ssize_t vfio_ccw_mdev_write_io_region(struct vfio_ccw_private *private, 25562306a36Sopenharmony_ci const char __user *buf, 25662306a36Sopenharmony_ci size_t count, loff_t *ppos) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; 25962306a36Sopenharmony_ci struct ccw_io_region *region; 26062306a36Sopenharmony_ci int ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (pos + count > sizeof(*region)) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (!mutex_trylock(&private->io_mutex)) 26662306a36Sopenharmony_ci return -EAGAIN; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci region = private->io_region; 26962306a36Sopenharmony_ci if (copy_from_user((void *)region + pos, buf, count)) { 27062306a36Sopenharmony_ci ret = -EFAULT; 27162306a36Sopenharmony_ci goto out_unlock; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ); 27562306a36Sopenharmony_ci ret = (region->ret_code != 0) ? region->ret_code : count; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ciout_unlock: 27862306a36Sopenharmony_ci mutex_unlock(&private->io_mutex); 27962306a36Sopenharmony_ci return ret; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic ssize_t vfio_ccw_mdev_write(struct vfio_device *vdev, 28362306a36Sopenharmony_ci const char __user *buf, 28462306a36Sopenharmony_ci size_t count, 28562306a36Sopenharmony_ci loff_t *ppos) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct vfio_ccw_private *private = 28862306a36Sopenharmony_ci container_of(vdev, struct vfio_ccw_private, vdev); 28962306a36Sopenharmony_ci unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci switch (index) { 29562306a36Sopenharmony_ci case VFIO_CCW_CONFIG_REGION_INDEX: 29662306a36Sopenharmony_ci return vfio_ccw_mdev_write_io_region(private, buf, count, ppos); 29762306a36Sopenharmony_ci default: 29862306a36Sopenharmony_ci index -= VFIO_CCW_NUM_REGIONS; 29962306a36Sopenharmony_ci return private->region[index].ops->write(private, buf, count, 30062306a36Sopenharmony_ci ppos); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int vfio_ccw_mdev_get_device_info(struct vfio_ccw_private *private, 30762306a36Sopenharmony_ci struct vfio_device_info *info) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci info->flags = VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET; 31062306a36Sopenharmony_ci info->num_regions = VFIO_CCW_NUM_REGIONS + private->num_regions; 31162306a36Sopenharmony_ci info->num_irqs = VFIO_CCW_NUM_IRQS; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int vfio_ccw_mdev_get_region_info(struct vfio_ccw_private *private, 31762306a36Sopenharmony_ci struct vfio_region_info *info, 31862306a36Sopenharmony_ci unsigned long arg) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci int i; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci switch (info->index) { 32362306a36Sopenharmony_ci case VFIO_CCW_CONFIG_REGION_INDEX: 32462306a36Sopenharmony_ci info->offset = 0; 32562306a36Sopenharmony_ci info->size = sizeof(struct ccw_io_region); 32662306a36Sopenharmony_ci info->flags = VFIO_REGION_INFO_FLAG_READ 32762306a36Sopenharmony_ci | VFIO_REGION_INFO_FLAG_WRITE; 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci default: /* all other regions are handled via capability chain */ 33062306a36Sopenharmony_ci { 33162306a36Sopenharmony_ci struct vfio_info_cap caps = { .buf = NULL, .size = 0 }; 33262306a36Sopenharmony_ci struct vfio_region_info_cap_type cap_type = { 33362306a36Sopenharmony_ci .header.id = VFIO_REGION_INFO_CAP_TYPE, 33462306a36Sopenharmony_ci .header.version = 1 }; 33562306a36Sopenharmony_ci int ret; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (info->index >= 33862306a36Sopenharmony_ci VFIO_CCW_NUM_REGIONS + private->num_regions) 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci info->index = array_index_nospec(info->index, 34262306a36Sopenharmony_ci VFIO_CCW_NUM_REGIONS + 34362306a36Sopenharmony_ci private->num_regions); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci i = info->index - VFIO_CCW_NUM_REGIONS; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index); 34862306a36Sopenharmony_ci info->size = private->region[i].size; 34962306a36Sopenharmony_ci info->flags = private->region[i].flags; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci cap_type.type = private->region[i].type; 35262306a36Sopenharmony_ci cap_type.subtype = private->region[i].subtype; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci ret = vfio_info_add_capability(&caps, &cap_type.header, 35562306a36Sopenharmony_ci sizeof(cap_type)); 35662306a36Sopenharmony_ci if (ret) 35762306a36Sopenharmony_ci return ret; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci info->flags |= VFIO_REGION_INFO_FLAG_CAPS; 36062306a36Sopenharmony_ci if (info->argsz < sizeof(*info) + caps.size) { 36162306a36Sopenharmony_ci info->argsz = sizeof(*info) + caps.size; 36262306a36Sopenharmony_ci info->cap_offset = 0; 36362306a36Sopenharmony_ci } else { 36462306a36Sopenharmony_ci vfio_info_cap_shift(&caps, sizeof(*info)); 36562306a36Sopenharmony_ci if (copy_to_user((void __user *)arg + sizeof(*info), 36662306a36Sopenharmony_ci caps.buf, caps.size)) { 36762306a36Sopenharmony_ci kfree(caps.buf); 36862306a36Sopenharmony_ci return -EFAULT; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci info->cap_offset = sizeof(*info); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci kfree(caps.buf); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci switch (info->index) { 38362306a36Sopenharmony_ci case VFIO_CCW_IO_IRQ_INDEX: 38462306a36Sopenharmony_ci case VFIO_CCW_CRW_IRQ_INDEX: 38562306a36Sopenharmony_ci case VFIO_CCW_REQ_IRQ_INDEX: 38662306a36Sopenharmony_ci info->count = 1; 38762306a36Sopenharmony_ci info->flags = VFIO_IRQ_INFO_EVENTFD; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci default: 39062306a36Sopenharmony_ci return -EINVAL; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic int vfio_ccw_mdev_set_irqs(struct vfio_ccw_private *private, 39762306a36Sopenharmony_ci uint32_t flags, 39862306a36Sopenharmony_ci uint32_t index, 39962306a36Sopenharmony_ci void __user *data) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct eventfd_ctx **ctx; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (!(flags & VFIO_IRQ_SET_ACTION_TRIGGER)) 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci switch (index) { 40762306a36Sopenharmony_ci case VFIO_CCW_IO_IRQ_INDEX: 40862306a36Sopenharmony_ci ctx = &private->io_trigger; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case VFIO_CCW_CRW_IRQ_INDEX: 41162306a36Sopenharmony_ci ctx = &private->crw_trigger; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case VFIO_CCW_REQ_IRQ_INDEX: 41462306a36Sopenharmony_ci ctx = &private->req_trigger; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci default: 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci switch (flags & VFIO_IRQ_SET_DATA_TYPE_MASK) { 42162306a36Sopenharmony_ci case VFIO_IRQ_SET_DATA_NONE: 42262306a36Sopenharmony_ci { 42362306a36Sopenharmony_ci if (*ctx) 42462306a36Sopenharmony_ci eventfd_signal(*ctx, 1); 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci case VFIO_IRQ_SET_DATA_BOOL: 42862306a36Sopenharmony_ci { 42962306a36Sopenharmony_ci uint8_t trigger; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (get_user(trigger, (uint8_t __user *)data)) 43262306a36Sopenharmony_ci return -EFAULT; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (trigger && *ctx) 43562306a36Sopenharmony_ci eventfd_signal(*ctx, 1); 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci case VFIO_IRQ_SET_DATA_EVENTFD: 43962306a36Sopenharmony_ci { 44062306a36Sopenharmony_ci int32_t fd; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (get_user(fd, (int32_t __user *)data)) 44362306a36Sopenharmony_ci return -EFAULT; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (fd == -1) { 44662306a36Sopenharmony_ci if (*ctx) 44762306a36Sopenharmony_ci eventfd_ctx_put(*ctx); 44862306a36Sopenharmony_ci *ctx = NULL; 44962306a36Sopenharmony_ci } else if (fd >= 0) { 45062306a36Sopenharmony_ci struct eventfd_ctx *efdctx; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci efdctx = eventfd_ctx_fdget(fd); 45362306a36Sopenharmony_ci if (IS_ERR(efdctx)) 45462306a36Sopenharmony_ci return PTR_ERR(efdctx); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (*ctx) 45762306a36Sopenharmony_ci eventfd_ctx_put(*ctx); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci *ctx = efdctx; 46062306a36Sopenharmony_ci } else 46162306a36Sopenharmony_ci return -EINVAL; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci default: 46662306a36Sopenharmony_ci return -EINVAL; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ciint vfio_ccw_register_dev_region(struct vfio_ccw_private *private, 47162306a36Sopenharmony_ci unsigned int subtype, 47262306a36Sopenharmony_ci const struct vfio_ccw_regops *ops, 47362306a36Sopenharmony_ci size_t size, u32 flags, void *data) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct vfio_ccw_region *region; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci region = krealloc(private->region, 47862306a36Sopenharmony_ci (private->num_regions + 1) * sizeof(*region), 47962306a36Sopenharmony_ci GFP_KERNEL); 48062306a36Sopenharmony_ci if (!region) 48162306a36Sopenharmony_ci return -ENOMEM; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci private->region = region; 48462306a36Sopenharmony_ci private->region[private->num_regions].type = VFIO_REGION_TYPE_CCW; 48562306a36Sopenharmony_ci private->region[private->num_regions].subtype = subtype; 48662306a36Sopenharmony_ci private->region[private->num_regions].ops = ops; 48762306a36Sopenharmony_ci private->region[private->num_regions].size = size; 48862306a36Sopenharmony_ci private->region[private->num_regions].flags = flags; 48962306a36Sopenharmony_ci private->region[private->num_regions].data = data; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci private->num_regions++; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_civoid vfio_ccw_unregister_dev_regions(struct vfio_ccw_private *private) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci int i; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci for (i = 0; i < private->num_regions; i++) 50162306a36Sopenharmony_ci private->region[i].ops->release(private, &private->region[i]); 50262306a36Sopenharmony_ci private->num_regions = 0; 50362306a36Sopenharmony_ci kfree(private->region); 50462306a36Sopenharmony_ci private->region = NULL; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic ssize_t vfio_ccw_mdev_ioctl(struct vfio_device *vdev, 50862306a36Sopenharmony_ci unsigned int cmd, 50962306a36Sopenharmony_ci unsigned long arg) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct vfio_ccw_private *private = 51262306a36Sopenharmony_ci container_of(vdev, struct vfio_ccw_private, vdev); 51362306a36Sopenharmony_ci int ret = 0; 51462306a36Sopenharmony_ci unsigned long minsz; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci switch (cmd) { 51762306a36Sopenharmony_ci case VFIO_DEVICE_GET_INFO: 51862306a36Sopenharmony_ci { 51962306a36Sopenharmony_ci struct vfio_device_info info; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci minsz = offsetofend(struct vfio_device_info, num_irqs); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 52462306a36Sopenharmony_ci return -EFAULT; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (info.argsz < minsz) 52762306a36Sopenharmony_ci return -EINVAL; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ret = vfio_ccw_mdev_get_device_info(private, &info); 53062306a36Sopenharmony_ci if (ret) 53162306a36Sopenharmony_ci return ret; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci case VFIO_DEVICE_GET_REGION_INFO: 53662306a36Sopenharmony_ci { 53762306a36Sopenharmony_ci struct vfio_region_info info; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci minsz = offsetofend(struct vfio_region_info, offset); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 54262306a36Sopenharmony_ci return -EFAULT; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (info.argsz < minsz) 54562306a36Sopenharmony_ci return -EINVAL; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ret = vfio_ccw_mdev_get_region_info(private, &info, arg); 54862306a36Sopenharmony_ci if (ret) 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci case VFIO_DEVICE_GET_IRQ_INFO: 55462306a36Sopenharmony_ci { 55562306a36Sopenharmony_ci struct vfio_irq_info info; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci minsz = offsetofend(struct vfio_irq_info, count); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 56062306a36Sopenharmony_ci return -EFAULT; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (info.argsz < minsz || info.index >= VFIO_CCW_NUM_IRQS) 56362306a36Sopenharmony_ci return -EINVAL; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci ret = vfio_ccw_mdev_get_irq_info(&info); 56662306a36Sopenharmony_ci if (ret) 56762306a36Sopenharmony_ci return ret; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (info.count == -1) 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci case VFIO_DEVICE_SET_IRQS: 57562306a36Sopenharmony_ci { 57662306a36Sopenharmony_ci struct vfio_irq_set hdr; 57762306a36Sopenharmony_ci size_t data_size; 57862306a36Sopenharmony_ci void __user *data; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci minsz = offsetofend(struct vfio_irq_set, count); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (copy_from_user(&hdr, (void __user *)arg, minsz)) 58362306a36Sopenharmony_ci return -EFAULT; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci ret = vfio_set_irqs_validate_and_prepare(&hdr, 1, 58662306a36Sopenharmony_ci VFIO_CCW_NUM_IRQS, 58762306a36Sopenharmony_ci &data_size); 58862306a36Sopenharmony_ci if (ret) 58962306a36Sopenharmony_ci return ret; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci data = (void __user *)(arg + minsz); 59262306a36Sopenharmony_ci return vfio_ccw_mdev_set_irqs(private, hdr.flags, hdr.index, 59362306a36Sopenharmony_ci data); 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci case VFIO_DEVICE_RESET: 59662306a36Sopenharmony_ci return vfio_ccw_mdev_reset(private); 59762306a36Sopenharmony_ci default: 59862306a36Sopenharmony_ci return -ENOTTY; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/* Request removal of the device*/ 60362306a36Sopenharmony_cistatic void vfio_ccw_mdev_request(struct vfio_device *vdev, unsigned int count) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct vfio_ccw_private *private = 60662306a36Sopenharmony_ci container_of(vdev, struct vfio_ccw_private, vdev); 60762306a36Sopenharmony_ci struct device *dev = vdev->dev; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (private->req_trigger) { 61062306a36Sopenharmony_ci if (!(count % 10)) 61162306a36Sopenharmony_ci dev_notice_ratelimited(dev, 61262306a36Sopenharmony_ci "Relaying device request to user (#%u)\n", 61362306a36Sopenharmony_ci count); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci eventfd_signal(private->req_trigger, 1); 61662306a36Sopenharmony_ci } else if (count == 0) { 61762306a36Sopenharmony_ci dev_notice(dev, 61862306a36Sopenharmony_ci "No device request channel registered, blocked until released by user\n"); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic const struct vfio_device_ops vfio_ccw_dev_ops = { 62362306a36Sopenharmony_ci .init = vfio_ccw_mdev_init_dev, 62462306a36Sopenharmony_ci .release = vfio_ccw_mdev_release_dev, 62562306a36Sopenharmony_ci .open_device = vfio_ccw_mdev_open_device, 62662306a36Sopenharmony_ci .close_device = vfio_ccw_mdev_close_device, 62762306a36Sopenharmony_ci .read = vfio_ccw_mdev_read, 62862306a36Sopenharmony_ci .write = vfio_ccw_mdev_write, 62962306a36Sopenharmony_ci .ioctl = vfio_ccw_mdev_ioctl, 63062306a36Sopenharmony_ci .request = vfio_ccw_mdev_request, 63162306a36Sopenharmony_ci .dma_unmap = vfio_ccw_dma_unmap, 63262306a36Sopenharmony_ci .bind_iommufd = vfio_iommufd_emulated_bind, 63362306a36Sopenharmony_ci .unbind_iommufd = vfio_iommufd_emulated_unbind, 63462306a36Sopenharmony_ci .attach_ioas = vfio_iommufd_emulated_attach_ioas, 63562306a36Sopenharmony_ci .detach_ioas = vfio_iommufd_emulated_detach_ioas, 63662306a36Sopenharmony_ci}; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistruct mdev_driver vfio_ccw_mdev_driver = { 63962306a36Sopenharmony_ci .device_api = VFIO_DEVICE_API_CCW_STRING, 64062306a36Sopenharmony_ci .max_instances = 1, 64162306a36Sopenharmony_ci .driver = { 64262306a36Sopenharmony_ci .name = "vfio_ccw_mdev", 64362306a36Sopenharmony_ci .owner = THIS_MODULE, 64462306a36Sopenharmony_ci .mod_name = KBUILD_MODNAME, 64562306a36Sopenharmony_ci }, 64662306a36Sopenharmony_ci .probe = vfio_ccw_mdev_probe, 64762306a36Sopenharmony_ci .remove = vfio_ccw_mdev_remove, 64862306a36Sopenharmony_ci}; 649