162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Adjunct processor matrix VFIO device driver callbacks. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2018 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author(s): Tony Krowiak <akrowiak@linux.ibm.com> 862306a36Sopenharmony_ci * Halil Pasic <pasic@linux.ibm.com> 962306a36Sopenharmony_ci * Pierre Morel <pmorel@linux.ibm.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/string.h> 1262306a36Sopenharmony_ci#include <linux/vfio.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/list.h> 1562306a36Sopenharmony_ci#include <linux/ctype.h> 1662306a36Sopenharmony_ci#include <linux/bitops.h> 1762306a36Sopenharmony_ci#include <linux/kvm_host.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/uuid.h> 2062306a36Sopenharmony_ci#include <asm/kvm.h> 2162306a36Sopenharmony_ci#include <asm/zcrypt.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "vfio_ap_private.h" 2462306a36Sopenharmony_ci#include "vfio_ap_debug.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define VFIO_AP_MDEV_TYPE_HWVIRT "passthrough" 2762306a36Sopenharmony_ci#define VFIO_AP_MDEV_NAME_HWVIRT "VFIO AP Passthrough Device" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define AP_QUEUE_ASSIGNED "assigned" 3062306a36Sopenharmony_ci#define AP_QUEUE_UNASSIGNED "unassigned" 3162306a36Sopenharmony_ci#define AP_QUEUE_IN_USE "in use" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define AP_RESET_INTERVAL 20 /* Reset sleep interval (20ms) */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev); 3662306a36Sopenharmony_cistatic int vfio_ap_mdev_reset_qlist(struct list_head *qlist); 3762306a36Sopenharmony_cistatic struct vfio_ap_queue *vfio_ap_find_queue(int apqn); 3862306a36Sopenharmony_cistatic const struct vfio_device_ops vfio_ap_matrix_dev_ops; 3962306a36Sopenharmony_cistatic void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/** 4262306a36Sopenharmony_ci * get_update_locks_for_kvm: Acquire the locks required to dynamically update a 4362306a36Sopenharmony_ci * KVM guest's APCB in the proper order. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * @kvm: a pointer to a struct kvm object containing the KVM guest's APCB. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * The proper locking order is: 4862306a36Sopenharmony_ci * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM 4962306a36Sopenharmony_ci * guest's APCB. 5062306a36Sopenharmony_ci * 2. kvm->lock: required to update a guest's APCB 5162306a36Sopenharmony_ci * 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Note: If @kvm is NULL, the KVM lock will not be taken. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistatic inline void get_update_locks_for_kvm(struct kvm *kvm) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci mutex_lock(&matrix_dev->guests_lock); 5862306a36Sopenharmony_ci if (kvm) 5962306a36Sopenharmony_ci mutex_lock(&kvm->lock); 6062306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * release_update_locks_for_kvm: Release the locks used to dynamically update a 6562306a36Sopenharmony_ci * KVM guest's APCB in the proper order. 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * @kvm: a pointer to a struct kvm object containing the KVM guest's APCB. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * The proper unlocking order is: 7062306a36Sopenharmony_ci * 1. matrix_dev->mdevs_lock 7162306a36Sopenharmony_ci * 2. kvm->lock 7262306a36Sopenharmony_ci * 3. matrix_dev->guests_lock 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Note: If @kvm is NULL, the KVM lock will not be released. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistatic inline void release_update_locks_for_kvm(struct kvm *kvm) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 7962306a36Sopenharmony_ci if (kvm) 8062306a36Sopenharmony_ci mutex_unlock(&kvm->lock); 8162306a36Sopenharmony_ci mutex_unlock(&matrix_dev->guests_lock); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/** 8562306a36Sopenharmony_ci * get_update_locks_for_mdev: Acquire the locks required to dynamically update a 8662306a36Sopenharmony_ci * KVM guest's APCB in the proper order. 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP 8962306a36Sopenharmony_ci * configuration data to use to update a KVM guest's APCB. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * The proper locking order is: 9262306a36Sopenharmony_ci * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM 9362306a36Sopenharmony_ci * guest's APCB. 9462306a36Sopenharmony_ci * 2. matrix_mdev->kvm->lock: required to update a guest's APCB 9562306a36Sopenharmony_ci * 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM 9862306a36Sopenharmony_ci * lock will not be taken. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cistatic inline void get_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci mutex_lock(&matrix_dev->guests_lock); 10362306a36Sopenharmony_ci if (matrix_mdev && matrix_mdev->kvm) 10462306a36Sopenharmony_ci mutex_lock(&matrix_mdev->kvm->lock); 10562306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/** 10962306a36Sopenharmony_ci * release_update_locks_for_mdev: Release the locks used to dynamically update a 11062306a36Sopenharmony_ci * KVM guest's APCB in the proper order. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP 11362306a36Sopenharmony_ci * configuration data to use to update a KVM guest's APCB. 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * The proper unlocking order is: 11662306a36Sopenharmony_ci * 1. matrix_dev->mdevs_lock 11762306a36Sopenharmony_ci * 2. matrix_mdev->kvm->lock 11862306a36Sopenharmony_ci * 3. matrix_dev->guests_lock 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM 12162306a36Sopenharmony_ci * lock will not be released. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_cistatic inline void release_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 12662306a36Sopenharmony_ci if (matrix_mdev && matrix_mdev->kvm) 12762306a36Sopenharmony_ci mutex_unlock(&matrix_mdev->kvm->lock); 12862306a36Sopenharmony_ci mutex_unlock(&matrix_dev->guests_lock); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/** 13262306a36Sopenharmony_ci * get_update_locks_by_apqn: Find the mdev to which an APQN is assigned and 13362306a36Sopenharmony_ci * acquire the locks required to update the APCB of 13462306a36Sopenharmony_ci * the KVM guest to which the mdev is attached. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * @apqn: the APQN of a queue device. 13762306a36Sopenharmony_ci * 13862306a36Sopenharmony_ci * The proper locking order is: 13962306a36Sopenharmony_ci * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM 14062306a36Sopenharmony_ci * guest's APCB. 14162306a36Sopenharmony_ci * 2. matrix_mdev->kvm->lock: required to update a guest's APCB 14262306a36Sopenharmony_ci * 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * Note: If @apqn is not assigned to a matrix_mdev, the matrix_mdev->kvm->lock 14562306a36Sopenharmony_ci * will not be taken. 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * Return: the ap_matrix_mdev object to which @apqn is assigned or NULL if @apqn 14862306a36Sopenharmony_ci * is not assigned to an ap_matrix_mdev. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistatic struct ap_matrix_mdev *get_update_locks_by_apqn(int apqn) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci mutex_lock(&matrix_dev->guests_lock); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { 15762306a36Sopenharmony_ci if (test_bit_inv(AP_QID_CARD(apqn), matrix_mdev->matrix.apm) && 15862306a36Sopenharmony_ci test_bit_inv(AP_QID_QUEUE(apqn), matrix_mdev->matrix.aqm)) { 15962306a36Sopenharmony_ci if (matrix_mdev->kvm) 16062306a36Sopenharmony_ci mutex_lock(&matrix_mdev->kvm->lock); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return matrix_mdev; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return NULL; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/** 17462306a36Sopenharmony_ci * get_update_locks_for_queue: get the locks required to update the APCB of the 17562306a36Sopenharmony_ci * KVM guest to which the matrix mdev linked to a 17662306a36Sopenharmony_ci * vfio_ap_queue object is attached. 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * @q: a pointer to a vfio_ap_queue object. 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * The proper locking order is: 18162306a36Sopenharmony_ci * 1. q->matrix_dev->guests_lock: required to use the KVM pointer to update a 18262306a36Sopenharmony_ci * KVM guest's APCB. 18362306a36Sopenharmony_ci * 2. q->matrix_mdev->kvm->lock: required to update a guest's APCB 18462306a36Sopenharmony_ci * 3. matrix_dev->mdevs_lock: required to access data stored in matrix_mdev 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Note: if @queue is not linked to an ap_matrix_mdev object, the KVM lock 18762306a36Sopenharmony_ci * will not be taken. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistatic inline void get_update_locks_for_queue(struct vfio_ap_queue *q) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci mutex_lock(&matrix_dev->guests_lock); 19262306a36Sopenharmony_ci if (q->matrix_mdev && q->matrix_mdev->kvm) 19362306a36Sopenharmony_ci mutex_lock(&q->matrix_mdev->kvm->lock); 19462306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/** 19862306a36Sopenharmony_ci * vfio_ap_mdev_get_queue - retrieve a queue with a specific APQN from a 19962306a36Sopenharmony_ci * hash table of queues assigned to a matrix mdev 20062306a36Sopenharmony_ci * @matrix_mdev: the matrix mdev 20162306a36Sopenharmony_ci * @apqn: The APQN of a queue device 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * Return: the pointer to the vfio_ap_queue struct representing the queue or 20462306a36Sopenharmony_ci * NULL if the queue is not assigned to @matrix_mdev 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic struct vfio_ap_queue *vfio_ap_mdev_get_queue( 20762306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev, 20862306a36Sopenharmony_ci int apqn) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct vfio_ap_queue *q; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci hash_for_each_possible(matrix_mdev->qtable.queues, q, mdev_qnode, 21362306a36Sopenharmony_ci apqn) { 21462306a36Sopenharmony_ci if (q && q->apqn == apqn) 21562306a36Sopenharmony_ci return q; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return NULL; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/** 22262306a36Sopenharmony_ci * vfio_ap_wait_for_irqclear - clears the IR bit or gives up after 5 tries 22362306a36Sopenharmony_ci * @apqn: The AP Queue number 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * Checks the IRQ bit for the status of this APQN using ap_tapq. 22662306a36Sopenharmony_ci * Returns if the ap_tapq function succeeded and the bit is clear. 22762306a36Sopenharmony_ci * Returns if ap_tapq function failed with invalid, deconfigured or 22862306a36Sopenharmony_ci * checkstopped AP. 22962306a36Sopenharmony_ci * Otherwise retries up to 5 times after waiting 20ms. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_cistatic void vfio_ap_wait_for_irqclear(int apqn) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct ap_queue_status status; 23462306a36Sopenharmony_ci int retry = 5; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci do { 23762306a36Sopenharmony_ci status = ap_tapq(apqn, NULL); 23862306a36Sopenharmony_ci switch (status.response_code) { 23962306a36Sopenharmony_ci case AP_RESPONSE_NORMAL: 24062306a36Sopenharmony_ci case AP_RESPONSE_RESET_IN_PROGRESS: 24162306a36Sopenharmony_ci if (!status.irq_enabled) 24262306a36Sopenharmony_ci return; 24362306a36Sopenharmony_ci fallthrough; 24462306a36Sopenharmony_ci case AP_RESPONSE_BUSY: 24562306a36Sopenharmony_ci msleep(20); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci case AP_RESPONSE_Q_NOT_AVAIL: 24862306a36Sopenharmony_ci case AP_RESPONSE_DECONFIGURED: 24962306a36Sopenharmony_ci case AP_RESPONSE_CHECKSTOPPED: 25062306a36Sopenharmony_ci default: 25162306a36Sopenharmony_ci WARN_ONCE(1, "%s: tapq rc %02x: %04x\n", __func__, 25262306a36Sopenharmony_ci status.response_code, apqn); 25362306a36Sopenharmony_ci return; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } while (--retry); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci WARN_ONCE(1, "%s: tapq rc %02x: %04x could not clear IR bit\n", 25862306a36Sopenharmony_ci __func__, status.response_code, apqn); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/** 26262306a36Sopenharmony_ci * vfio_ap_free_aqic_resources - free vfio_ap_queue resources 26362306a36Sopenharmony_ci * @q: The vfio_ap_queue 26462306a36Sopenharmony_ci * 26562306a36Sopenharmony_ci * Unregisters the ISC in the GIB when the saved ISC not invalid. 26662306a36Sopenharmony_ci * Unpins the guest's page holding the NIB when it exists. 26762306a36Sopenharmony_ci * Resets the saved_iova and saved_isc to invalid values. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_cistatic void vfio_ap_free_aqic_resources(struct vfio_ap_queue *q) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci if (!q) 27262306a36Sopenharmony_ci return; 27362306a36Sopenharmony_ci if (q->saved_isc != VFIO_AP_ISC_INVALID && 27462306a36Sopenharmony_ci !WARN_ON(!(q->matrix_mdev && q->matrix_mdev->kvm))) { 27562306a36Sopenharmony_ci kvm_s390_gisc_unregister(q->matrix_mdev->kvm, q->saved_isc); 27662306a36Sopenharmony_ci q->saved_isc = VFIO_AP_ISC_INVALID; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci if (q->saved_iova && !WARN_ON(!q->matrix_mdev)) { 27962306a36Sopenharmony_ci vfio_unpin_pages(&q->matrix_mdev->vdev, q->saved_iova, 1); 28062306a36Sopenharmony_ci q->saved_iova = 0; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/** 28562306a36Sopenharmony_ci * vfio_ap_irq_disable - disables and clears an ap_queue interrupt 28662306a36Sopenharmony_ci * @q: The vfio_ap_queue 28762306a36Sopenharmony_ci * 28862306a36Sopenharmony_ci * Uses ap_aqic to disable the interruption and in case of success, reset 28962306a36Sopenharmony_ci * in progress or IRQ disable command already proceeded: calls 29062306a36Sopenharmony_ci * vfio_ap_wait_for_irqclear() to check for the IRQ bit to be clear 29162306a36Sopenharmony_ci * and calls vfio_ap_free_aqic_resources() to free the resources associated 29262306a36Sopenharmony_ci * with the AP interrupt handling. 29362306a36Sopenharmony_ci * 29462306a36Sopenharmony_ci * In the case the AP is busy, or a reset is in progress, 29562306a36Sopenharmony_ci * retries after 20ms, up to 5 times. 29662306a36Sopenharmony_ci * 29762306a36Sopenharmony_ci * Returns if ap_aqic function failed with invalid, deconfigured or 29862306a36Sopenharmony_ci * checkstopped AP. 29962306a36Sopenharmony_ci * 30062306a36Sopenharmony_ci * Return: &struct ap_queue_status 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_cistatic struct ap_queue_status vfio_ap_irq_disable(struct vfio_ap_queue *q) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci union ap_qirq_ctrl aqic_gisa = { .value = 0 }; 30562306a36Sopenharmony_ci struct ap_queue_status status; 30662306a36Sopenharmony_ci int retries = 5; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci do { 30962306a36Sopenharmony_ci status = ap_aqic(q->apqn, aqic_gisa, 0); 31062306a36Sopenharmony_ci switch (status.response_code) { 31162306a36Sopenharmony_ci case AP_RESPONSE_OTHERWISE_CHANGED: 31262306a36Sopenharmony_ci case AP_RESPONSE_NORMAL: 31362306a36Sopenharmony_ci vfio_ap_wait_for_irqclear(q->apqn); 31462306a36Sopenharmony_ci goto end_free; 31562306a36Sopenharmony_ci case AP_RESPONSE_RESET_IN_PROGRESS: 31662306a36Sopenharmony_ci case AP_RESPONSE_BUSY: 31762306a36Sopenharmony_ci msleep(20); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case AP_RESPONSE_Q_NOT_AVAIL: 32062306a36Sopenharmony_ci case AP_RESPONSE_DECONFIGURED: 32162306a36Sopenharmony_ci case AP_RESPONSE_CHECKSTOPPED: 32262306a36Sopenharmony_ci case AP_RESPONSE_INVALID_ADDRESS: 32362306a36Sopenharmony_ci default: 32462306a36Sopenharmony_ci /* All cases in default means AP not operational */ 32562306a36Sopenharmony_ci WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__, 32662306a36Sopenharmony_ci status.response_code); 32762306a36Sopenharmony_ci goto end_free; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } while (retries--); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__, 33262306a36Sopenharmony_ci status.response_code); 33362306a36Sopenharmony_ciend_free: 33462306a36Sopenharmony_ci vfio_ap_free_aqic_resources(q); 33562306a36Sopenharmony_ci return status; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci/** 33962306a36Sopenharmony_ci * vfio_ap_validate_nib - validate a notification indicator byte (nib) address. 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * @vcpu: the object representing the vcpu executing the PQAP(AQIC) instruction. 34262306a36Sopenharmony_ci * @nib: the location for storing the nib address. 34362306a36Sopenharmony_ci * 34462306a36Sopenharmony_ci * When the PQAP(AQIC) instruction is executed, general register 2 contains the 34562306a36Sopenharmony_ci * address of the notification indicator byte (nib) used for IRQ notification. 34662306a36Sopenharmony_ci * This function parses and validates the nib from gr2. 34762306a36Sopenharmony_ci * 34862306a36Sopenharmony_ci * Return: returns zero if the nib address is a valid; otherwise, returns 34962306a36Sopenharmony_ci * -EINVAL. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_cistatic int vfio_ap_validate_nib(struct kvm_vcpu *vcpu, dma_addr_t *nib) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci *nib = vcpu->run->s.regs.gprs[2]; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!*nib) 35662306a36Sopenharmony_ci return -EINVAL; 35762306a36Sopenharmony_ci if (kvm_is_error_hva(gfn_to_hva(vcpu->kvm, *nib >> PAGE_SHIFT))) 35862306a36Sopenharmony_ci return -EINVAL; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int ensure_nib_shared(unsigned long addr, struct gmap *gmap) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci int ret; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * The nib has to be located in shared storage since guest and 36962306a36Sopenharmony_ci * host access it. vfio_pin_pages() will do a pin shared and 37062306a36Sopenharmony_ci * if that fails (possibly because it's not a shared page) it 37162306a36Sopenharmony_ci * calls export. We try to do a second pin shared here so that 37262306a36Sopenharmony_ci * the UV gives us an error code if we try to pin a non-shared 37362306a36Sopenharmony_ci * page. 37462306a36Sopenharmony_ci * 37562306a36Sopenharmony_ci * If the page is already pinned shared the UV will return a success. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci ret = uv_pin_shared(addr); 37862306a36Sopenharmony_ci if (ret) { 37962306a36Sopenharmony_ci /* vfio_pin_pages() likely exported the page so let's re-import */ 38062306a36Sopenharmony_ci gmap_convert_to_secure(gmap, addr); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci return ret; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/** 38662306a36Sopenharmony_ci * vfio_ap_irq_enable - Enable Interruption for a APQN 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * @q: the vfio_ap_queue holding AQIC parameters 38962306a36Sopenharmony_ci * @isc: the guest ISC to register with the GIB interface 39062306a36Sopenharmony_ci * @vcpu: the vcpu object containing the registers specifying the parameters 39162306a36Sopenharmony_ci * passed to the PQAP(AQIC) instruction. 39262306a36Sopenharmony_ci * 39362306a36Sopenharmony_ci * Pin the NIB saved in *q 39462306a36Sopenharmony_ci * Register the guest ISC to GIB interface and retrieve the 39562306a36Sopenharmony_ci * host ISC to issue the host side PQAP/AQIC 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * Response.status may be set to AP_RESPONSE_INVALID_ADDRESS in case the 39862306a36Sopenharmony_ci * vfio_pin_pages failed. 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * Otherwise return the ap_queue_status returned by the ap_aqic(), 40162306a36Sopenharmony_ci * all retry handling will be done by the guest. 40262306a36Sopenharmony_ci * 40362306a36Sopenharmony_ci * Return: &struct ap_queue_status 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_cistatic struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q, 40662306a36Sopenharmony_ci int isc, 40762306a36Sopenharmony_ci struct kvm_vcpu *vcpu) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci union ap_qirq_ctrl aqic_gisa = { .value = 0 }; 41062306a36Sopenharmony_ci struct ap_queue_status status = {}; 41162306a36Sopenharmony_ci struct kvm_s390_gisa *gisa; 41262306a36Sopenharmony_ci struct page *h_page; 41362306a36Sopenharmony_ci int nisc; 41462306a36Sopenharmony_ci struct kvm *kvm; 41562306a36Sopenharmony_ci phys_addr_t h_nib; 41662306a36Sopenharmony_ci dma_addr_t nib; 41762306a36Sopenharmony_ci int ret; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Verify that the notification indicator byte address is valid */ 42062306a36Sopenharmony_ci if (vfio_ap_validate_nib(vcpu, &nib)) { 42162306a36Sopenharmony_ci VFIO_AP_DBF_WARN("%s: invalid NIB address: nib=%pad, apqn=%#04x\n", 42262306a36Sopenharmony_ci __func__, &nib, q->apqn); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci status.response_code = AP_RESPONSE_INVALID_ADDRESS; 42562306a36Sopenharmony_ci return status; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ret = vfio_pin_pages(&q->matrix_mdev->vdev, nib, 1, 42962306a36Sopenharmony_ci IOMMU_READ | IOMMU_WRITE, &h_page); 43062306a36Sopenharmony_ci switch (ret) { 43162306a36Sopenharmony_ci case 1: 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci default: 43462306a36Sopenharmony_ci VFIO_AP_DBF_WARN("%s: vfio_pin_pages failed: rc=%d," 43562306a36Sopenharmony_ci "nib=%pad, apqn=%#04x\n", 43662306a36Sopenharmony_ci __func__, ret, &nib, q->apqn); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci status.response_code = AP_RESPONSE_INVALID_ADDRESS; 43962306a36Sopenharmony_ci return status; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci kvm = q->matrix_mdev->kvm; 44362306a36Sopenharmony_ci gisa = kvm->arch.gisa_int.origin; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci h_nib = page_to_phys(h_page) | (nib & ~PAGE_MASK); 44662306a36Sopenharmony_ci aqic_gisa.gisc = isc; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* NIB in non-shared storage is a rc 6 for PV guests */ 44962306a36Sopenharmony_ci if (kvm_s390_pv_cpu_is_protected(vcpu) && 45062306a36Sopenharmony_ci ensure_nib_shared(h_nib & PAGE_MASK, kvm->arch.gmap)) { 45162306a36Sopenharmony_ci vfio_unpin_pages(&q->matrix_mdev->vdev, nib, 1); 45262306a36Sopenharmony_ci status.response_code = AP_RESPONSE_INVALID_ADDRESS; 45362306a36Sopenharmony_ci return status; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci nisc = kvm_s390_gisc_register(kvm, isc); 45762306a36Sopenharmony_ci if (nisc < 0) { 45862306a36Sopenharmony_ci VFIO_AP_DBF_WARN("%s: gisc registration failed: nisc=%d, isc=%d, apqn=%#04x\n", 45962306a36Sopenharmony_ci __func__, nisc, isc, q->apqn); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci vfio_unpin_pages(&q->matrix_mdev->vdev, nib, 1); 46262306a36Sopenharmony_ci status.response_code = AP_RESPONSE_INVALID_GISA; 46362306a36Sopenharmony_ci return status; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci aqic_gisa.isc = nisc; 46762306a36Sopenharmony_ci aqic_gisa.ir = 1; 46862306a36Sopenharmony_ci aqic_gisa.gisa = virt_to_phys(gisa) >> 4; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci status = ap_aqic(q->apqn, aqic_gisa, h_nib); 47162306a36Sopenharmony_ci switch (status.response_code) { 47262306a36Sopenharmony_ci case AP_RESPONSE_NORMAL: 47362306a36Sopenharmony_ci /* See if we did clear older IRQ configuration */ 47462306a36Sopenharmony_ci vfio_ap_free_aqic_resources(q); 47562306a36Sopenharmony_ci q->saved_iova = nib; 47662306a36Sopenharmony_ci q->saved_isc = isc; 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci case AP_RESPONSE_OTHERWISE_CHANGED: 47962306a36Sopenharmony_ci /* We could not modify IRQ settings: clear new configuration */ 48062306a36Sopenharmony_ci vfio_unpin_pages(&q->matrix_mdev->vdev, nib, 1); 48162306a36Sopenharmony_ci kvm_s390_gisc_unregister(kvm, isc); 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci default: 48462306a36Sopenharmony_ci pr_warn("%s: apqn %04x: response: %02x\n", __func__, q->apqn, 48562306a36Sopenharmony_ci status.response_code); 48662306a36Sopenharmony_ci vfio_ap_irq_disable(q); 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (status.response_code != AP_RESPONSE_NORMAL) { 49162306a36Sopenharmony_ci VFIO_AP_DBF_WARN("%s: PQAP(AQIC) failed with status=%#02x: " 49262306a36Sopenharmony_ci "zone=%#x, ir=%#x, gisc=%#x, f=%#x," 49362306a36Sopenharmony_ci "gisa=%#x, isc=%#x, apqn=%#04x\n", 49462306a36Sopenharmony_ci __func__, status.response_code, 49562306a36Sopenharmony_ci aqic_gisa.zone, aqic_gisa.ir, aqic_gisa.gisc, 49662306a36Sopenharmony_ci aqic_gisa.gf, aqic_gisa.gisa, aqic_gisa.isc, 49762306a36Sopenharmony_ci q->apqn); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return status; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/** 50462306a36Sopenharmony_ci * vfio_ap_le_guid_to_be_uuid - convert a little endian guid array into an array 50562306a36Sopenharmony_ci * of big endian elements that can be passed by 50662306a36Sopenharmony_ci * value to an s390dbf sprintf event function to 50762306a36Sopenharmony_ci * format a UUID string. 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * @guid: the object containing the little endian guid 51062306a36Sopenharmony_ci * @uuid: a six-element array of long values that can be passed by value as 51162306a36Sopenharmony_ci * arguments for a formatting string specifying a UUID. 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * The S390 Debug Feature (s390dbf) allows the use of "%s" in the sprintf 51462306a36Sopenharmony_ci * event functions if the memory for the passed string is available as long as 51562306a36Sopenharmony_ci * the debug feature exists. Since a mediated device can be removed at any 51662306a36Sopenharmony_ci * time, it's name can not be used because %s passes the reference to the string 51762306a36Sopenharmony_ci * in memory and the reference will go stale once the device is removed . 51862306a36Sopenharmony_ci * 51962306a36Sopenharmony_ci * The s390dbf string formatting function allows a maximum of 9 arguments for a 52062306a36Sopenharmony_ci * message to be displayed in the 'sprintf' view. In order to use the bytes 52162306a36Sopenharmony_ci * comprising the mediated device's UUID to display the mediated device name, 52262306a36Sopenharmony_ci * they will have to be converted into an array whose elements can be passed by 52362306a36Sopenharmony_ci * value to sprintf. For example: 52462306a36Sopenharmony_ci * 52562306a36Sopenharmony_ci * guid array: { 83, 78, 17, 62, bb, f1, f0, 47, 91, 4d, 32, a2, 2e, 3a, 88, 04 } 52662306a36Sopenharmony_ci * mdev name: 62177883-f1bb-47f0-914d-32a22e3a8804 52762306a36Sopenharmony_ci * array returned: { 62177883, f1bb, 47f0, 914d, 32a2, 2e3a8804 } 52862306a36Sopenharmony_ci * formatting string: "%08lx-%04lx-%04lx-%04lx-%02lx%04lx" 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_cistatic void vfio_ap_le_guid_to_be_uuid(guid_t *guid, unsigned long *uuid) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci /* 53362306a36Sopenharmony_ci * The input guid is ordered in little endian, so it needs to be 53462306a36Sopenharmony_ci * reordered for displaying a UUID as a string. This specifies the 53562306a36Sopenharmony_ci * guid indices in proper order. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci uuid[0] = le32_to_cpup((__le32 *)guid); 53862306a36Sopenharmony_ci uuid[1] = le16_to_cpup((__le16 *)&guid->b[4]); 53962306a36Sopenharmony_ci uuid[2] = le16_to_cpup((__le16 *)&guid->b[6]); 54062306a36Sopenharmony_ci uuid[3] = *((__u16 *)&guid->b[8]); 54162306a36Sopenharmony_ci uuid[4] = *((__u16 *)&guid->b[10]); 54262306a36Sopenharmony_ci uuid[5] = *((__u32 *)&guid->b[12]); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/** 54662306a36Sopenharmony_ci * handle_pqap - PQAP instruction callback 54762306a36Sopenharmony_ci * 54862306a36Sopenharmony_ci * @vcpu: The vcpu on which we received the PQAP instruction 54962306a36Sopenharmony_ci * 55062306a36Sopenharmony_ci * Get the general register contents to initialize internal variables. 55162306a36Sopenharmony_ci * REG[0]: APQN 55262306a36Sopenharmony_ci * REG[1]: IR and ISC 55362306a36Sopenharmony_ci * REG[2]: NIB 55462306a36Sopenharmony_ci * 55562306a36Sopenharmony_ci * Response.status may be set to following Response Code: 55662306a36Sopenharmony_ci * - AP_RESPONSE_Q_NOT_AVAIL: if the queue is not available 55762306a36Sopenharmony_ci * - AP_RESPONSE_DECONFIGURED: if the queue is not configured 55862306a36Sopenharmony_ci * - AP_RESPONSE_NORMAL (0) : in case of success 55962306a36Sopenharmony_ci * Check vfio_ap_setirq() and vfio_ap_clrirq() for other possible RC. 56062306a36Sopenharmony_ci * We take the matrix_dev lock to ensure serialization on queues and 56162306a36Sopenharmony_ci * mediated device access. 56262306a36Sopenharmony_ci * 56362306a36Sopenharmony_ci * Return: 0 if we could handle the request inside KVM. 56462306a36Sopenharmony_ci * Otherwise, returns -EOPNOTSUPP to let QEMU handle the fault. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_cistatic int handle_pqap(struct kvm_vcpu *vcpu) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci uint64_t status; 56962306a36Sopenharmony_ci uint16_t apqn; 57062306a36Sopenharmony_ci unsigned long uuid[6]; 57162306a36Sopenharmony_ci struct vfio_ap_queue *q; 57262306a36Sopenharmony_ci struct ap_queue_status qstatus = { 57362306a36Sopenharmony_ci .response_code = AP_RESPONSE_Q_NOT_AVAIL, }; 57462306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci apqn = vcpu->run->s.regs.gprs[0] & 0xffff; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* If we do not use the AIV facility just go to userland */ 57962306a36Sopenharmony_ci if (!(vcpu->arch.sie_block->eca & ECA_AIV)) { 58062306a36Sopenharmony_ci VFIO_AP_DBF_WARN("%s: AIV facility not installed: apqn=0x%04x, eca=0x%04x\n", 58162306a36Sopenharmony_ci __func__, apqn, vcpu->arch.sie_block->eca); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return -EOPNOTSUPP; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (!vcpu->kvm->arch.crypto.pqap_hook) { 58962306a36Sopenharmony_ci VFIO_AP_DBF_WARN("%s: PQAP(AQIC) hook not registered with the vfio_ap driver: apqn=0x%04x\n", 59062306a36Sopenharmony_ci __func__, apqn); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci goto out_unlock; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci matrix_mdev = container_of(vcpu->kvm->arch.crypto.pqap_hook, 59662306a36Sopenharmony_ci struct ap_matrix_mdev, pqap_hook); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* If the there is no guest using the mdev, there is nothing to do */ 59962306a36Sopenharmony_ci if (!matrix_mdev->kvm) { 60062306a36Sopenharmony_ci vfio_ap_le_guid_to_be_uuid(&matrix_mdev->mdev->uuid, uuid); 60162306a36Sopenharmony_ci VFIO_AP_DBF_WARN("%s: mdev %08lx-%04lx-%04lx-%04lx-%04lx%08lx not in use: apqn=0x%04x\n", 60262306a36Sopenharmony_ci __func__, uuid[0], uuid[1], uuid[2], 60362306a36Sopenharmony_ci uuid[3], uuid[4], uuid[5], apqn); 60462306a36Sopenharmony_ci goto out_unlock; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci q = vfio_ap_mdev_get_queue(matrix_mdev, apqn); 60862306a36Sopenharmony_ci if (!q) { 60962306a36Sopenharmony_ci VFIO_AP_DBF_WARN("%s: Queue %02x.%04x not bound to the vfio_ap driver\n", 61062306a36Sopenharmony_ci __func__, AP_QID_CARD(apqn), 61162306a36Sopenharmony_ci AP_QID_QUEUE(apqn)); 61262306a36Sopenharmony_ci goto out_unlock; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci status = vcpu->run->s.regs.gprs[1]; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* If IR bit(16) is set we enable the interrupt */ 61862306a36Sopenharmony_ci if ((status >> (63 - 16)) & 0x01) 61962306a36Sopenharmony_ci qstatus = vfio_ap_irq_enable(q, status & 0x07, vcpu); 62062306a36Sopenharmony_ci else 62162306a36Sopenharmony_ci qstatus = vfio_ap_irq_disable(q); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ciout_unlock: 62462306a36Sopenharmony_ci memcpy(&vcpu->run->s.regs.gprs[1], &qstatus, sizeof(qstatus)); 62562306a36Sopenharmony_ci vcpu->run->s.regs.gprs[1] >>= 32; 62662306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic void vfio_ap_matrix_init(struct ap_config_info *info, 63162306a36Sopenharmony_ci struct ap_matrix *matrix) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci matrix->apm_max = info->apxa ? info->na : 63; 63462306a36Sopenharmony_ci matrix->aqm_max = info->apxa ? info->nd : 15; 63562306a36Sopenharmony_ci matrix->adm_max = info->apxa ? info->nd : 15; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic void vfio_ap_mdev_update_guest_apcb(struct ap_matrix_mdev *matrix_mdev) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci if (matrix_mdev->kvm) 64162306a36Sopenharmony_ci kvm_arch_crypto_set_masks(matrix_mdev->kvm, 64262306a36Sopenharmony_ci matrix_mdev->shadow_apcb.apm, 64362306a36Sopenharmony_ci matrix_mdev->shadow_apcb.aqm, 64462306a36Sopenharmony_ci matrix_mdev->shadow_apcb.adm); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic bool vfio_ap_mdev_filter_cdoms(struct ap_matrix_mdev *matrix_mdev) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci DECLARE_BITMAP(prev_shadow_adm, AP_DOMAINS); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci bitmap_copy(prev_shadow_adm, matrix_mdev->shadow_apcb.adm, AP_DOMAINS); 65262306a36Sopenharmony_ci bitmap_and(matrix_mdev->shadow_apcb.adm, matrix_mdev->matrix.adm, 65362306a36Sopenharmony_ci (unsigned long *)matrix_dev->info.adm, AP_DOMAINS); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return !bitmap_equal(prev_shadow_adm, matrix_mdev->shadow_apcb.adm, 65662306a36Sopenharmony_ci AP_DOMAINS); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci/* 66062306a36Sopenharmony_ci * vfio_ap_mdev_filter_matrix - filter the APQNs assigned to the matrix mdev 66162306a36Sopenharmony_ci * to ensure no queue devices are passed through to 66262306a36Sopenharmony_ci * the guest that are not bound to the vfio_ap 66362306a36Sopenharmony_ci * device driver. 66462306a36Sopenharmony_ci * 66562306a36Sopenharmony_ci * @matrix_mdev: the matrix mdev whose matrix is to be filtered. 66662306a36Sopenharmony_ci * @apm_filtered: a 256-bit bitmap for storing the APIDs filtered from the 66762306a36Sopenharmony_ci * guest's AP configuration that are still in the host's AP 66862306a36Sopenharmony_ci * configuration. 66962306a36Sopenharmony_ci * 67062306a36Sopenharmony_ci * Note: If an APQN referencing a queue device that is not bound to the vfio_ap 67162306a36Sopenharmony_ci * driver, its APID will be filtered from the guest's APCB. The matrix 67262306a36Sopenharmony_ci * structure precludes filtering an individual APQN, so its APID will be 67362306a36Sopenharmony_ci * filtered. Consequently, all queues associated with the adapter that 67462306a36Sopenharmony_ci * are in the host's AP configuration must be reset. If queues are 67562306a36Sopenharmony_ci * subsequently made available again to the guest, they should re-appear 67662306a36Sopenharmony_ci * in a reset state 67762306a36Sopenharmony_ci * 67862306a36Sopenharmony_ci * Return: a boolean value indicating whether the KVM guest's APCB was changed 67962306a36Sopenharmony_ci * by the filtering or not. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_cistatic bool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev, 68262306a36Sopenharmony_ci unsigned long *apm_filtered) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci unsigned long apid, apqi, apqn; 68562306a36Sopenharmony_ci DECLARE_BITMAP(prev_shadow_apm, AP_DEVICES); 68662306a36Sopenharmony_ci DECLARE_BITMAP(prev_shadow_aqm, AP_DOMAINS); 68762306a36Sopenharmony_ci struct vfio_ap_queue *q; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci bitmap_copy(prev_shadow_apm, matrix_mdev->shadow_apcb.apm, AP_DEVICES); 69062306a36Sopenharmony_ci bitmap_copy(prev_shadow_aqm, matrix_mdev->shadow_apcb.aqm, AP_DOMAINS); 69162306a36Sopenharmony_ci vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->shadow_apcb); 69262306a36Sopenharmony_ci bitmap_clear(apm_filtered, 0, AP_DEVICES); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* 69562306a36Sopenharmony_ci * Copy the adapters, domains and control domains to the shadow_apcb 69662306a36Sopenharmony_ci * from the matrix mdev, but only those that are assigned to the host's 69762306a36Sopenharmony_ci * AP configuration. 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_ci bitmap_and(matrix_mdev->shadow_apcb.apm, matrix_mdev->matrix.apm, 70062306a36Sopenharmony_ci (unsigned long *)matrix_dev->info.apm, AP_DEVICES); 70162306a36Sopenharmony_ci bitmap_and(matrix_mdev->shadow_apcb.aqm, matrix_mdev->matrix.aqm, 70262306a36Sopenharmony_ci (unsigned long *)matrix_dev->info.aqm, AP_DOMAINS); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci for_each_set_bit_inv(apid, matrix_mdev->shadow_apcb.apm, AP_DEVICES) { 70562306a36Sopenharmony_ci for_each_set_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm, 70662306a36Sopenharmony_ci AP_DOMAINS) { 70762306a36Sopenharmony_ci /* 70862306a36Sopenharmony_ci * If the APQN is not bound to the vfio_ap device 70962306a36Sopenharmony_ci * driver, then we can't assign it to the guest's 71062306a36Sopenharmony_ci * AP configuration. The AP architecture won't 71162306a36Sopenharmony_ci * allow filtering of a single APQN, so let's filter 71262306a36Sopenharmony_ci * the APID since an adapter represents a physical 71362306a36Sopenharmony_ci * hardware device. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci apqn = AP_MKQID(apid, apqi); 71662306a36Sopenharmony_ci q = vfio_ap_mdev_get_queue(matrix_mdev, apqn); 71762306a36Sopenharmony_ci if (!q || q->reset_status.response_code) { 71862306a36Sopenharmony_ci clear_bit_inv(apid, matrix_mdev->shadow_apcb.apm); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* 72162306a36Sopenharmony_ci * If the adapter was previously plugged into 72262306a36Sopenharmony_ci * the guest, let's let the caller know that 72362306a36Sopenharmony_ci * the APID was filtered. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci if (test_bit_inv(apid, prev_shadow_apm)) 72662306a36Sopenharmony_ci set_bit_inv(apid, apm_filtered); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return !bitmap_equal(prev_shadow_apm, matrix_mdev->shadow_apcb.apm, 73462306a36Sopenharmony_ci AP_DEVICES) || 73562306a36Sopenharmony_ci !bitmap_equal(prev_shadow_aqm, matrix_mdev->shadow_apcb.aqm, 73662306a36Sopenharmony_ci AP_DOMAINS); 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic int vfio_ap_mdev_init_dev(struct vfio_device *vdev) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = 74262306a36Sopenharmony_ci container_of(vdev, struct ap_matrix_mdev, vdev); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci matrix_mdev->mdev = to_mdev_device(vdev->dev); 74562306a36Sopenharmony_ci vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->matrix); 74662306a36Sopenharmony_ci matrix_mdev->pqap_hook = handle_pqap; 74762306a36Sopenharmony_ci vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->shadow_apcb); 74862306a36Sopenharmony_ci hash_init(matrix_mdev->qtable.queues); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci return 0; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic int vfio_ap_mdev_probe(struct mdev_device *mdev) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 75662306a36Sopenharmony_ci int ret; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci matrix_mdev = vfio_alloc_device(ap_matrix_mdev, vdev, &mdev->dev, 75962306a36Sopenharmony_ci &vfio_ap_matrix_dev_ops); 76062306a36Sopenharmony_ci if (IS_ERR(matrix_mdev)) 76162306a36Sopenharmony_ci return PTR_ERR(matrix_mdev); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci ret = vfio_register_emulated_iommu_dev(&matrix_mdev->vdev); 76462306a36Sopenharmony_ci if (ret) 76562306a36Sopenharmony_ci goto err_put_vdev; 76662306a36Sopenharmony_ci matrix_mdev->req_trigger = NULL; 76762306a36Sopenharmony_ci dev_set_drvdata(&mdev->dev, matrix_mdev); 76862306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 76962306a36Sopenharmony_ci list_add(&matrix_mdev->node, &matrix_dev->mdev_list); 77062306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 77162306a36Sopenharmony_ci return 0; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cierr_put_vdev: 77462306a36Sopenharmony_ci vfio_put_device(&matrix_mdev->vdev); 77562306a36Sopenharmony_ci return ret; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic void vfio_ap_mdev_link_queue(struct ap_matrix_mdev *matrix_mdev, 77962306a36Sopenharmony_ci struct vfio_ap_queue *q) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci if (q) { 78262306a36Sopenharmony_ci q->matrix_mdev = matrix_mdev; 78362306a36Sopenharmony_ci hash_add(matrix_mdev->qtable.queues, &q->mdev_qnode, q->apqn); 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic void vfio_ap_mdev_link_apqn(struct ap_matrix_mdev *matrix_mdev, int apqn) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct vfio_ap_queue *q; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci q = vfio_ap_find_queue(apqn); 79262306a36Sopenharmony_ci vfio_ap_mdev_link_queue(matrix_mdev, q); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic void vfio_ap_unlink_queue_fr_mdev(struct vfio_ap_queue *q) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci hash_del(&q->mdev_qnode); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic void vfio_ap_unlink_mdev_fr_queue(struct vfio_ap_queue *q) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci q->matrix_mdev = NULL; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic void vfio_ap_mdev_unlink_fr_queues(struct ap_matrix_mdev *matrix_mdev) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci struct vfio_ap_queue *q; 80862306a36Sopenharmony_ci unsigned long apid, apqi; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, AP_DEVICES) { 81162306a36Sopenharmony_ci for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, 81262306a36Sopenharmony_ci AP_DOMAINS) { 81362306a36Sopenharmony_ci q = vfio_ap_mdev_get_queue(matrix_mdev, 81462306a36Sopenharmony_ci AP_MKQID(apid, apqi)); 81562306a36Sopenharmony_ci if (q) 81662306a36Sopenharmony_ci q->matrix_mdev = NULL; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic void vfio_ap_mdev_remove(struct mdev_device *mdev) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(&mdev->dev); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci vfio_unregister_group_dev(&matrix_mdev->vdev); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci mutex_lock(&matrix_dev->guests_lock); 82862306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 82962306a36Sopenharmony_ci vfio_ap_mdev_reset_queues(matrix_mdev); 83062306a36Sopenharmony_ci vfio_ap_mdev_unlink_fr_queues(matrix_mdev); 83162306a36Sopenharmony_ci list_del(&matrix_mdev->node); 83262306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 83362306a36Sopenharmony_ci mutex_unlock(&matrix_dev->guests_lock); 83462306a36Sopenharmony_ci vfio_put_device(&matrix_mdev->vdev); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci#define MDEV_SHARING_ERR "Userspace may not re-assign queue %02lx.%04lx " \ 83862306a36Sopenharmony_ci "already assigned to %s" 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic void vfio_ap_mdev_log_sharing_err(struct ap_matrix_mdev *matrix_mdev, 84162306a36Sopenharmony_ci unsigned long *apm, 84262306a36Sopenharmony_ci unsigned long *aqm) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci unsigned long apid, apqi; 84562306a36Sopenharmony_ci const struct device *dev = mdev_dev(matrix_mdev->mdev); 84662306a36Sopenharmony_ci const char *mdev_name = dev_name(dev); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci for_each_set_bit_inv(apid, apm, AP_DEVICES) 84962306a36Sopenharmony_ci for_each_set_bit_inv(apqi, aqm, AP_DOMAINS) 85062306a36Sopenharmony_ci dev_warn(dev, MDEV_SHARING_ERR, apid, apqi, mdev_name); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci/** 85462306a36Sopenharmony_ci * vfio_ap_mdev_verify_no_sharing - verify APQNs are not shared by matrix mdevs 85562306a36Sopenharmony_ci * 85662306a36Sopenharmony_ci * @mdev_apm: mask indicating the APIDs of the APQNs to be verified 85762306a36Sopenharmony_ci * @mdev_aqm: mask indicating the APQIs of the APQNs to be verified 85862306a36Sopenharmony_ci * 85962306a36Sopenharmony_ci * Verifies that each APQN derived from the Cartesian product of a bitmap of 86062306a36Sopenharmony_ci * AP adapter IDs and AP queue indexes is not configured for any matrix 86162306a36Sopenharmony_ci * mediated device. AP queue sharing is not allowed. 86262306a36Sopenharmony_ci * 86362306a36Sopenharmony_ci * Return: 0 if the APQNs are not shared; otherwise return -EADDRINUSE. 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_cistatic int vfio_ap_mdev_verify_no_sharing(unsigned long *mdev_apm, 86662306a36Sopenharmony_ci unsigned long *mdev_aqm) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 86962306a36Sopenharmony_ci DECLARE_BITMAP(apm, AP_DEVICES); 87062306a36Sopenharmony_ci DECLARE_BITMAP(aqm, AP_DOMAINS); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { 87362306a36Sopenharmony_ci /* 87462306a36Sopenharmony_ci * If the input apm and aqm are fields of the matrix_mdev 87562306a36Sopenharmony_ci * object, then move on to the next matrix_mdev. 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_ci if (mdev_apm == matrix_mdev->matrix.apm && 87862306a36Sopenharmony_ci mdev_aqm == matrix_mdev->matrix.aqm) 87962306a36Sopenharmony_ci continue; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci memset(apm, 0, sizeof(apm)); 88262306a36Sopenharmony_ci memset(aqm, 0, sizeof(aqm)); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* 88562306a36Sopenharmony_ci * We work on full longs, as we can only exclude the leftover 88662306a36Sopenharmony_ci * bits in non-inverse order. The leftover is all zeros. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci if (!bitmap_and(apm, mdev_apm, matrix_mdev->matrix.apm, 88962306a36Sopenharmony_ci AP_DEVICES)) 89062306a36Sopenharmony_ci continue; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (!bitmap_and(aqm, mdev_aqm, matrix_mdev->matrix.aqm, 89362306a36Sopenharmony_ci AP_DOMAINS)) 89462306a36Sopenharmony_ci continue; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci vfio_ap_mdev_log_sharing_err(matrix_mdev, apm, aqm); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return -EADDRINUSE; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci return 0; 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci/** 90562306a36Sopenharmony_ci * vfio_ap_mdev_validate_masks - verify that the APQNs assigned to the mdev are 90662306a36Sopenharmony_ci * not reserved for the default zcrypt driver and 90762306a36Sopenharmony_ci * are not assigned to another mdev. 90862306a36Sopenharmony_ci * 90962306a36Sopenharmony_ci * @matrix_mdev: the mdev to which the APQNs being validated are assigned. 91062306a36Sopenharmony_ci * 91162306a36Sopenharmony_ci * Return: One of the following values: 91262306a36Sopenharmony_ci * o the error returned from the ap_apqn_in_matrix_owned_by_def_drv() function, 91362306a36Sopenharmony_ci * most likely -EBUSY indicating the ap_perms_mutex lock is already held. 91462306a36Sopenharmony_ci * o EADDRNOTAVAIL if an APQN assigned to @matrix_mdev is reserved for the 91562306a36Sopenharmony_ci * zcrypt default driver. 91662306a36Sopenharmony_ci * o EADDRINUSE if an APQN assigned to @matrix_mdev is assigned to another mdev 91762306a36Sopenharmony_ci * o A zero indicating validation succeeded. 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_cistatic int vfio_ap_mdev_validate_masks(struct ap_matrix_mdev *matrix_mdev) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci if (ap_apqn_in_matrix_owned_by_def_drv(matrix_mdev->matrix.apm, 92262306a36Sopenharmony_ci matrix_mdev->matrix.aqm)) 92362306a36Sopenharmony_ci return -EADDRNOTAVAIL; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return vfio_ap_mdev_verify_no_sharing(matrix_mdev->matrix.apm, 92662306a36Sopenharmony_ci matrix_mdev->matrix.aqm); 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic void vfio_ap_mdev_link_adapter(struct ap_matrix_mdev *matrix_mdev, 93062306a36Sopenharmony_ci unsigned long apid) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci unsigned long apqi; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, AP_DOMAINS) 93562306a36Sopenharmony_ci vfio_ap_mdev_link_apqn(matrix_mdev, 93662306a36Sopenharmony_ci AP_MKQID(apid, apqi)); 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic void collect_queues_to_reset(struct ap_matrix_mdev *matrix_mdev, 94062306a36Sopenharmony_ci unsigned long apid, 94162306a36Sopenharmony_ci struct list_head *qlist) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct vfio_ap_queue *q; 94462306a36Sopenharmony_ci unsigned long apqi; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci for_each_set_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm, AP_DOMAINS) { 94762306a36Sopenharmony_ci q = vfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi)); 94862306a36Sopenharmony_ci if (q) 94962306a36Sopenharmony_ci list_add_tail(&q->reset_qnode, qlist); 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic void reset_queues_for_apid(struct ap_matrix_mdev *matrix_mdev, 95462306a36Sopenharmony_ci unsigned long apid) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct list_head qlist; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci INIT_LIST_HEAD(&qlist); 95962306a36Sopenharmony_ci collect_queues_to_reset(matrix_mdev, apid, &qlist); 96062306a36Sopenharmony_ci vfio_ap_mdev_reset_qlist(&qlist); 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic int reset_queues_for_apids(struct ap_matrix_mdev *matrix_mdev, 96462306a36Sopenharmony_ci unsigned long *apm_reset) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct list_head qlist; 96762306a36Sopenharmony_ci unsigned long apid; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (bitmap_empty(apm_reset, AP_DEVICES)) 97062306a36Sopenharmony_ci return 0; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci INIT_LIST_HEAD(&qlist); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci for_each_set_bit_inv(apid, apm_reset, AP_DEVICES) 97562306a36Sopenharmony_ci collect_queues_to_reset(matrix_mdev, apid, &qlist); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return vfio_ap_mdev_reset_qlist(&qlist); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/** 98162306a36Sopenharmony_ci * assign_adapter_store - parses the APID from @buf and sets the 98262306a36Sopenharmony_ci * corresponding bit in the mediated matrix device's APM 98362306a36Sopenharmony_ci * 98462306a36Sopenharmony_ci * @dev: the matrix device 98562306a36Sopenharmony_ci * @attr: the mediated matrix device's assign_adapter attribute 98662306a36Sopenharmony_ci * @buf: a buffer containing the AP adapter number (APID) to 98762306a36Sopenharmony_ci * be assigned 98862306a36Sopenharmony_ci * @count: the number of bytes in @buf 98962306a36Sopenharmony_ci * 99062306a36Sopenharmony_ci * Return: the number of bytes processed if the APID is valid; otherwise, 99162306a36Sopenharmony_ci * returns one of the following errors: 99262306a36Sopenharmony_ci * 99362306a36Sopenharmony_ci * 1. -EINVAL 99462306a36Sopenharmony_ci * The APID is not a valid number 99562306a36Sopenharmony_ci * 99662306a36Sopenharmony_ci * 2. -ENODEV 99762306a36Sopenharmony_ci * The APID exceeds the maximum value configured for the system 99862306a36Sopenharmony_ci * 99962306a36Sopenharmony_ci * 3. -EADDRNOTAVAIL 100062306a36Sopenharmony_ci * An APQN derived from the cross product of the APID being assigned 100162306a36Sopenharmony_ci * and the APQIs previously assigned is not bound to the vfio_ap device 100262306a36Sopenharmony_ci * driver; or, if no APQIs have yet been assigned, the APID is not 100362306a36Sopenharmony_ci * contained in an APQN bound to the vfio_ap device driver. 100462306a36Sopenharmony_ci * 100562306a36Sopenharmony_ci * 4. -EADDRINUSE 100662306a36Sopenharmony_ci * An APQN derived from the cross product of the APID being assigned 100762306a36Sopenharmony_ci * and the APQIs previously assigned is being used by another mediated 100862306a36Sopenharmony_ci * matrix device 100962306a36Sopenharmony_ci * 101062306a36Sopenharmony_ci * 5. -EAGAIN 101162306a36Sopenharmony_ci * A lock required to validate the mdev's AP configuration could not 101262306a36Sopenharmony_ci * be obtained. 101362306a36Sopenharmony_ci */ 101462306a36Sopenharmony_cistatic ssize_t assign_adapter_store(struct device *dev, 101562306a36Sopenharmony_ci struct device_attribute *attr, 101662306a36Sopenharmony_ci const char *buf, size_t count) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci int ret; 101962306a36Sopenharmony_ci unsigned long apid; 102062306a36Sopenharmony_ci DECLARE_BITMAP(apm_filtered, AP_DEVICES); 102162306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci mutex_lock(&ap_perms_mutex); 102462306a36Sopenharmony_ci get_update_locks_for_mdev(matrix_mdev); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &apid); 102762306a36Sopenharmony_ci if (ret) 102862306a36Sopenharmony_ci goto done; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (apid > matrix_mdev->matrix.apm_max) { 103162306a36Sopenharmony_ci ret = -ENODEV; 103262306a36Sopenharmony_ci goto done; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (test_bit_inv(apid, matrix_mdev->matrix.apm)) { 103662306a36Sopenharmony_ci ret = count; 103762306a36Sopenharmony_ci goto done; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci set_bit_inv(apid, matrix_mdev->matrix.apm); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci ret = vfio_ap_mdev_validate_masks(matrix_mdev); 104362306a36Sopenharmony_ci if (ret) { 104462306a36Sopenharmony_ci clear_bit_inv(apid, matrix_mdev->matrix.apm); 104562306a36Sopenharmony_ci goto done; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci vfio_ap_mdev_link_adapter(matrix_mdev, apid); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci if (vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered)) { 105162306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 105262306a36Sopenharmony_ci reset_queues_for_apids(matrix_mdev, apm_filtered); 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci ret = count; 105662306a36Sopenharmony_cidone: 105762306a36Sopenharmony_ci release_update_locks_for_mdev(matrix_mdev); 105862306a36Sopenharmony_ci mutex_unlock(&ap_perms_mutex); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci return ret; 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_cistatic DEVICE_ATTR_WO(assign_adapter); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic struct vfio_ap_queue 106562306a36Sopenharmony_ci*vfio_ap_unlink_apqn_fr_mdev(struct ap_matrix_mdev *matrix_mdev, 106662306a36Sopenharmony_ci unsigned long apid, unsigned long apqi) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct vfio_ap_queue *q = NULL; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci q = vfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi)); 107162306a36Sopenharmony_ci /* If the queue is assigned to the matrix mdev, unlink it. */ 107262306a36Sopenharmony_ci if (q) 107362306a36Sopenharmony_ci vfio_ap_unlink_queue_fr_mdev(q); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci return q; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci/** 107962306a36Sopenharmony_ci * vfio_ap_mdev_unlink_adapter - unlink all queues associated with unassigned 108062306a36Sopenharmony_ci * adapter from the matrix mdev to which the 108162306a36Sopenharmony_ci * adapter was assigned. 108262306a36Sopenharmony_ci * @matrix_mdev: the matrix mediated device to which the adapter was assigned. 108362306a36Sopenharmony_ci * @apid: the APID of the unassigned adapter. 108462306a36Sopenharmony_ci * @qlist: list for storing queues associated with unassigned adapter that 108562306a36Sopenharmony_ci * need to be reset. 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_cistatic void vfio_ap_mdev_unlink_adapter(struct ap_matrix_mdev *matrix_mdev, 108862306a36Sopenharmony_ci unsigned long apid, 108962306a36Sopenharmony_ci struct list_head *qlist) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci unsigned long apqi; 109262306a36Sopenharmony_ci struct vfio_ap_queue *q; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, AP_DOMAINS) { 109562306a36Sopenharmony_ci q = vfio_ap_unlink_apqn_fr_mdev(matrix_mdev, apid, apqi); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (q && qlist) { 109862306a36Sopenharmony_ci if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) && 109962306a36Sopenharmony_ci test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) 110062306a36Sopenharmony_ci list_add_tail(&q->reset_qnode, qlist); 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic void vfio_ap_mdev_hot_unplug_adapter(struct ap_matrix_mdev *matrix_mdev, 110662306a36Sopenharmony_ci unsigned long apid) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct vfio_ap_queue *q, *tmpq; 110962306a36Sopenharmony_ci struct list_head qlist; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci INIT_LIST_HEAD(&qlist); 111262306a36Sopenharmony_ci vfio_ap_mdev_unlink_adapter(matrix_mdev, apid, &qlist); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm)) { 111562306a36Sopenharmony_ci clear_bit_inv(apid, matrix_mdev->shadow_apcb.apm); 111662306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci vfio_ap_mdev_reset_qlist(&qlist); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci list_for_each_entry_safe(q, tmpq, &qlist, reset_qnode) { 112262306a36Sopenharmony_ci vfio_ap_unlink_mdev_fr_queue(q); 112362306a36Sopenharmony_ci list_del(&q->reset_qnode); 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci/** 112862306a36Sopenharmony_ci * unassign_adapter_store - parses the APID from @buf and clears the 112962306a36Sopenharmony_ci * corresponding bit in the mediated matrix device's APM 113062306a36Sopenharmony_ci * 113162306a36Sopenharmony_ci * @dev: the matrix device 113262306a36Sopenharmony_ci * @attr: the mediated matrix device's unassign_adapter attribute 113362306a36Sopenharmony_ci * @buf: a buffer containing the adapter number (APID) to be unassigned 113462306a36Sopenharmony_ci * @count: the number of bytes in @buf 113562306a36Sopenharmony_ci * 113662306a36Sopenharmony_ci * Return: the number of bytes processed if the APID is valid; otherwise, 113762306a36Sopenharmony_ci * returns one of the following errors: 113862306a36Sopenharmony_ci * -EINVAL if the APID is not a number 113962306a36Sopenharmony_ci * -ENODEV if the APID it exceeds the maximum value configured for the 114062306a36Sopenharmony_ci * system 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_cistatic ssize_t unassign_adapter_store(struct device *dev, 114362306a36Sopenharmony_ci struct device_attribute *attr, 114462306a36Sopenharmony_ci const char *buf, size_t count) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci int ret; 114762306a36Sopenharmony_ci unsigned long apid; 114862306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci get_update_locks_for_mdev(matrix_mdev); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &apid); 115362306a36Sopenharmony_ci if (ret) 115462306a36Sopenharmony_ci goto done; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (apid > matrix_mdev->matrix.apm_max) { 115762306a36Sopenharmony_ci ret = -ENODEV; 115862306a36Sopenharmony_ci goto done; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (!test_bit_inv(apid, matrix_mdev->matrix.apm)) { 116262306a36Sopenharmony_ci ret = count; 116362306a36Sopenharmony_ci goto done; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci clear_bit_inv((unsigned long)apid, matrix_mdev->matrix.apm); 116762306a36Sopenharmony_ci vfio_ap_mdev_hot_unplug_adapter(matrix_mdev, apid); 116862306a36Sopenharmony_ci ret = count; 116962306a36Sopenharmony_cidone: 117062306a36Sopenharmony_ci release_update_locks_for_mdev(matrix_mdev); 117162306a36Sopenharmony_ci return ret; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_cistatic DEVICE_ATTR_WO(unassign_adapter); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic void vfio_ap_mdev_link_domain(struct ap_matrix_mdev *matrix_mdev, 117662306a36Sopenharmony_ci unsigned long apqi) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci unsigned long apid; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, AP_DEVICES) 118162306a36Sopenharmony_ci vfio_ap_mdev_link_apqn(matrix_mdev, 118262306a36Sopenharmony_ci AP_MKQID(apid, apqi)); 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci/** 118662306a36Sopenharmony_ci * assign_domain_store - parses the APQI from @buf and sets the 118762306a36Sopenharmony_ci * corresponding bit in the mediated matrix device's AQM 118862306a36Sopenharmony_ci * 118962306a36Sopenharmony_ci * @dev: the matrix device 119062306a36Sopenharmony_ci * @attr: the mediated matrix device's assign_domain attribute 119162306a36Sopenharmony_ci * @buf: a buffer containing the AP queue index (APQI) of the domain to 119262306a36Sopenharmony_ci * be assigned 119362306a36Sopenharmony_ci * @count: the number of bytes in @buf 119462306a36Sopenharmony_ci * 119562306a36Sopenharmony_ci * Return: the number of bytes processed if the APQI is valid; otherwise returns 119662306a36Sopenharmony_ci * one of the following errors: 119762306a36Sopenharmony_ci * 119862306a36Sopenharmony_ci * 1. -EINVAL 119962306a36Sopenharmony_ci * The APQI is not a valid number 120062306a36Sopenharmony_ci * 120162306a36Sopenharmony_ci * 2. -ENODEV 120262306a36Sopenharmony_ci * The APQI exceeds the maximum value configured for the system 120362306a36Sopenharmony_ci * 120462306a36Sopenharmony_ci * 3. -EADDRNOTAVAIL 120562306a36Sopenharmony_ci * An APQN derived from the cross product of the APQI being assigned 120662306a36Sopenharmony_ci * and the APIDs previously assigned is not bound to the vfio_ap device 120762306a36Sopenharmony_ci * driver; or, if no APIDs have yet been assigned, the APQI is not 120862306a36Sopenharmony_ci * contained in an APQN bound to the vfio_ap device driver. 120962306a36Sopenharmony_ci * 121062306a36Sopenharmony_ci * 4. -EADDRINUSE 121162306a36Sopenharmony_ci * An APQN derived from the cross product of the APQI being assigned 121262306a36Sopenharmony_ci * and the APIDs previously assigned is being used by another mediated 121362306a36Sopenharmony_ci * matrix device 121462306a36Sopenharmony_ci * 121562306a36Sopenharmony_ci * 5. -EAGAIN 121662306a36Sopenharmony_ci * The lock required to validate the mdev's AP configuration could not 121762306a36Sopenharmony_ci * be obtained. 121862306a36Sopenharmony_ci */ 121962306a36Sopenharmony_cistatic ssize_t assign_domain_store(struct device *dev, 122062306a36Sopenharmony_ci struct device_attribute *attr, 122162306a36Sopenharmony_ci const char *buf, size_t count) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci int ret; 122462306a36Sopenharmony_ci unsigned long apqi; 122562306a36Sopenharmony_ci DECLARE_BITMAP(apm_filtered, AP_DEVICES); 122662306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci mutex_lock(&ap_perms_mutex); 122962306a36Sopenharmony_ci get_update_locks_for_mdev(matrix_mdev); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &apqi); 123262306a36Sopenharmony_ci if (ret) 123362306a36Sopenharmony_ci goto done; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (apqi > matrix_mdev->matrix.aqm_max) { 123662306a36Sopenharmony_ci ret = -ENODEV; 123762306a36Sopenharmony_ci goto done; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci if (test_bit_inv(apqi, matrix_mdev->matrix.aqm)) { 124162306a36Sopenharmony_ci ret = count; 124262306a36Sopenharmony_ci goto done; 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci set_bit_inv(apqi, matrix_mdev->matrix.aqm); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci ret = vfio_ap_mdev_validate_masks(matrix_mdev); 124862306a36Sopenharmony_ci if (ret) { 124962306a36Sopenharmony_ci clear_bit_inv(apqi, matrix_mdev->matrix.aqm); 125062306a36Sopenharmony_ci goto done; 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci vfio_ap_mdev_link_domain(matrix_mdev, apqi); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered)) { 125662306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 125762306a36Sopenharmony_ci reset_queues_for_apids(matrix_mdev, apm_filtered); 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci ret = count; 126162306a36Sopenharmony_cidone: 126262306a36Sopenharmony_ci release_update_locks_for_mdev(matrix_mdev); 126362306a36Sopenharmony_ci mutex_unlock(&ap_perms_mutex); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci return ret; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_cistatic DEVICE_ATTR_WO(assign_domain); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic void vfio_ap_mdev_unlink_domain(struct ap_matrix_mdev *matrix_mdev, 127062306a36Sopenharmony_ci unsigned long apqi, 127162306a36Sopenharmony_ci struct list_head *qlist) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci unsigned long apid; 127462306a36Sopenharmony_ci struct vfio_ap_queue *q; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, AP_DEVICES) { 127762306a36Sopenharmony_ci q = vfio_ap_unlink_apqn_fr_mdev(matrix_mdev, apid, apqi); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (q && qlist) { 128062306a36Sopenharmony_ci if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) && 128162306a36Sopenharmony_ci test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) 128262306a36Sopenharmony_ci list_add_tail(&q->reset_qnode, qlist); 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic void vfio_ap_mdev_hot_unplug_domain(struct ap_matrix_mdev *matrix_mdev, 128862306a36Sopenharmony_ci unsigned long apqi) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct vfio_ap_queue *q, *tmpq; 129162306a36Sopenharmony_ci struct list_head qlist; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci INIT_LIST_HEAD(&qlist); 129462306a36Sopenharmony_ci vfio_ap_mdev_unlink_domain(matrix_mdev, apqi, &qlist); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci if (test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) { 129762306a36Sopenharmony_ci clear_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm); 129862306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci vfio_ap_mdev_reset_qlist(&qlist); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci list_for_each_entry_safe(q, tmpq, &qlist, reset_qnode) { 130462306a36Sopenharmony_ci vfio_ap_unlink_mdev_fr_queue(q); 130562306a36Sopenharmony_ci list_del(&q->reset_qnode); 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci/** 131062306a36Sopenharmony_ci * unassign_domain_store - parses the APQI from @buf and clears the 131162306a36Sopenharmony_ci * corresponding bit in the mediated matrix device's AQM 131262306a36Sopenharmony_ci * 131362306a36Sopenharmony_ci * @dev: the matrix device 131462306a36Sopenharmony_ci * @attr: the mediated matrix device's unassign_domain attribute 131562306a36Sopenharmony_ci * @buf: a buffer containing the AP queue index (APQI) of the domain to 131662306a36Sopenharmony_ci * be unassigned 131762306a36Sopenharmony_ci * @count: the number of bytes in @buf 131862306a36Sopenharmony_ci * 131962306a36Sopenharmony_ci * Return: the number of bytes processed if the APQI is valid; otherwise, 132062306a36Sopenharmony_ci * returns one of the following errors: 132162306a36Sopenharmony_ci * -EINVAL if the APQI is not a number 132262306a36Sopenharmony_ci * -ENODEV if the APQI exceeds the maximum value configured for the system 132362306a36Sopenharmony_ci */ 132462306a36Sopenharmony_cistatic ssize_t unassign_domain_store(struct device *dev, 132562306a36Sopenharmony_ci struct device_attribute *attr, 132662306a36Sopenharmony_ci const char *buf, size_t count) 132762306a36Sopenharmony_ci{ 132862306a36Sopenharmony_ci int ret; 132962306a36Sopenharmony_ci unsigned long apqi; 133062306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci get_update_locks_for_mdev(matrix_mdev); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &apqi); 133562306a36Sopenharmony_ci if (ret) 133662306a36Sopenharmony_ci goto done; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci if (apqi > matrix_mdev->matrix.aqm_max) { 133962306a36Sopenharmony_ci ret = -ENODEV; 134062306a36Sopenharmony_ci goto done; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (!test_bit_inv(apqi, matrix_mdev->matrix.aqm)) { 134462306a36Sopenharmony_ci ret = count; 134562306a36Sopenharmony_ci goto done; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci clear_bit_inv((unsigned long)apqi, matrix_mdev->matrix.aqm); 134962306a36Sopenharmony_ci vfio_ap_mdev_hot_unplug_domain(matrix_mdev, apqi); 135062306a36Sopenharmony_ci ret = count; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cidone: 135362306a36Sopenharmony_ci release_update_locks_for_mdev(matrix_mdev); 135462306a36Sopenharmony_ci return ret; 135562306a36Sopenharmony_ci} 135662306a36Sopenharmony_cistatic DEVICE_ATTR_WO(unassign_domain); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci/** 135962306a36Sopenharmony_ci * assign_control_domain_store - parses the domain ID from @buf and sets 136062306a36Sopenharmony_ci * the corresponding bit in the mediated matrix device's ADM 136162306a36Sopenharmony_ci * 136262306a36Sopenharmony_ci * @dev: the matrix device 136362306a36Sopenharmony_ci * @attr: the mediated matrix device's assign_control_domain attribute 136462306a36Sopenharmony_ci * @buf: a buffer containing the domain ID to be assigned 136562306a36Sopenharmony_ci * @count: the number of bytes in @buf 136662306a36Sopenharmony_ci * 136762306a36Sopenharmony_ci * Return: the number of bytes processed if the domain ID is valid; otherwise, 136862306a36Sopenharmony_ci * returns one of the following errors: 136962306a36Sopenharmony_ci * -EINVAL if the ID is not a number 137062306a36Sopenharmony_ci * -ENODEV if the ID exceeds the maximum value configured for the system 137162306a36Sopenharmony_ci */ 137262306a36Sopenharmony_cistatic ssize_t assign_control_domain_store(struct device *dev, 137362306a36Sopenharmony_ci struct device_attribute *attr, 137462306a36Sopenharmony_ci const char *buf, size_t count) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci int ret; 137762306a36Sopenharmony_ci unsigned long id; 137862306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci get_update_locks_for_mdev(matrix_mdev); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &id); 138362306a36Sopenharmony_ci if (ret) 138462306a36Sopenharmony_ci goto done; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (id > matrix_mdev->matrix.adm_max) { 138762306a36Sopenharmony_ci ret = -ENODEV; 138862306a36Sopenharmony_ci goto done; 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci if (test_bit_inv(id, matrix_mdev->matrix.adm)) { 139262306a36Sopenharmony_ci ret = count; 139362306a36Sopenharmony_ci goto done; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* Set the bit in the ADM (bitmask) corresponding to the AP control 139762306a36Sopenharmony_ci * domain number (id). The bits in the mask, from most significant to 139862306a36Sopenharmony_ci * least significant, correspond to IDs 0 up to the one less than the 139962306a36Sopenharmony_ci * number of control domains that can be assigned. 140062306a36Sopenharmony_ci */ 140162306a36Sopenharmony_ci set_bit_inv(id, matrix_mdev->matrix.adm); 140262306a36Sopenharmony_ci if (vfio_ap_mdev_filter_cdoms(matrix_mdev)) 140362306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci ret = count; 140662306a36Sopenharmony_cidone: 140762306a36Sopenharmony_ci release_update_locks_for_mdev(matrix_mdev); 140862306a36Sopenharmony_ci return ret; 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_cistatic DEVICE_ATTR_WO(assign_control_domain); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci/** 141362306a36Sopenharmony_ci * unassign_control_domain_store - parses the domain ID from @buf and 141462306a36Sopenharmony_ci * clears the corresponding bit in the mediated matrix device's ADM 141562306a36Sopenharmony_ci * 141662306a36Sopenharmony_ci * @dev: the matrix device 141762306a36Sopenharmony_ci * @attr: the mediated matrix device's unassign_control_domain attribute 141862306a36Sopenharmony_ci * @buf: a buffer containing the domain ID to be unassigned 141962306a36Sopenharmony_ci * @count: the number of bytes in @buf 142062306a36Sopenharmony_ci * 142162306a36Sopenharmony_ci * Return: the number of bytes processed if the domain ID is valid; otherwise, 142262306a36Sopenharmony_ci * returns one of the following errors: 142362306a36Sopenharmony_ci * -EINVAL if the ID is not a number 142462306a36Sopenharmony_ci * -ENODEV if the ID exceeds the maximum value configured for the system 142562306a36Sopenharmony_ci */ 142662306a36Sopenharmony_cistatic ssize_t unassign_control_domain_store(struct device *dev, 142762306a36Sopenharmony_ci struct device_attribute *attr, 142862306a36Sopenharmony_ci const char *buf, size_t count) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci int ret; 143162306a36Sopenharmony_ci unsigned long domid; 143262306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci get_update_locks_for_mdev(matrix_mdev); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &domid); 143762306a36Sopenharmony_ci if (ret) 143862306a36Sopenharmony_ci goto done; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci if (domid > matrix_mdev->matrix.adm_max) { 144162306a36Sopenharmony_ci ret = -ENODEV; 144262306a36Sopenharmony_ci goto done; 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci if (!test_bit_inv(domid, matrix_mdev->matrix.adm)) { 144662306a36Sopenharmony_ci ret = count; 144762306a36Sopenharmony_ci goto done; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci clear_bit_inv(domid, matrix_mdev->matrix.adm); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (test_bit_inv(domid, matrix_mdev->shadow_apcb.adm)) { 145362306a36Sopenharmony_ci clear_bit_inv(domid, matrix_mdev->shadow_apcb.adm); 145462306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci ret = count; 145862306a36Sopenharmony_cidone: 145962306a36Sopenharmony_ci release_update_locks_for_mdev(matrix_mdev); 146062306a36Sopenharmony_ci return ret; 146162306a36Sopenharmony_ci} 146262306a36Sopenharmony_cistatic DEVICE_ATTR_WO(unassign_control_domain); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_cistatic ssize_t control_domains_show(struct device *dev, 146562306a36Sopenharmony_ci struct device_attribute *dev_attr, 146662306a36Sopenharmony_ci char *buf) 146762306a36Sopenharmony_ci{ 146862306a36Sopenharmony_ci unsigned long id; 146962306a36Sopenharmony_ci int nchars = 0; 147062306a36Sopenharmony_ci int n; 147162306a36Sopenharmony_ci char *bufpos = buf; 147262306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); 147362306a36Sopenharmony_ci unsigned long max_domid = matrix_mdev->matrix.adm_max; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 147662306a36Sopenharmony_ci for_each_set_bit_inv(id, matrix_mdev->matrix.adm, max_domid + 1) { 147762306a36Sopenharmony_ci n = sprintf(bufpos, "%04lx\n", id); 147862306a36Sopenharmony_ci bufpos += n; 147962306a36Sopenharmony_ci nchars += n; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci return nchars; 148462306a36Sopenharmony_ci} 148562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(control_domains); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_cistatic ssize_t vfio_ap_mdev_matrix_show(struct ap_matrix *matrix, char *buf) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci char *bufpos = buf; 149062306a36Sopenharmony_ci unsigned long apid; 149162306a36Sopenharmony_ci unsigned long apqi; 149262306a36Sopenharmony_ci unsigned long apid1; 149362306a36Sopenharmony_ci unsigned long apqi1; 149462306a36Sopenharmony_ci unsigned long napm_bits = matrix->apm_max + 1; 149562306a36Sopenharmony_ci unsigned long naqm_bits = matrix->aqm_max + 1; 149662306a36Sopenharmony_ci int nchars = 0; 149762306a36Sopenharmony_ci int n; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci apid1 = find_first_bit_inv(matrix->apm, napm_bits); 150062306a36Sopenharmony_ci apqi1 = find_first_bit_inv(matrix->aqm, naqm_bits); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci if ((apid1 < napm_bits) && (apqi1 < naqm_bits)) { 150362306a36Sopenharmony_ci for_each_set_bit_inv(apid, matrix->apm, napm_bits) { 150462306a36Sopenharmony_ci for_each_set_bit_inv(apqi, matrix->aqm, 150562306a36Sopenharmony_ci naqm_bits) { 150662306a36Sopenharmony_ci n = sprintf(bufpos, "%02lx.%04lx\n", apid, 150762306a36Sopenharmony_ci apqi); 150862306a36Sopenharmony_ci bufpos += n; 150962306a36Sopenharmony_ci nchars += n; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci } else if (apid1 < napm_bits) { 151362306a36Sopenharmony_ci for_each_set_bit_inv(apid, matrix->apm, napm_bits) { 151462306a36Sopenharmony_ci n = sprintf(bufpos, "%02lx.\n", apid); 151562306a36Sopenharmony_ci bufpos += n; 151662306a36Sopenharmony_ci nchars += n; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci } else if (apqi1 < naqm_bits) { 151962306a36Sopenharmony_ci for_each_set_bit_inv(apqi, matrix->aqm, naqm_bits) { 152062306a36Sopenharmony_ci n = sprintf(bufpos, ".%04lx\n", apqi); 152162306a36Sopenharmony_ci bufpos += n; 152262306a36Sopenharmony_ci nchars += n; 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci return nchars; 152762306a36Sopenharmony_ci} 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_cistatic ssize_t matrix_show(struct device *dev, struct device_attribute *attr, 153062306a36Sopenharmony_ci char *buf) 153162306a36Sopenharmony_ci{ 153262306a36Sopenharmony_ci ssize_t nchars; 153362306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 153662306a36Sopenharmony_ci nchars = vfio_ap_mdev_matrix_show(&matrix_mdev->matrix, buf); 153762306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci return nchars; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(matrix); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_cistatic ssize_t guest_matrix_show(struct device *dev, 154462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 154562306a36Sopenharmony_ci{ 154662306a36Sopenharmony_ci ssize_t nchars; 154762306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 155062306a36Sopenharmony_ci nchars = vfio_ap_mdev_matrix_show(&matrix_mdev->shadow_apcb, buf); 155162306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci return nchars; 155462306a36Sopenharmony_ci} 155562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(guest_matrix); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cistatic struct attribute *vfio_ap_mdev_attrs[] = { 155862306a36Sopenharmony_ci &dev_attr_assign_adapter.attr, 155962306a36Sopenharmony_ci &dev_attr_unassign_adapter.attr, 156062306a36Sopenharmony_ci &dev_attr_assign_domain.attr, 156162306a36Sopenharmony_ci &dev_attr_unassign_domain.attr, 156262306a36Sopenharmony_ci &dev_attr_assign_control_domain.attr, 156362306a36Sopenharmony_ci &dev_attr_unassign_control_domain.attr, 156462306a36Sopenharmony_ci &dev_attr_control_domains.attr, 156562306a36Sopenharmony_ci &dev_attr_matrix.attr, 156662306a36Sopenharmony_ci &dev_attr_guest_matrix.attr, 156762306a36Sopenharmony_ci NULL, 156862306a36Sopenharmony_ci}; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_cistatic struct attribute_group vfio_ap_mdev_attr_group = { 157162306a36Sopenharmony_ci .attrs = vfio_ap_mdev_attrs 157262306a36Sopenharmony_ci}; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_cistatic const struct attribute_group *vfio_ap_mdev_attr_groups[] = { 157562306a36Sopenharmony_ci &vfio_ap_mdev_attr_group, 157662306a36Sopenharmony_ci NULL 157762306a36Sopenharmony_ci}; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci/** 158062306a36Sopenharmony_ci * vfio_ap_mdev_set_kvm - sets all data for @matrix_mdev that are needed 158162306a36Sopenharmony_ci * to manage AP resources for the guest whose state is represented by @kvm 158262306a36Sopenharmony_ci * 158362306a36Sopenharmony_ci * @matrix_mdev: a mediated matrix device 158462306a36Sopenharmony_ci * @kvm: reference to KVM instance 158562306a36Sopenharmony_ci * 158662306a36Sopenharmony_ci * Return: 0 if no other mediated matrix device has a reference to @kvm; 158762306a36Sopenharmony_ci * otherwise, returns an -EPERM. 158862306a36Sopenharmony_ci */ 158962306a36Sopenharmony_cistatic int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev, 159062306a36Sopenharmony_ci struct kvm *kvm) 159162306a36Sopenharmony_ci{ 159262306a36Sopenharmony_ci struct ap_matrix_mdev *m; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (kvm->arch.crypto.crycbd) { 159562306a36Sopenharmony_ci down_write(&kvm->arch.crypto.pqap_hook_rwsem); 159662306a36Sopenharmony_ci kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook; 159762306a36Sopenharmony_ci up_write(&kvm->arch.crypto.pqap_hook_rwsem); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci get_update_locks_for_kvm(kvm); 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci list_for_each_entry(m, &matrix_dev->mdev_list, node) { 160262306a36Sopenharmony_ci if (m != matrix_mdev && m->kvm == kvm) { 160362306a36Sopenharmony_ci release_update_locks_for_kvm(kvm); 160462306a36Sopenharmony_ci return -EPERM; 160562306a36Sopenharmony_ci } 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci kvm_get_kvm(kvm); 160962306a36Sopenharmony_ci matrix_mdev->kvm = kvm; 161062306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci release_update_locks_for_kvm(kvm); 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci return 0; 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_cistatic void unmap_iova(struct ap_matrix_mdev *matrix_mdev, u64 iova, u64 length) 161962306a36Sopenharmony_ci{ 162062306a36Sopenharmony_ci struct ap_queue_table *qtable = &matrix_mdev->qtable; 162162306a36Sopenharmony_ci struct vfio_ap_queue *q; 162262306a36Sopenharmony_ci int loop_cursor; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode) { 162562306a36Sopenharmony_ci if (q->saved_iova >= iova && q->saved_iova < iova + length) 162662306a36Sopenharmony_ci vfio_ap_irq_disable(q); 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci} 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_cistatic void vfio_ap_mdev_dma_unmap(struct vfio_device *vdev, u64 iova, 163162306a36Sopenharmony_ci u64 length) 163262306a36Sopenharmony_ci{ 163362306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = 163462306a36Sopenharmony_ci container_of(vdev, struct ap_matrix_mdev, vdev); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci unmap_iova(matrix_mdev, iova, length); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci/** 164462306a36Sopenharmony_ci * vfio_ap_mdev_unset_kvm - performs clean-up of resources no longer needed 164562306a36Sopenharmony_ci * by @matrix_mdev. 164662306a36Sopenharmony_ci * 164762306a36Sopenharmony_ci * @matrix_mdev: a matrix mediated device 164862306a36Sopenharmony_ci */ 164962306a36Sopenharmony_cistatic void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev) 165062306a36Sopenharmony_ci{ 165162306a36Sopenharmony_ci struct kvm *kvm = matrix_mdev->kvm; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci if (kvm && kvm->arch.crypto.crycbd) { 165462306a36Sopenharmony_ci down_write(&kvm->arch.crypto.pqap_hook_rwsem); 165562306a36Sopenharmony_ci kvm->arch.crypto.pqap_hook = NULL; 165662306a36Sopenharmony_ci up_write(&kvm->arch.crypto.pqap_hook_rwsem); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci get_update_locks_for_kvm(kvm); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci kvm_arch_crypto_clear_masks(kvm); 166162306a36Sopenharmony_ci vfio_ap_mdev_reset_queues(matrix_mdev); 166262306a36Sopenharmony_ci kvm_put_kvm(kvm); 166362306a36Sopenharmony_ci matrix_mdev->kvm = NULL; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci release_update_locks_for_kvm(kvm); 166662306a36Sopenharmony_ci } 166762306a36Sopenharmony_ci} 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cistatic struct vfio_ap_queue *vfio_ap_find_queue(int apqn) 167062306a36Sopenharmony_ci{ 167162306a36Sopenharmony_ci struct ap_queue *queue; 167262306a36Sopenharmony_ci struct vfio_ap_queue *q = NULL; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci queue = ap_get_qdev(apqn); 167562306a36Sopenharmony_ci if (!queue) 167662306a36Sopenharmony_ci return NULL; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (queue->ap_dev.device.driver == &matrix_dev->vfio_ap_drv->driver) 167962306a36Sopenharmony_ci q = dev_get_drvdata(&queue->ap_dev.device); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci put_device(&queue->ap_dev.device); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci return q; 168462306a36Sopenharmony_ci} 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_cistatic int apq_status_check(int apqn, struct ap_queue_status *status) 168762306a36Sopenharmony_ci{ 168862306a36Sopenharmony_ci switch (status->response_code) { 168962306a36Sopenharmony_ci case AP_RESPONSE_NORMAL: 169062306a36Sopenharmony_ci case AP_RESPONSE_DECONFIGURED: 169162306a36Sopenharmony_ci return 0; 169262306a36Sopenharmony_ci case AP_RESPONSE_RESET_IN_PROGRESS: 169362306a36Sopenharmony_ci case AP_RESPONSE_BUSY: 169462306a36Sopenharmony_ci return -EBUSY; 169562306a36Sopenharmony_ci case AP_RESPONSE_ASSOC_SECRET_NOT_UNIQUE: 169662306a36Sopenharmony_ci case AP_RESPONSE_ASSOC_FAILED: 169762306a36Sopenharmony_ci /* 169862306a36Sopenharmony_ci * These asynchronous response codes indicate a PQAP(AAPQ) 169962306a36Sopenharmony_ci * instruction to associate a secret with the guest failed. All 170062306a36Sopenharmony_ci * subsequent AP instructions will end with the asynchronous 170162306a36Sopenharmony_ci * response code until the AP queue is reset; so, let's return 170262306a36Sopenharmony_ci * a value indicating a reset needs to be performed again. 170362306a36Sopenharmony_ci */ 170462306a36Sopenharmony_ci return -EAGAIN; 170562306a36Sopenharmony_ci default: 170662306a36Sopenharmony_ci WARN(true, 170762306a36Sopenharmony_ci "failed to verify reset of queue %02x.%04x: TAPQ rc=%u\n", 170862306a36Sopenharmony_ci AP_QID_CARD(apqn), AP_QID_QUEUE(apqn), 170962306a36Sopenharmony_ci status->response_code); 171062306a36Sopenharmony_ci return -EIO; 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci} 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci#define WAIT_MSG "Waited %dms for reset of queue %02x.%04x (%u, %u, %u)" 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_cistatic void apq_reset_check(struct work_struct *reset_work) 171762306a36Sopenharmony_ci{ 171862306a36Sopenharmony_ci int ret = -EBUSY, elapsed = 0; 171962306a36Sopenharmony_ci struct ap_queue_status status; 172062306a36Sopenharmony_ci struct vfio_ap_queue *q; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci q = container_of(reset_work, struct vfio_ap_queue, reset_work); 172362306a36Sopenharmony_ci memcpy(&status, &q->reset_status, sizeof(status)); 172462306a36Sopenharmony_ci while (true) { 172562306a36Sopenharmony_ci msleep(AP_RESET_INTERVAL); 172662306a36Sopenharmony_ci elapsed += AP_RESET_INTERVAL; 172762306a36Sopenharmony_ci status = ap_tapq(q->apqn, NULL); 172862306a36Sopenharmony_ci ret = apq_status_check(q->apqn, &status); 172962306a36Sopenharmony_ci if (ret == -EIO) 173062306a36Sopenharmony_ci return; 173162306a36Sopenharmony_ci if (ret == -EBUSY) { 173262306a36Sopenharmony_ci pr_notice_ratelimited(WAIT_MSG, elapsed, 173362306a36Sopenharmony_ci AP_QID_CARD(q->apqn), 173462306a36Sopenharmony_ci AP_QID_QUEUE(q->apqn), 173562306a36Sopenharmony_ci status.response_code, 173662306a36Sopenharmony_ci status.queue_empty, 173762306a36Sopenharmony_ci status.irq_enabled); 173862306a36Sopenharmony_ci } else { 173962306a36Sopenharmony_ci if (q->reset_status.response_code == AP_RESPONSE_RESET_IN_PROGRESS || 174062306a36Sopenharmony_ci q->reset_status.response_code == AP_RESPONSE_BUSY || 174162306a36Sopenharmony_ci q->reset_status.response_code == AP_RESPONSE_STATE_CHANGE_IN_PROGRESS || 174262306a36Sopenharmony_ci ret == -EAGAIN) { 174362306a36Sopenharmony_ci status = ap_zapq(q->apqn, 0); 174462306a36Sopenharmony_ci memcpy(&q->reset_status, &status, sizeof(status)); 174562306a36Sopenharmony_ci continue; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci /* 174862306a36Sopenharmony_ci * When an AP adapter is deconfigured, the 174962306a36Sopenharmony_ci * associated queues are reset, so let's set the 175062306a36Sopenharmony_ci * status response code to 0 so the queue may be 175162306a36Sopenharmony_ci * passed through (i.e., not filtered) 175262306a36Sopenharmony_ci */ 175362306a36Sopenharmony_ci if (status.response_code == AP_RESPONSE_DECONFIGURED) 175462306a36Sopenharmony_ci q->reset_status.response_code = 0; 175562306a36Sopenharmony_ci if (q->saved_isc != VFIO_AP_ISC_INVALID) 175662306a36Sopenharmony_ci vfio_ap_free_aqic_resources(q); 175762306a36Sopenharmony_ci break; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci} 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_cistatic void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q) 176362306a36Sopenharmony_ci{ 176462306a36Sopenharmony_ci struct ap_queue_status status; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci if (!q) 176762306a36Sopenharmony_ci return; 176862306a36Sopenharmony_ci status = ap_zapq(q->apqn, 0); 176962306a36Sopenharmony_ci memcpy(&q->reset_status, &status, sizeof(status)); 177062306a36Sopenharmony_ci switch (status.response_code) { 177162306a36Sopenharmony_ci case AP_RESPONSE_NORMAL: 177262306a36Sopenharmony_ci case AP_RESPONSE_RESET_IN_PROGRESS: 177362306a36Sopenharmony_ci case AP_RESPONSE_BUSY: 177462306a36Sopenharmony_ci case AP_RESPONSE_STATE_CHANGE_IN_PROGRESS: 177562306a36Sopenharmony_ci /* 177662306a36Sopenharmony_ci * Let's verify whether the ZAPQ completed successfully on a work queue. 177762306a36Sopenharmony_ci */ 177862306a36Sopenharmony_ci queue_work(system_long_wq, &q->reset_work); 177962306a36Sopenharmony_ci break; 178062306a36Sopenharmony_ci case AP_RESPONSE_DECONFIGURED: 178162306a36Sopenharmony_ci /* 178262306a36Sopenharmony_ci * When an AP adapter is deconfigured, the associated 178362306a36Sopenharmony_ci * queues are reset, so let's set the status response code to 0 178462306a36Sopenharmony_ci * so the queue may be passed through (i.e., not filtered). 178562306a36Sopenharmony_ci */ 178662306a36Sopenharmony_ci q->reset_status.response_code = 0; 178762306a36Sopenharmony_ci vfio_ap_free_aqic_resources(q); 178862306a36Sopenharmony_ci break; 178962306a36Sopenharmony_ci default: 179062306a36Sopenharmony_ci WARN(true, 179162306a36Sopenharmony_ci "PQAP/ZAPQ for %02x.%04x failed with invalid rc=%u\n", 179262306a36Sopenharmony_ci AP_QID_CARD(q->apqn), AP_QID_QUEUE(q->apqn), 179362306a36Sopenharmony_ci status.response_code); 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci} 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_cistatic int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev) 179862306a36Sopenharmony_ci{ 179962306a36Sopenharmony_ci int ret = 0, loop_cursor; 180062306a36Sopenharmony_ci struct vfio_ap_queue *q; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci hash_for_each(matrix_mdev->qtable.queues, loop_cursor, q, mdev_qnode) 180362306a36Sopenharmony_ci vfio_ap_mdev_reset_queue(q); 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci hash_for_each(matrix_mdev->qtable.queues, loop_cursor, q, mdev_qnode) { 180662306a36Sopenharmony_ci flush_work(&q->reset_work); 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci if (q->reset_status.response_code) 180962306a36Sopenharmony_ci ret = -EIO; 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci return ret; 181362306a36Sopenharmony_ci} 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_cistatic int vfio_ap_mdev_reset_qlist(struct list_head *qlist) 181662306a36Sopenharmony_ci{ 181762306a36Sopenharmony_ci int ret = 0; 181862306a36Sopenharmony_ci struct vfio_ap_queue *q; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci list_for_each_entry(q, qlist, reset_qnode) 182162306a36Sopenharmony_ci vfio_ap_mdev_reset_queue(q); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci list_for_each_entry(q, qlist, reset_qnode) { 182462306a36Sopenharmony_ci flush_work(&q->reset_work); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci if (q->reset_status.response_code) 182762306a36Sopenharmony_ci ret = -EIO; 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci return ret; 183162306a36Sopenharmony_ci} 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_cistatic int vfio_ap_mdev_open_device(struct vfio_device *vdev) 183462306a36Sopenharmony_ci{ 183562306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = 183662306a36Sopenharmony_ci container_of(vdev, struct ap_matrix_mdev, vdev); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci if (!vdev->kvm) 183962306a36Sopenharmony_ci return -EINVAL; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci return vfio_ap_mdev_set_kvm(matrix_mdev, vdev->kvm); 184262306a36Sopenharmony_ci} 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_cistatic void vfio_ap_mdev_close_device(struct vfio_device *vdev) 184562306a36Sopenharmony_ci{ 184662306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = 184762306a36Sopenharmony_ci container_of(vdev, struct ap_matrix_mdev, vdev); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci vfio_ap_mdev_unset_kvm(matrix_mdev); 185062306a36Sopenharmony_ci} 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_cistatic void vfio_ap_mdev_request(struct vfio_device *vdev, unsigned int count) 185362306a36Sopenharmony_ci{ 185462306a36Sopenharmony_ci struct device *dev = vdev->dev; 185562306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev); 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci if (matrix_mdev->req_trigger) { 186062306a36Sopenharmony_ci if (!(count % 10)) 186162306a36Sopenharmony_ci dev_notice_ratelimited(dev, 186262306a36Sopenharmony_ci "Relaying device request to user (#%u)\n", 186362306a36Sopenharmony_ci count); 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci eventfd_signal(matrix_mdev->req_trigger, 1); 186662306a36Sopenharmony_ci } else if (count == 0) { 186762306a36Sopenharmony_ci dev_notice(dev, 186862306a36Sopenharmony_ci "No device request registered, blocked until released by user\n"); 186962306a36Sopenharmony_ci } 187062306a36Sopenharmony_ci} 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cistatic int vfio_ap_mdev_get_device_info(unsigned long arg) 187362306a36Sopenharmony_ci{ 187462306a36Sopenharmony_ci unsigned long minsz; 187562306a36Sopenharmony_ci struct vfio_device_info info; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci minsz = offsetofend(struct vfio_device_info, num_irqs); 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 188062306a36Sopenharmony_ci return -EFAULT; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci if (info.argsz < minsz) 188362306a36Sopenharmony_ci return -EINVAL; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci info.flags = VFIO_DEVICE_FLAGS_AP | VFIO_DEVICE_FLAGS_RESET; 188662306a36Sopenharmony_ci info.num_regions = 0; 188762306a36Sopenharmony_ci info.num_irqs = VFIO_AP_NUM_IRQS; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; 189062306a36Sopenharmony_ci} 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_cistatic ssize_t vfio_ap_get_irq_info(unsigned long arg) 189362306a36Sopenharmony_ci{ 189462306a36Sopenharmony_ci unsigned long minsz; 189562306a36Sopenharmony_ci struct vfio_irq_info info; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci minsz = offsetofend(struct vfio_irq_info, count); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 190062306a36Sopenharmony_ci return -EFAULT; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci if (info.argsz < minsz || info.index >= VFIO_AP_NUM_IRQS) 190362306a36Sopenharmony_ci return -EINVAL; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci switch (info.index) { 190662306a36Sopenharmony_ci case VFIO_AP_REQ_IRQ_INDEX: 190762306a36Sopenharmony_ci info.count = 1; 190862306a36Sopenharmony_ci info.flags = VFIO_IRQ_INFO_EVENTFD; 190962306a36Sopenharmony_ci break; 191062306a36Sopenharmony_ci default: 191162306a36Sopenharmony_ci return -EINVAL; 191262306a36Sopenharmony_ci } 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; 191562306a36Sopenharmony_ci} 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_cistatic int vfio_ap_irq_set_init(struct vfio_irq_set *irq_set, unsigned long arg) 191862306a36Sopenharmony_ci{ 191962306a36Sopenharmony_ci int ret; 192062306a36Sopenharmony_ci size_t data_size; 192162306a36Sopenharmony_ci unsigned long minsz; 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci minsz = offsetofend(struct vfio_irq_set, count); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci if (copy_from_user(irq_set, (void __user *)arg, minsz)) 192662306a36Sopenharmony_ci return -EFAULT; 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci ret = vfio_set_irqs_validate_and_prepare(irq_set, 1, VFIO_AP_NUM_IRQS, 192962306a36Sopenharmony_ci &data_size); 193062306a36Sopenharmony_ci if (ret) 193162306a36Sopenharmony_ci return ret; 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci if (!(irq_set->flags & VFIO_IRQ_SET_ACTION_TRIGGER)) 193462306a36Sopenharmony_ci return -EINVAL; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci return 0; 193762306a36Sopenharmony_ci} 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_cistatic int vfio_ap_set_request_irq(struct ap_matrix_mdev *matrix_mdev, 194062306a36Sopenharmony_ci unsigned long arg) 194162306a36Sopenharmony_ci{ 194262306a36Sopenharmony_ci s32 fd; 194362306a36Sopenharmony_ci void __user *data; 194462306a36Sopenharmony_ci unsigned long minsz; 194562306a36Sopenharmony_ci struct eventfd_ctx *req_trigger; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci minsz = offsetofend(struct vfio_irq_set, count); 194862306a36Sopenharmony_ci data = (void __user *)(arg + minsz); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci if (get_user(fd, (s32 __user *)data)) 195162306a36Sopenharmony_ci return -EFAULT; 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci if (fd == -1) { 195462306a36Sopenharmony_ci if (matrix_mdev->req_trigger) 195562306a36Sopenharmony_ci eventfd_ctx_put(matrix_mdev->req_trigger); 195662306a36Sopenharmony_ci matrix_mdev->req_trigger = NULL; 195762306a36Sopenharmony_ci } else if (fd >= 0) { 195862306a36Sopenharmony_ci req_trigger = eventfd_ctx_fdget(fd); 195962306a36Sopenharmony_ci if (IS_ERR(req_trigger)) 196062306a36Sopenharmony_ci return PTR_ERR(req_trigger); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci if (matrix_mdev->req_trigger) 196362306a36Sopenharmony_ci eventfd_ctx_put(matrix_mdev->req_trigger); 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci matrix_mdev->req_trigger = req_trigger; 196662306a36Sopenharmony_ci } else { 196762306a36Sopenharmony_ci return -EINVAL; 196862306a36Sopenharmony_ci } 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci return 0; 197162306a36Sopenharmony_ci} 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_cistatic int vfio_ap_set_irqs(struct ap_matrix_mdev *matrix_mdev, 197462306a36Sopenharmony_ci unsigned long arg) 197562306a36Sopenharmony_ci{ 197662306a36Sopenharmony_ci int ret; 197762306a36Sopenharmony_ci struct vfio_irq_set irq_set; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci ret = vfio_ap_irq_set_init(&irq_set, arg); 198062306a36Sopenharmony_ci if (ret) 198162306a36Sopenharmony_ci return ret; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci switch (irq_set.flags & VFIO_IRQ_SET_DATA_TYPE_MASK) { 198462306a36Sopenharmony_ci case VFIO_IRQ_SET_DATA_EVENTFD: 198562306a36Sopenharmony_ci switch (irq_set.index) { 198662306a36Sopenharmony_ci case VFIO_AP_REQ_IRQ_INDEX: 198762306a36Sopenharmony_ci return vfio_ap_set_request_irq(matrix_mdev, arg); 198862306a36Sopenharmony_ci default: 198962306a36Sopenharmony_ci return -EINVAL; 199062306a36Sopenharmony_ci } 199162306a36Sopenharmony_ci default: 199262306a36Sopenharmony_ci return -EINVAL; 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci} 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_cistatic ssize_t vfio_ap_mdev_ioctl(struct vfio_device *vdev, 199762306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 199862306a36Sopenharmony_ci{ 199962306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev = 200062306a36Sopenharmony_ci container_of(vdev, struct ap_matrix_mdev, vdev); 200162306a36Sopenharmony_ci int ret; 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 200462306a36Sopenharmony_ci switch (cmd) { 200562306a36Sopenharmony_ci case VFIO_DEVICE_GET_INFO: 200662306a36Sopenharmony_ci ret = vfio_ap_mdev_get_device_info(arg); 200762306a36Sopenharmony_ci break; 200862306a36Sopenharmony_ci case VFIO_DEVICE_RESET: 200962306a36Sopenharmony_ci ret = vfio_ap_mdev_reset_queues(matrix_mdev); 201062306a36Sopenharmony_ci break; 201162306a36Sopenharmony_ci case VFIO_DEVICE_GET_IRQ_INFO: 201262306a36Sopenharmony_ci ret = vfio_ap_get_irq_info(arg); 201362306a36Sopenharmony_ci break; 201462306a36Sopenharmony_ci case VFIO_DEVICE_SET_IRQS: 201562306a36Sopenharmony_ci ret = vfio_ap_set_irqs(matrix_mdev, arg); 201662306a36Sopenharmony_ci break; 201762306a36Sopenharmony_ci default: 201862306a36Sopenharmony_ci ret = -EOPNOTSUPP; 201962306a36Sopenharmony_ci break; 202062306a36Sopenharmony_ci } 202162306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci return ret; 202462306a36Sopenharmony_ci} 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_cistatic struct ap_matrix_mdev *vfio_ap_mdev_for_queue(struct vfio_ap_queue *q) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 202962306a36Sopenharmony_ci unsigned long apid = AP_QID_CARD(q->apqn); 203062306a36Sopenharmony_ci unsigned long apqi = AP_QID_QUEUE(q->apqn); 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { 203362306a36Sopenharmony_ci if (test_bit_inv(apid, matrix_mdev->matrix.apm) && 203462306a36Sopenharmony_ci test_bit_inv(apqi, matrix_mdev->matrix.aqm)) 203562306a36Sopenharmony_ci return matrix_mdev; 203662306a36Sopenharmony_ci } 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci return NULL; 203962306a36Sopenharmony_ci} 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_cistatic ssize_t status_show(struct device *dev, 204262306a36Sopenharmony_ci struct device_attribute *attr, 204362306a36Sopenharmony_ci char *buf) 204462306a36Sopenharmony_ci{ 204562306a36Sopenharmony_ci ssize_t nchars = 0; 204662306a36Sopenharmony_ci struct vfio_ap_queue *q; 204762306a36Sopenharmony_ci unsigned long apid, apqi; 204862306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 204962306a36Sopenharmony_ci struct ap_device *apdev = to_ap_dev(dev); 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 205262306a36Sopenharmony_ci q = dev_get_drvdata(&apdev->device); 205362306a36Sopenharmony_ci matrix_mdev = vfio_ap_mdev_for_queue(q); 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci /* If the queue is assigned to the matrix mediated device, then 205662306a36Sopenharmony_ci * determine whether it is passed through to a guest; otherwise, 205762306a36Sopenharmony_ci * indicate that it is unassigned. 205862306a36Sopenharmony_ci */ 205962306a36Sopenharmony_ci if (matrix_mdev) { 206062306a36Sopenharmony_ci apid = AP_QID_CARD(q->apqn); 206162306a36Sopenharmony_ci apqi = AP_QID_QUEUE(q->apqn); 206262306a36Sopenharmony_ci /* 206362306a36Sopenharmony_ci * If the queue is passed through to the guest, then indicate 206462306a36Sopenharmony_ci * that it is in use; otherwise, indicate that it is 206562306a36Sopenharmony_ci * merely assigned to a matrix mediated device. 206662306a36Sopenharmony_ci */ 206762306a36Sopenharmony_ci if (matrix_mdev->kvm && 206862306a36Sopenharmony_ci test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) && 206962306a36Sopenharmony_ci test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) 207062306a36Sopenharmony_ci nchars = scnprintf(buf, PAGE_SIZE, "%s\n", 207162306a36Sopenharmony_ci AP_QUEUE_IN_USE); 207262306a36Sopenharmony_ci else 207362306a36Sopenharmony_ci nchars = scnprintf(buf, PAGE_SIZE, "%s\n", 207462306a36Sopenharmony_ci AP_QUEUE_ASSIGNED); 207562306a36Sopenharmony_ci } else { 207662306a36Sopenharmony_ci nchars = scnprintf(buf, PAGE_SIZE, "%s\n", 207762306a36Sopenharmony_ci AP_QUEUE_UNASSIGNED); 207862306a36Sopenharmony_ci } 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci return nchars; 208362306a36Sopenharmony_ci} 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(status); 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_cistatic struct attribute *vfio_queue_attrs[] = { 208862306a36Sopenharmony_ci &dev_attr_status.attr, 208962306a36Sopenharmony_ci NULL, 209062306a36Sopenharmony_ci}; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_cistatic const struct attribute_group vfio_queue_attr_group = { 209362306a36Sopenharmony_ci .attrs = vfio_queue_attrs, 209462306a36Sopenharmony_ci}; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_cistatic const struct vfio_device_ops vfio_ap_matrix_dev_ops = { 209762306a36Sopenharmony_ci .init = vfio_ap_mdev_init_dev, 209862306a36Sopenharmony_ci .open_device = vfio_ap_mdev_open_device, 209962306a36Sopenharmony_ci .close_device = vfio_ap_mdev_close_device, 210062306a36Sopenharmony_ci .ioctl = vfio_ap_mdev_ioctl, 210162306a36Sopenharmony_ci .dma_unmap = vfio_ap_mdev_dma_unmap, 210262306a36Sopenharmony_ci .bind_iommufd = vfio_iommufd_emulated_bind, 210362306a36Sopenharmony_ci .unbind_iommufd = vfio_iommufd_emulated_unbind, 210462306a36Sopenharmony_ci .attach_ioas = vfio_iommufd_emulated_attach_ioas, 210562306a36Sopenharmony_ci .detach_ioas = vfio_iommufd_emulated_detach_ioas, 210662306a36Sopenharmony_ci .request = vfio_ap_mdev_request 210762306a36Sopenharmony_ci}; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_cistatic struct mdev_driver vfio_ap_matrix_driver = { 211062306a36Sopenharmony_ci .device_api = VFIO_DEVICE_API_AP_STRING, 211162306a36Sopenharmony_ci .max_instances = MAX_ZDEV_ENTRIES_EXT, 211262306a36Sopenharmony_ci .driver = { 211362306a36Sopenharmony_ci .name = "vfio_ap_mdev", 211462306a36Sopenharmony_ci .owner = THIS_MODULE, 211562306a36Sopenharmony_ci .mod_name = KBUILD_MODNAME, 211662306a36Sopenharmony_ci .dev_groups = vfio_ap_mdev_attr_groups, 211762306a36Sopenharmony_ci }, 211862306a36Sopenharmony_ci .probe = vfio_ap_mdev_probe, 211962306a36Sopenharmony_ci .remove = vfio_ap_mdev_remove, 212062306a36Sopenharmony_ci}; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ciint vfio_ap_mdev_register(void) 212362306a36Sopenharmony_ci{ 212462306a36Sopenharmony_ci int ret; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci ret = mdev_register_driver(&vfio_ap_matrix_driver); 212762306a36Sopenharmony_ci if (ret) 212862306a36Sopenharmony_ci return ret; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci matrix_dev->mdev_type.sysfs_name = VFIO_AP_MDEV_TYPE_HWVIRT; 213162306a36Sopenharmony_ci matrix_dev->mdev_type.pretty_name = VFIO_AP_MDEV_NAME_HWVIRT; 213262306a36Sopenharmony_ci matrix_dev->mdev_types[0] = &matrix_dev->mdev_type; 213362306a36Sopenharmony_ci ret = mdev_register_parent(&matrix_dev->parent, &matrix_dev->device, 213462306a36Sopenharmony_ci &vfio_ap_matrix_driver, 213562306a36Sopenharmony_ci matrix_dev->mdev_types, 1); 213662306a36Sopenharmony_ci if (ret) 213762306a36Sopenharmony_ci goto err_driver; 213862306a36Sopenharmony_ci return 0; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_cierr_driver: 214162306a36Sopenharmony_ci mdev_unregister_driver(&vfio_ap_matrix_driver); 214262306a36Sopenharmony_ci return ret; 214362306a36Sopenharmony_ci} 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_civoid vfio_ap_mdev_unregister(void) 214662306a36Sopenharmony_ci{ 214762306a36Sopenharmony_ci mdev_unregister_parent(&matrix_dev->parent); 214862306a36Sopenharmony_ci mdev_unregister_driver(&vfio_ap_matrix_driver); 214962306a36Sopenharmony_ci} 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ciint vfio_ap_mdev_probe_queue(struct ap_device *apdev) 215262306a36Sopenharmony_ci{ 215362306a36Sopenharmony_ci int ret; 215462306a36Sopenharmony_ci struct vfio_ap_queue *q; 215562306a36Sopenharmony_ci DECLARE_BITMAP(apm_filtered, AP_DEVICES); 215662306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci ret = sysfs_create_group(&apdev->device.kobj, &vfio_queue_attr_group); 215962306a36Sopenharmony_ci if (ret) 216062306a36Sopenharmony_ci return ret; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci q = kzalloc(sizeof(*q), GFP_KERNEL); 216362306a36Sopenharmony_ci if (!q) { 216462306a36Sopenharmony_ci ret = -ENOMEM; 216562306a36Sopenharmony_ci goto err_remove_group; 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci q->apqn = to_ap_queue(&apdev->device)->qid; 216962306a36Sopenharmony_ci q->saved_isc = VFIO_AP_ISC_INVALID; 217062306a36Sopenharmony_ci memset(&q->reset_status, 0, sizeof(q->reset_status)); 217162306a36Sopenharmony_ci INIT_WORK(&q->reset_work, apq_reset_check); 217262306a36Sopenharmony_ci matrix_mdev = get_update_locks_by_apqn(q->apqn); 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci if (matrix_mdev) { 217562306a36Sopenharmony_ci vfio_ap_mdev_link_queue(matrix_mdev, q); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci /* 217862306a36Sopenharmony_ci * If we're in the process of handling the adding of adapters or 217962306a36Sopenharmony_ci * domains to the host's AP configuration, then let the 218062306a36Sopenharmony_ci * vfio_ap device driver's on_scan_complete callback filter the 218162306a36Sopenharmony_ci * matrix and update the guest's AP configuration after all of 218262306a36Sopenharmony_ci * the new queue devices are probed. 218362306a36Sopenharmony_ci */ 218462306a36Sopenharmony_ci if (!bitmap_empty(matrix_mdev->apm_add, AP_DEVICES) || 218562306a36Sopenharmony_ci !bitmap_empty(matrix_mdev->aqm_add, AP_DOMAINS)) 218662306a36Sopenharmony_ci goto done; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci if (vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered)) { 218962306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 219062306a36Sopenharmony_ci reset_queues_for_apids(matrix_mdev, apm_filtered); 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_cidone: 219562306a36Sopenharmony_ci dev_set_drvdata(&apdev->device, q); 219662306a36Sopenharmony_ci release_update_locks_for_mdev(matrix_mdev); 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci return ret; 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_cierr_remove_group: 220162306a36Sopenharmony_ci sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group); 220262306a36Sopenharmony_ci return ret; 220362306a36Sopenharmony_ci} 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_civoid vfio_ap_mdev_remove_queue(struct ap_device *apdev) 220662306a36Sopenharmony_ci{ 220762306a36Sopenharmony_ci unsigned long apid, apqi; 220862306a36Sopenharmony_ci struct vfio_ap_queue *q; 220962306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group); 221262306a36Sopenharmony_ci q = dev_get_drvdata(&apdev->device); 221362306a36Sopenharmony_ci get_update_locks_for_queue(q); 221462306a36Sopenharmony_ci matrix_mdev = q->matrix_mdev; 221562306a36Sopenharmony_ci apid = AP_QID_CARD(q->apqn); 221662306a36Sopenharmony_ci apqi = AP_QID_QUEUE(q->apqn); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci if (matrix_mdev) { 221962306a36Sopenharmony_ci /* If the queue is assigned to the guest's AP configuration */ 222062306a36Sopenharmony_ci if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) && 222162306a36Sopenharmony_ci test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) { 222262306a36Sopenharmony_ci /* 222362306a36Sopenharmony_ci * Since the queues are defined via a matrix of adapters 222462306a36Sopenharmony_ci * and domains, it is not possible to hot unplug a 222562306a36Sopenharmony_ci * single queue; so, let's unplug the adapter. 222662306a36Sopenharmony_ci */ 222762306a36Sopenharmony_ci clear_bit_inv(apid, matrix_mdev->shadow_apcb.apm); 222862306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 222962306a36Sopenharmony_ci reset_queues_for_apid(matrix_mdev, apid); 223062306a36Sopenharmony_ci goto done; 223162306a36Sopenharmony_ci } 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci /* 223562306a36Sopenharmony_ci * If the queue is not in the host's AP configuration, then resetting 223662306a36Sopenharmony_ci * it will fail with response code 01, (APQN not valid); so, let's make 223762306a36Sopenharmony_ci * sure it is in the host's config. 223862306a36Sopenharmony_ci */ 223962306a36Sopenharmony_ci if (test_bit_inv(apid, (unsigned long *)matrix_dev->info.apm) && 224062306a36Sopenharmony_ci test_bit_inv(apqi, (unsigned long *)matrix_dev->info.aqm)) { 224162306a36Sopenharmony_ci vfio_ap_mdev_reset_queue(q); 224262306a36Sopenharmony_ci flush_work(&q->reset_work); 224362306a36Sopenharmony_ci } 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_cidone: 224662306a36Sopenharmony_ci if (matrix_mdev) 224762306a36Sopenharmony_ci vfio_ap_unlink_queue_fr_mdev(q); 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci dev_set_drvdata(&apdev->device, NULL); 225062306a36Sopenharmony_ci kfree(q); 225162306a36Sopenharmony_ci release_update_locks_for_mdev(matrix_mdev); 225262306a36Sopenharmony_ci} 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci/** 225562306a36Sopenharmony_ci * vfio_ap_mdev_resource_in_use: check whether any of a set of APQNs is 225662306a36Sopenharmony_ci * assigned to a mediated device under the control 225762306a36Sopenharmony_ci * of the vfio_ap device driver. 225862306a36Sopenharmony_ci * 225962306a36Sopenharmony_ci * @apm: a bitmap specifying a set of APIDs comprising the APQNs to check. 226062306a36Sopenharmony_ci * @aqm: a bitmap specifying a set of APQIs comprising the APQNs to check. 226162306a36Sopenharmony_ci * 226262306a36Sopenharmony_ci * Return: 226362306a36Sopenharmony_ci * * -EADDRINUSE if one or more of the APQNs specified via @apm/@aqm are 226462306a36Sopenharmony_ci * assigned to a mediated device under the control of the vfio_ap 226562306a36Sopenharmony_ci * device driver. 226662306a36Sopenharmony_ci * * Otherwise, return 0. 226762306a36Sopenharmony_ci */ 226862306a36Sopenharmony_ciint vfio_ap_mdev_resource_in_use(unsigned long *apm, unsigned long *aqm) 226962306a36Sopenharmony_ci{ 227062306a36Sopenharmony_ci int ret; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci mutex_lock(&matrix_dev->guests_lock); 227362306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 227462306a36Sopenharmony_ci ret = vfio_ap_mdev_verify_no_sharing(apm, aqm); 227562306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 227662306a36Sopenharmony_ci mutex_unlock(&matrix_dev->guests_lock); 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci return ret; 227962306a36Sopenharmony_ci} 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci/** 228262306a36Sopenharmony_ci * vfio_ap_mdev_hot_unplug_cfg - hot unplug the adapters, domains and control 228362306a36Sopenharmony_ci * domains that have been removed from the host's 228462306a36Sopenharmony_ci * AP configuration from a guest. 228562306a36Sopenharmony_ci * 228662306a36Sopenharmony_ci * @matrix_mdev: an ap_matrix_mdev object attached to a KVM guest. 228762306a36Sopenharmony_ci * @aprem: the adapters that have been removed from the host's AP configuration 228862306a36Sopenharmony_ci * @aqrem: the domains that have been removed from the host's AP configuration 228962306a36Sopenharmony_ci * @cdrem: the control domains that have been removed from the host's AP 229062306a36Sopenharmony_ci * configuration. 229162306a36Sopenharmony_ci */ 229262306a36Sopenharmony_cistatic void vfio_ap_mdev_hot_unplug_cfg(struct ap_matrix_mdev *matrix_mdev, 229362306a36Sopenharmony_ci unsigned long *aprem, 229462306a36Sopenharmony_ci unsigned long *aqrem, 229562306a36Sopenharmony_ci unsigned long *cdrem) 229662306a36Sopenharmony_ci{ 229762306a36Sopenharmony_ci int do_hotplug = 0; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci if (!bitmap_empty(aprem, AP_DEVICES)) { 230062306a36Sopenharmony_ci do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.apm, 230162306a36Sopenharmony_ci matrix_mdev->shadow_apcb.apm, 230262306a36Sopenharmony_ci aprem, AP_DEVICES); 230362306a36Sopenharmony_ci } 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci if (!bitmap_empty(aqrem, AP_DOMAINS)) { 230662306a36Sopenharmony_ci do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.aqm, 230762306a36Sopenharmony_ci matrix_mdev->shadow_apcb.aqm, 230862306a36Sopenharmony_ci aqrem, AP_DEVICES); 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci if (!bitmap_empty(cdrem, AP_DOMAINS)) 231262306a36Sopenharmony_ci do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.adm, 231362306a36Sopenharmony_ci matrix_mdev->shadow_apcb.adm, 231462306a36Sopenharmony_ci cdrem, AP_DOMAINS); 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci if (do_hotplug) 231762306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 231862306a36Sopenharmony_ci} 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci/** 232162306a36Sopenharmony_ci * vfio_ap_mdev_cfg_remove - determines which guests are using the adapters, 232262306a36Sopenharmony_ci * domains and control domains that have been removed 232362306a36Sopenharmony_ci * from the host AP configuration and unplugs them 232462306a36Sopenharmony_ci * from those guests. 232562306a36Sopenharmony_ci * 232662306a36Sopenharmony_ci * @ap_remove: bitmap specifying which adapters have been removed from the host 232762306a36Sopenharmony_ci * config. 232862306a36Sopenharmony_ci * @aq_remove: bitmap specifying which domains have been removed from the host 232962306a36Sopenharmony_ci * config. 233062306a36Sopenharmony_ci * @cd_remove: bitmap specifying which control domains have been removed from 233162306a36Sopenharmony_ci * the host config. 233262306a36Sopenharmony_ci */ 233362306a36Sopenharmony_cistatic void vfio_ap_mdev_cfg_remove(unsigned long *ap_remove, 233462306a36Sopenharmony_ci unsigned long *aq_remove, 233562306a36Sopenharmony_ci unsigned long *cd_remove) 233662306a36Sopenharmony_ci{ 233762306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 233862306a36Sopenharmony_ci DECLARE_BITMAP(aprem, AP_DEVICES); 233962306a36Sopenharmony_ci DECLARE_BITMAP(aqrem, AP_DOMAINS); 234062306a36Sopenharmony_ci DECLARE_BITMAP(cdrem, AP_DOMAINS); 234162306a36Sopenharmony_ci int do_remove = 0; 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { 234462306a36Sopenharmony_ci mutex_lock(&matrix_mdev->kvm->lock); 234562306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci do_remove |= bitmap_and(aprem, ap_remove, 234862306a36Sopenharmony_ci matrix_mdev->matrix.apm, 234962306a36Sopenharmony_ci AP_DEVICES); 235062306a36Sopenharmony_ci do_remove |= bitmap_and(aqrem, aq_remove, 235162306a36Sopenharmony_ci matrix_mdev->matrix.aqm, 235262306a36Sopenharmony_ci AP_DOMAINS); 235362306a36Sopenharmony_ci do_remove |= bitmap_andnot(cdrem, cd_remove, 235462306a36Sopenharmony_ci matrix_mdev->matrix.adm, 235562306a36Sopenharmony_ci AP_DOMAINS); 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci if (do_remove) 235862306a36Sopenharmony_ci vfio_ap_mdev_hot_unplug_cfg(matrix_mdev, aprem, aqrem, 235962306a36Sopenharmony_ci cdrem); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 236262306a36Sopenharmony_ci mutex_unlock(&matrix_mdev->kvm->lock); 236362306a36Sopenharmony_ci } 236462306a36Sopenharmony_ci} 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci/** 236762306a36Sopenharmony_ci * vfio_ap_mdev_on_cfg_remove - responds to the removal of adapters, domains and 236862306a36Sopenharmony_ci * control domains from the host AP configuration 236962306a36Sopenharmony_ci * by unplugging them from the guests that are 237062306a36Sopenharmony_ci * using them. 237162306a36Sopenharmony_ci * @cur_config_info: the current host AP configuration information 237262306a36Sopenharmony_ci * @prev_config_info: the previous host AP configuration information 237362306a36Sopenharmony_ci */ 237462306a36Sopenharmony_cistatic void vfio_ap_mdev_on_cfg_remove(struct ap_config_info *cur_config_info, 237562306a36Sopenharmony_ci struct ap_config_info *prev_config_info) 237662306a36Sopenharmony_ci{ 237762306a36Sopenharmony_ci int do_remove; 237862306a36Sopenharmony_ci DECLARE_BITMAP(aprem, AP_DEVICES); 237962306a36Sopenharmony_ci DECLARE_BITMAP(aqrem, AP_DOMAINS); 238062306a36Sopenharmony_ci DECLARE_BITMAP(cdrem, AP_DOMAINS); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci do_remove = bitmap_andnot(aprem, 238362306a36Sopenharmony_ci (unsigned long *)prev_config_info->apm, 238462306a36Sopenharmony_ci (unsigned long *)cur_config_info->apm, 238562306a36Sopenharmony_ci AP_DEVICES); 238662306a36Sopenharmony_ci do_remove |= bitmap_andnot(aqrem, 238762306a36Sopenharmony_ci (unsigned long *)prev_config_info->aqm, 238862306a36Sopenharmony_ci (unsigned long *)cur_config_info->aqm, 238962306a36Sopenharmony_ci AP_DEVICES); 239062306a36Sopenharmony_ci do_remove |= bitmap_andnot(cdrem, 239162306a36Sopenharmony_ci (unsigned long *)prev_config_info->adm, 239262306a36Sopenharmony_ci (unsigned long *)cur_config_info->adm, 239362306a36Sopenharmony_ci AP_DEVICES); 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci if (do_remove) 239662306a36Sopenharmony_ci vfio_ap_mdev_cfg_remove(aprem, aqrem, cdrem); 239762306a36Sopenharmony_ci} 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci/** 240062306a36Sopenharmony_ci * vfio_ap_filter_apid_by_qtype: filter APIDs from an AP mask for adapters that 240162306a36Sopenharmony_ci * are older than AP type 10 (CEX4). 240262306a36Sopenharmony_ci * @apm: a bitmap of the APIDs to examine 240362306a36Sopenharmony_ci * @aqm: a bitmap of the APQIs of the queues to query for the AP type. 240462306a36Sopenharmony_ci */ 240562306a36Sopenharmony_cistatic void vfio_ap_filter_apid_by_qtype(unsigned long *apm, unsigned long *aqm) 240662306a36Sopenharmony_ci{ 240762306a36Sopenharmony_ci bool apid_cleared; 240862306a36Sopenharmony_ci struct ap_queue_status status; 240962306a36Sopenharmony_ci unsigned long apid, apqi; 241062306a36Sopenharmony_ci struct ap_tapq_gr2 info; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci for_each_set_bit_inv(apid, apm, AP_DEVICES) { 241362306a36Sopenharmony_ci apid_cleared = false; 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci for_each_set_bit_inv(apqi, aqm, AP_DOMAINS) { 241662306a36Sopenharmony_ci status = ap_test_queue(AP_MKQID(apid, apqi), 1, &info); 241762306a36Sopenharmony_ci switch (status.response_code) { 241862306a36Sopenharmony_ci /* 241962306a36Sopenharmony_ci * According to the architecture in each case 242062306a36Sopenharmony_ci * below, the queue's info should be filled. 242162306a36Sopenharmony_ci */ 242262306a36Sopenharmony_ci case AP_RESPONSE_NORMAL: 242362306a36Sopenharmony_ci case AP_RESPONSE_RESET_IN_PROGRESS: 242462306a36Sopenharmony_ci case AP_RESPONSE_DECONFIGURED: 242562306a36Sopenharmony_ci case AP_RESPONSE_CHECKSTOPPED: 242662306a36Sopenharmony_ci case AP_RESPONSE_BUSY: 242762306a36Sopenharmony_ci /* 242862306a36Sopenharmony_ci * The vfio_ap device driver only 242962306a36Sopenharmony_ci * supports CEX4 and newer adapters, so 243062306a36Sopenharmony_ci * remove the APID if the adapter is 243162306a36Sopenharmony_ci * older than a CEX4. 243262306a36Sopenharmony_ci */ 243362306a36Sopenharmony_ci if (info.at < AP_DEVICE_TYPE_CEX4) { 243462306a36Sopenharmony_ci clear_bit_inv(apid, apm); 243562306a36Sopenharmony_ci apid_cleared = true; 243662306a36Sopenharmony_ci } 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci break; 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci default: 244162306a36Sopenharmony_ci /* 244262306a36Sopenharmony_ci * If we don't know the adapter type, 244362306a36Sopenharmony_ci * clear its APID since it can't be 244462306a36Sopenharmony_ci * determined whether the vfio_ap 244562306a36Sopenharmony_ci * device driver supports it. 244662306a36Sopenharmony_ci */ 244762306a36Sopenharmony_ci clear_bit_inv(apid, apm); 244862306a36Sopenharmony_ci apid_cleared = true; 244962306a36Sopenharmony_ci break; 245062306a36Sopenharmony_ci } 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci /* 245362306a36Sopenharmony_ci * If we've already cleared the APID from the apm, there 245462306a36Sopenharmony_ci * is no need to continue examining the remainin AP 245562306a36Sopenharmony_ci * queues to determine the type of the adapter. 245662306a36Sopenharmony_ci */ 245762306a36Sopenharmony_ci if (apid_cleared) 245862306a36Sopenharmony_ci continue; 245962306a36Sopenharmony_ci } 246062306a36Sopenharmony_ci } 246162306a36Sopenharmony_ci} 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci/** 246462306a36Sopenharmony_ci * vfio_ap_mdev_cfg_add - store bitmaps specifying the adapters, domains and 246562306a36Sopenharmony_ci * control domains that have been added to the host's 246662306a36Sopenharmony_ci * AP configuration for each matrix mdev to which they 246762306a36Sopenharmony_ci * are assigned. 246862306a36Sopenharmony_ci * 246962306a36Sopenharmony_ci * @apm_add: a bitmap specifying the adapters that have been added to the AP 247062306a36Sopenharmony_ci * configuration. 247162306a36Sopenharmony_ci * @aqm_add: a bitmap specifying the domains that have been added to the AP 247262306a36Sopenharmony_ci * configuration. 247362306a36Sopenharmony_ci * @adm_add: a bitmap specifying the control domains that have been added to the 247462306a36Sopenharmony_ci * AP configuration. 247562306a36Sopenharmony_ci */ 247662306a36Sopenharmony_cistatic void vfio_ap_mdev_cfg_add(unsigned long *apm_add, unsigned long *aqm_add, 247762306a36Sopenharmony_ci unsigned long *adm_add) 247862306a36Sopenharmony_ci{ 247962306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci if (list_empty(&matrix_dev->mdev_list)) 248262306a36Sopenharmony_ci return; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci vfio_ap_filter_apid_by_qtype(apm_add, aqm_add); 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { 248762306a36Sopenharmony_ci bitmap_and(matrix_mdev->apm_add, 248862306a36Sopenharmony_ci matrix_mdev->matrix.apm, apm_add, AP_DEVICES); 248962306a36Sopenharmony_ci bitmap_and(matrix_mdev->aqm_add, 249062306a36Sopenharmony_ci matrix_mdev->matrix.aqm, aqm_add, AP_DOMAINS); 249162306a36Sopenharmony_ci bitmap_and(matrix_mdev->adm_add, 249262306a36Sopenharmony_ci matrix_mdev->matrix.adm, adm_add, AP_DEVICES); 249362306a36Sopenharmony_ci } 249462306a36Sopenharmony_ci} 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci/** 249762306a36Sopenharmony_ci * vfio_ap_mdev_on_cfg_add - responds to the addition of adapters, domains and 249862306a36Sopenharmony_ci * control domains to the host AP configuration 249962306a36Sopenharmony_ci * by updating the bitmaps that specify what adapters, 250062306a36Sopenharmony_ci * domains and control domains have been added so they 250162306a36Sopenharmony_ci * can be hot plugged into the guest when the AP bus 250262306a36Sopenharmony_ci * scan completes (see vfio_ap_on_scan_complete 250362306a36Sopenharmony_ci * function). 250462306a36Sopenharmony_ci * @cur_config_info: the current AP configuration information 250562306a36Sopenharmony_ci * @prev_config_info: the previous AP configuration information 250662306a36Sopenharmony_ci */ 250762306a36Sopenharmony_cistatic void vfio_ap_mdev_on_cfg_add(struct ap_config_info *cur_config_info, 250862306a36Sopenharmony_ci struct ap_config_info *prev_config_info) 250962306a36Sopenharmony_ci{ 251062306a36Sopenharmony_ci bool do_add; 251162306a36Sopenharmony_ci DECLARE_BITMAP(apm_add, AP_DEVICES); 251262306a36Sopenharmony_ci DECLARE_BITMAP(aqm_add, AP_DOMAINS); 251362306a36Sopenharmony_ci DECLARE_BITMAP(adm_add, AP_DOMAINS); 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci do_add = bitmap_andnot(apm_add, 251662306a36Sopenharmony_ci (unsigned long *)cur_config_info->apm, 251762306a36Sopenharmony_ci (unsigned long *)prev_config_info->apm, 251862306a36Sopenharmony_ci AP_DEVICES); 251962306a36Sopenharmony_ci do_add |= bitmap_andnot(aqm_add, 252062306a36Sopenharmony_ci (unsigned long *)cur_config_info->aqm, 252162306a36Sopenharmony_ci (unsigned long *)prev_config_info->aqm, 252262306a36Sopenharmony_ci AP_DOMAINS); 252362306a36Sopenharmony_ci do_add |= bitmap_andnot(adm_add, 252462306a36Sopenharmony_ci (unsigned long *)cur_config_info->adm, 252562306a36Sopenharmony_ci (unsigned long *)prev_config_info->adm, 252662306a36Sopenharmony_ci AP_DOMAINS); 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci if (do_add) 252962306a36Sopenharmony_ci vfio_ap_mdev_cfg_add(apm_add, aqm_add, adm_add); 253062306a36Sopenharmony_ci} 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci/** 253362306a36Sopenharmony_ci * vfio_ap_on_cfg_changed - handles notification of changes to the host AP 253462306a36Sopenharmony_ci * configuration. 253562306a36Sopenharmony_ci * 253662306a36Sopenharmony_ci * @cur_cfg_info: the current host AP configuration 253762306a36Sopenharmony_ci * @prev_cfg_info: the previous host AP configuration 253862306a36Sopenharmony_ci */ 253962306a36Sopenharmony_civoid vfio_ap_on_cfg_changed(struct ap_config_info *cur_cfg_info, 254062306a36Sopenharmony_ci struct ap_config_info *prev_cfg_info) 254162306a36Sopenharmony_ci{ 254262306a36Sopenharmony_ci if (!cur_cfg_info || !prev_cfg_info) 254362306a36Sopenharmony_ci return; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci mutex_lock(&matrix_dev->guests_lock); 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ci vfio_ap_mdev_on_cfg_remove(cur_cfg_info, prev_cfg_info); 254862306a36Sopenharmony_ci vfio_ap_mdev_on_cfg_add(cur_cfg_info, prev_cfg_info); 254962306a36Sopenharmony_ci memcpy(&matrix_dev->info, cur_cfg_info, sizeof(*cur_cfg_info)); 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci mutex_unlock(&matrix_dev->guests_lock); 255262306a36Sopenharmony_ci} 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_cistatic void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev) 255562306a36Sopenharmony_ci{ 255662306a36Sopenharmony_ci DECLARE_BITMAP(apm_filtered, AP_DEVICES); 255762306a36Sopenharmony_ci bool filter_domains, filter_adapters, filter_cdoms, do_hotplug = false; 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci mutex_lock(&matrix_mdev->kvm->lock); 256062306a36Sopenharmony_ci mutex_lock(&matrix_dev->mdevs_lock); 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci filter_adapters = bitmap_intersects(matrix_mdev->matrix.apm, 256362306a36Sopenharmony_ci matrix_mdev->apm_add, AP_DEVICES); 256462306a36Sopenharmony_ci filter_domains = bitmap_intersects(matrix_mdev->matrix.aqm, 256562306a36Sopenharmony_ci matrix_mdev->aqm_add, AP_DOMAINS); 256662306a36Sopenharmony_ci filter_cdoms = bitmap_intersects(matrix_mdev->matrix.adm, 256762306a36Sopenharmony_ci matrix_mdev->adm_add, AP_DOMAINS); 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci if (filter_adapters || filter_domains) 257062306a36Sopenharmony_ci do_hotplug = vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered); 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci if (filter_cdoms) 257362306a36Sopenharmony_ci do_hotplug |= vfio_ap_mdev_filter_cdoms(matrix_mdev); 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci if (do_hotplug) 257662306a36Sopenharmony_ci vfio_ap_mdev_update_guest_apcb(matrix_mdev); 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci reset_queues_for_apids(matrix_mdev, apm_filtered); 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci mutex_unlock(&matrix_dev->mdevs_lock); 258162306a36Sopenharmony_ci mutex_unlock(&matrix_mdev->kvm->lock); 258262306a36Sopenharmony_ci} 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_civoid vfio_ap_on_scan_complete(struct ap_config_info *new_config_info, 258562306a36Sopenharmony_ci struct ap_config_info *old_config_info) 258662306a36Sopenharmony_ci{ 258762306a36Sopenharmony_ci struct ap_matrix_mdev *matrix_mdev; 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci mutex_lock(&matrix_dev->guests_lock); 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { 259262306a36Sopenharmony_ci if (bitmap_empty(matrix_mdev->apm_add, AP_DEVICES) && 259362306a36Sopenharmony_ci bitmap_empty(matrix_mdev->aqm_add, AP_DOMAINS) && 259462306a36Sopenharmony_ci bitmap_empty(matrix_mdev->adm_add, AP_DOMAINS)) 259562306a36Sopenharmony_ci continue; 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci vfio_ap_mdev_hot_plug_cfg(matrix_mdev); 259862306a36Sopenharmony_ci bitmap_clear(matrix_mdev->apm_add, 0, AP_DEVICES); 259962306a36Sopenharmony_ci bitmap_clear(matrix_mdev->aqm_add, 0, AP_DOMAINS); 260062306a36Sopenharmony_ci bitmap_clear(matrix_mdev->adm_add, 0, AP_DOMAINS); 260162306a36Sopenharmony_ci } 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_ci mutex_unlock(&matrix_dev->guests_lock); 260462306a36Sopenharmony_ci} 2605