18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Adjunct processor matrix VFIO device driver callbacks.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2018
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
88c2ecf20Sopenharmony_ci *	      Halil Pasic <pasic@linux.ibm.com>
98c2ecf20Sopenharmony_ci *	      Pierre Morel <pmorel@linux.ibm.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/string.h>
128c2ecf20Sopenharmony_ci#include <linux/vfio.h>
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/list.h>
158c2ecf20Sopenharmony_ci#include <linux/ctype.h>
168c2ecf20Sopenharmony_ci#include <linux/bitops.h>
178c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <asm/kvm.h>
208c2ecf20Sopenharmony_ci#include <asm/zcrypt.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "vfio_ap_private.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define VFIO_AP_MDEV_TYPE_HWVIRT "passthrough"
258c2ecf20Sopenharmony_ci#define VFIO_AP_MDEV_NAME_HWVIRT "VFIO AP Passthrough Device"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_reset_queues(struct mdev_device *mdev);
288c2ecf20Sopenharmony_cistatic struct vfio_ap_queue *vfio_ap_find_queue(int apqn);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int match_apqn(struct device *dev, const void *data)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct vfio_ap_queue *q = dev_get_drvdata(dev);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return (q->apqn == *(int *)(data)) ? 1 : 0;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/**
388c2ecf20Sopenharmony_ci * vfio_ap_get_queue: Retrieve a queue with a specific APQN from a list
398c2ecf20Sopenharmony_ci * @matrix_mdev: the associated mediated matrix
408c2ecf20Sopenharmony_ci * @apqn: The queue APQN
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci * Retrieve a queue with a specific APQN from the list of the
438c2ecf20Sopenharmony_ci * devices of the vfio_ap_drv.
448c2ecf20Sopenharmony_ci * Verify that the APID and the APQI are set in the matrix.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * Returns the pointer to the associated vfio_ap_queue
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cistatic struct vfio_ap_queue *vfio_ap_get_queue(
498c2ecf20Sopenharmony_ci					struct ap_matrix_mdev *matrix_mdev,
508c2ecf20Sopenharmony_ci					int apqn)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct vfio_ap_queue *q;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (!test_bit_inv(AP_QID_CARD(apqn), matrix_mdev->matrix.apm))
558c2ecf20Sopenharmony_ci		return NULL;
568c2ecf20Sopenharmony_ci	if (!test_bit_inv(AP_QID_QUEUE(apqn), matrix_mdev->matrix.aqm))
578c2ecf20Sopenharmony_ci		return NULL;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	q = vfio_ap_find_queue(apqn);
608c2ecf20Sopenharmony_ci	if (q)
618c2ecf20Sopenharmony_ci		q->matrix_mdev = matrix_mdev;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return q;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/**
678c2ecf20Sopenharmony_ci * vfio_ap_wait_for_irqclear
688c2ecf20Sopenharmony_ci * @apqn: The AP Queue number
698c2ecf20Sopenharmony_ci *
708c2ecf20Sopenharmony_ci * Checks the IRQ bit for the status of this APQN using ap_tapq.
718c2ecf20Sopenharmony_ci * Returns if the ap_tapq function succeeded and the bit is clear.
728c2ecf20Sopenharmony_ci * Returns if ap_tapq function failed with invalid, deconfigured or
738c2ecf20Sopenharmony_ci * checkstopped AP.
748c2ecf20Sopenharmony_ci * Otherwise retries up to 5 times after waiting 20ms.
758c2ecf20Sopenharmony_ci *
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_cistatic void vfio_ap_wait_for_irqclear(int apqn)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct ap_queue_status status;
808c2ecf20Sopenharmony_ci	int retry = 5;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	do {
838c2ecf20Sopenharmony_ci		status = ap_tapq(apqn, NULL);
848c2ecf20Sopenharmony_ci		switch (status.response_code) {
858c2ecf20Sopenharmony_ci		case AP_RESPONSE_NORMAL:
868c2ecf20Sopenharmony_ci		case AP_RESPONSE_RESET_IN_PROGRESS:
878c2ecf20Sopenharmony_ci			if (!status.irq_enabled)
888c2ecf20Sopenharmony_ci				return;
898c2ecf20Sopenharmony_ci			fallthrough;
908c2ecf20Sopenharmony_ci		case AP_RESPONSE_BUSY:
918c2ecf20Sopenharmony_ci			msleep(20);
928c2ecf20Sopenharmony_ci			break;
938c2ecf20Sopenharmony_ci		case AP_RESPONSE_Q_NOT_AVAIL:
948c2ecf20Sopenharmony_ci		case AP_RESPONSE_DECONFIGURED:
958c2ecf20Sopenharmony_ci		case AP_RESPONSE_CHECKSTOPPED:
968c2ecf20Sopenharmony_ci		default:
978c2ecf20Sopenharmony_ci			WARN_ONCE(1, "%s: tapq rc %02x: %04x\n", __func__,
988c2ecf20Sopenharmony_ci				  status.response_code, apqn);
998c2ecf20Sopenharmony_ci			return;
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci	} while (--retry);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	WARN_ONCE(1, "%s: tapq rc %02x: %04x could not clear IR bit\n",
1048c2ecf20Sopenharmony_ci		  __func__, status.response_code, apqn);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/**
1088c2ecf20Sopenharmony_ci * vfio_ap_free_aqic_resources
1098c2ecf20Sopenharmony_ci * @q: The vfio_ap_queue
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * Unregisters the ISC in the GIB when the saved ISC not invalid.
1128c2ecf20Sopenharmony_ci * Unpin the guest's page holding the NIB when it exist.
1138c2ecf20Sopenharmony_ci * Reset the saved_pfn and saved_isc to invalid values.
1148c2ecf20Sopenharmony_ci *
1158c2ecf20Sopenharmony_ci */
1168c2ecf20Sopenharmony_cistatic void vfio_ap_free_aqic_resources(struct vfio_ap_queue *q)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	if (!q)
1198c2ecf20Sopenharmony_ci		return;
1208c2ecf20Sopenharmony_ci	if (q->saved_isc != VFIO_AP_ISC_INVALID &&
1218c2ecf20Sopenharmony_ci	    !WARN_ON(!(q->matrix_mdev && q->matrix_mdev->kvm))) {
1228c2ecf20Sopenharmony_ci		kvm_s390_gisc_unregister(q->matrix_mdev->kvm, q->saved_isc);
1238c2ecf20Sopenharmony_ci		q->saved_isc = VFIO_AP_ISC_INVALID;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	if (q->saved_pfn && !WARN_ON(!q->matrix_mdev)) {
1268c2ecf20Sopenharmony_ci		vfio_unpin_pages(mdev_dev(q->matrix_mdev->mdev),
1278c2ecf20Sopenharmony_ci				 &q->saved_pfn, 1);
1288c2ecf20Sopenharmony_ci		q->saved_pfn = 0;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/**
1338c2ecf20Sopenharmony_ci * vfio_ap_irq_disable
1348c2ecf20Sopenharmony_ci * @q: The vfio_ap_queue
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * Uses ap_aqic to disable the interruption and in case of success, reset
1378c2ecf20Sopenharmony_ci * in progress or IRQ disable command already proceeded: calls
1388c2ecf20Sopenharmony_ci * vfio_ap_wait_for_irqclear() to check for the IRQ bit to be clear
1398c2ecf20Sopenharmony_ci * and calls vfio_ap_free_aqic_resources() to free the resources associated
1408c2ecf20Sopenharmony_ci * with the AP interrupt handling.
1418c2ecf20Sopenharmony_ci *
1428c2ecf20Sopenharmony_ci * In the case the AP is busy, or a reset is in progress,
1438c2ecf20Sopenharmony_ci * retries after 20ms, up to 5 times.
1448c2ecf20Sopenharmony_ci *
1458c2ecf20Sopenharmony_ci * Returns if ap_aqic function failed with invalid, deconfigured or
1468c2ecf20Sopenharmony_ci * checkstopped AP.
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_cistatic struct ap_queue_status vfio_ap_irq_disable(struct vfio_ap_queue *q)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct ap_qirq_ctrl aqic_gisa = {};
1518c2ecf20Sopenharmony_ci	struct ap_queue_status status;
1528c2ecf20Sopenharmony_ci	int retries = 5;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	do {
1558c2ecf20Sopenharmony_ci		status = ap_aqic(q->apqn, aqic_gisa, NULL);
1568c2ecf20Sopenharmony_ci		switch (status.response_code) {
1578c2ecf20Sopenharmony_ci		case AP_RESPONSE_OTHERWISE_CHANGED:
1588c2ecf20Sopenharmony_ci		case AP_RESPONSE_NORMAL:
1598c2ecf20Sopenharmony_ci			vfio_ap_wait_for_irqclear(q->apqn);
1608c2ecf20Sopenharmony_ci			goto end_free;
1618c2ecf20Sopenharmony_ci		case AP_RESPONSE_RESET_IN_PROGRESS:
1628c2ecf20Sopenharmony_ci		case AP_RESPONSE_BUSY:
1638c2ecf20Sopenharmony_ci			msleep(20);
1648c2ecf20Sopenharmony_ci			break;
1658c2ecf20Sopenharmony_ci		case AP_RESPONSE_Q_NOT_AVAIL:
1668c2ecf20Sopenharmony_ci		case AP_RESPONSE_DECONFIGURED:
1678c2ecf20Sopenharmony_ci		case AP_RESPONSE_CHECKSTOPPED:
1688c2ecf20Sopenharmony_ci		case AP_RESPONSE_INVALID_ADDRESS:
1698c2ecf20Sopenharmony_ci		default:
1708c2ecf20Sopenharmony_ci			/* All cases in default means AP not operational */
1718c2ecf20Sopenharmony_ci			WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__,
1728c2ecf20Sopenharmony_ci				  status.response_code);
1738c2ecf20Sopenharmony_ci			goto end_free;
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci	} while (retries--);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__,
1788c2ecf20Sopenharmony_ci		  status.response_code);
1798c2ecf20Sopenharmony_ciend_free:
1808c2ecf20Sopenharmony_ci	vfio_ap_free_aqic_resources(q);
1818c2ecf20Sopenharmony_ci	q->matrix_mdev = NULL;
1828c2ecf20Sopenharmony_ci	return status;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/**
1868c2ecf20Sopenharmony_ci * vfio_ap_setirq: Enable Interruption for a APQN
1878c2ecf20Sopenharmony_ci *
1888c2ecf20Sopenharmony_ci * @dev: the device associated with the ap_queue
1898c2ecf20Sopenharmony_ci * @q:	 the vfio_ap_queue holding AQIC parameters
1908c2ecf20Sopenharmony_ci *
1918c2ecf20Sopenharmony_ci * Pin the NIB saved in *q
1928c2ecf20Sopenharmony_ci * Register the guest ISC to GIB interface and retrieve the
1938c2ecf20Sopenharmony_ci * host ISC to issue the host side PQAP/AQIC
1948c2ecf20Sopenharmony_ci *
1958c2ecf20Sopenharmony_ci * Response.status may be set to AP_RESPONSE_INVALID_ADDRESS in case the
1968c2ecf20Sopenharmony_ci * vfio_pin_pages failed.
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci * Otherwise return the ap_queue_status returned by the ap_aqic(),
1998c2ecf20Sopenharmony_ci * all retry handling will be done by the guest.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_cistatic struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,
2028c2ecf20Sopenharmony_ci						 int isc,
2038c2ecf20Sopenharmony_ci						 unsigned long nib)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct ap_qirq_ctrl aqic_gisa = {};
2068c2ecf20Sopenharmony_ci	struct ap_queue_status status = {};
2078c2ecf20Sopenharmony_ci	struct kvm_s390_gisa *gisa;
2088c2ecf20Sopenharmony_ci	struct kvm *kvm;
2098c2ecf20Sopenharmony_ci	unsigned long h_nib, g_pfn, h_pfn;
2108c2ecf20Sopenharmony_ci	int ret;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	g_pfn = nib >> PAGE_SHIFT;
2138c2ecf20Sopenharmony_ci	ret = vfio_pin_pages(mdev_dev(q->matrix_mdev->mdev), &g_pfn, 1,
2148c2ecf20Sopenharmony_ci			     IOMMU_READ | IOMMU_WRITE, &h_pfn);
2158c2ecf20Sopenharmony_ci	switch (ret) {
2168c2ecf20Sopenharmony_ci	case 1:
2178c2ecf20Sopenharmony_ci		break;
2188c2ecf20Sopenharmony_ci	default:
2198c2ecf20Sopenharmony_ci		status.response_code = AP_RESPONSE_INVALID_ADDRESS;
2208c2ecf20Sopenharmony_ci		return status;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	kvm = q->matrix_mdev->kvm;
2248c2ecf20Sopenharmony_ci	gisa = kvm->arch.gisa_int.origin;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	h_nib = (h_pfn << PAGE_SHIFT) | (nib & ~PAGE_MASK);
2278c2ecf20Sopenharmony_ci	aqic_gisa.gisc = isc;
2288c2ecf20Sopenharmony_ci	aqic_gisa.isc = kvm_s390_gisc_register(kvm, isc);
2298c2ecf20Sopenharmony_ci	aqic_gisa.ir = 1;
2308c2ecf20Sopenharmony_ci	aqic_gisa.gisa = (uint64_t)gisa >> 4;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	status = ap_aqic(q->apqn, aqic_gisa, (void *)h_nib);
2338c2ecf20Sopenharmony_ci	switch (status.response_code) {
2348c2ecf20Sopenharmony_ci	case AP_RESPONSE_NORMAL:
2358c2ecf20Sopenharmony_ci		/* See if we did clear older IRQ configuration */
2368c2ecf20Sopenharmony_ci		vfio_ap_free_aqic_resources(q);
2378c2ecf20Sopenharmony_ci		q->saved_pfn = g_pfn;
2388c2ecf20Sopenharmony_ci		q->saved_isc = isc;
2398c2ecf20Sopenharmony_ci		break;
2408c2ecf20Sopenharmony_ci	case AP_RESPONSE_OTHERWISE_CHANGED:
2418c2ecf20Sopenharmony_ci		/* We could not modify IRQ setings: clear new configuration */
2428c2ecf20Sopenharmony_ci		vfio_unpin_pages(mdev_dev(q->matrix_mdev->mdev), &g_pfn, 1);
2438c2ecf20Sopenharmony_ci		kvm_s390_gisc_unregister(kvm, isc);
2448c2ecf20Sopenharmony_ci		break;
2458c2ecf20Sopenharmony_ci	default:
2468c2ecf20Sopenharmony_ci		pr_warn("%s: apqn %04x: response: %02x\n", __func__, q->apqn,
2478c2ecf20Sopenharmony_ci			status.response_code);
2488c2ecf20Sopenharmony_ci		vfio_ap_irq_disable(q);
2498c2ecf20Sopenharmony_ci		break;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return status;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/**
2568c2ecf20Sopenharmony_ci * handle_pqap: PQAP instruction callback
2578c2ecf20Sopenharmony_ci *
2588c2ecf20Sopenharmony_ci * @vcpu: The vcpu on which we received the PQAP instruction
2598c2ecf20Sopenharmony_ci *
2608c2ecf20Sopenharmony_ci * Get the general register contents to initialize internal variables.
2618c2ecf20Sopenharmony_ci * REG[0]: APQN
2628c2ecf20Sopenharmony_ci * REG[1]: IR and ISC
2638c2ecf20Sopenharmony_ci * REG[2]: NIB
2648c2ecf20Sopenharmony_ci *
2658c2ecf20Sopenharmony_ci * Response.status may be set to following Response Code:
2668c2ecf20Sopenharmony_ci * - AP_RESPONSE_Q_NOT_AVAIL: if the queue is not available
2678c2ecf20Sopenharmony_ci * - AP_RESPONSE_DECONFIGURED: if the queue is not configured
2688c2ecf20Sopenharmony_ci * - AP_RESPONSE_NORMAL (0) : in case of successs
2698c2ecf20Sopenharmony_ci *   Check vfio_ap_setirq() and vfio_ap_clrirq() for other possible RC.
2708c2ecf20Sopenharmony_ci * We take the matrix_dev lock to ensure serialization on queues and
2718c2ecf20Sopenharmony_ci * mediated device access.
2728c2ecf20Sopenharmony_ci *
2738c2ecf20Sopenharmony_ci * Return 0 if we could handle the request inside KVM.
2748c2ecf20Sopenharmony_ci * otherwise, returns -EOPNOTSUPP to let QEMU handle the fault.
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic int handle_pqap(struct kvm_vcpu *vcpu)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	uint64_t status;
2798c2ecf20Sopenharmony_ci	uint16_t apqn;
2808c2ecf20Sopenharmony_ci	struct vfio_ap_queue *q;
2818c2ecf20Sopenharmony_ci	struct ap_queue_status qstatus = {
2828c2ecf20Sopenharmony_ci			       .response_code = AP_RESPONSE_Q_NOT_AVAIL, };
2838c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* If we do not use the AIV facility just go to userland */
2868c2ecf20Sopenharmony_ci	if (!(vcpu->arch.sie_block->eca & ECA_AIV))
2878c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	apqn = vcpu->run->s.regs.gprs[0] & 0xffff;
2908c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (!vcpu->kvm->arch.crypto.pqap_hook)
2938c2ecf20Sopenharmony_ci		goto out_unlock;
2948c2ecf20Sopenharmony_ci	matrix_mdev = container_of(vcpu->kvm->arch.crypto.pqap_hook,
2958c2ecf20Sopenharmony_ci				   struct ap_matrix_mdev, pqap_hook);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	q = vfio_ap_get_queue(matrix_mdev, apqn);
2988c2ecf20Sopenharmony_ci	if (!q)
2998c2ecf20Sopenharmony_ci		goto out_unlock;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	status = vcpu->run->s.regs.gprs[1];
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/* If IR bit(16) is set we enable the interrupt */
3048c2ecf20Sopenharmony_ci	if ((status >> (63 - 16)) & 0x01)
3058c2ecf20Sopenharmony_ci		qstatus = vfio_ap_irq_enable(q, status & 0x07,
3068c2ecf20Sopenharmony_ci					     vcpu->run->s.regs.gprs[2]);
3078c2ecf20Sopenharmony_ci	else
3088c2ecf20Sopenharmony_ci		qstatus = vfio_ap_irq_disable(q);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ciout_unlock:
3118c2ecf20Sopenharmony_ci	memcpy(&vcpu->run->s.regs.gprs[1], &qstatus, sizeof(qstatus));
3128c2ecf20Sopenharmony_ci	vcpu->run->s.regs.gprs[1] >>= 32;
3138c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void vfio_ap_matrix_init(struct ap_config_info *info,
3188c2ecf20Sopenharmony_ci				struct ap_matrix *matrix)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	matrix->apm_max = info->apxa ? info->Na : 63;
3218c2ecf20Sopenharmony_ci	matrix->aqm_max = info->apxa ? info->Nd : 15;
3228c2ecf20Sopenharmony_ci	matrix->adm_max = info->apxa ? info->Nd : 15;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_create(struct kobject *kobj, struct mdev_device *mdev)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if ((atomic_dec_if_positive(&matrix_dev->available_instances) < 0))
3308c2ecf20Sopenharmony_ci		return -EPERM;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	matrix_mdev = kzalloc(sizeof(*matrix_mdev), GFP_KERNEL);
3338c2ecf20Sopenharmony_ci	if (!matrix_mdev) {
3348c2ecf20Sopenharmony_ci		atomic_inc(&matrix_dev->available_instances);
3358c2ecf20Sopenharmony_ci		return -ENOMEM;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	matrix_mdev->mdev = mdev;
3398c2ecf20Sopenharmony_ci	vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->matrix);
3408c2ecf20Sopenharmony_ci	mdev_set_drvdata(mdev, matrix_mdev);
3418c2ecf20Sopenharmony_ci	matrix_mdev->pqap_hook.hook = handle_pqap;
3428c2ecf20Sopenharmony_ci	matrix_mdev->pqap_hook.owner = THIS_MODULE;
3438c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
3448c2ecf20Sopenharmony_ci	list_add(&matrix_mdev->node, &matrix_dev->mdev_list);
3458c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return 0;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_remove(struct mdev_device *mdev)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (matrix_mdev->kvm)
3558c2ecf20Sopenharmony_ci		return -EBUSY;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
3588c2ecf20Sopenharmony_ci	vfio_ap_mdev_reset_queues(mdev);
3598c2ecf20Sopenharmony_ci	list_del(&matrix_mdev->node);
3608c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	kfree(matrix_mdev);
3638c2ecf20Sopenharmony_ci	mdev_set_drvdata(mdev, NULL);
3648c2ecf20Sopenharmony_ci	atomic_inc(&matrix_dev->available_instances);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic ssize_t name_show(struct kobject *kobj, struct device *dev, char *buf)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", VFIO_AP_MDEV_NAME_HWVIRT);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic MDEV_TYPE_ATTR_RO(name);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic ssize_t available_instances_show(struct kobject *kobj,
3778c2ecf20Sopenharmony_ci					struct device *dev, char *buf)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n",
3808c2ecf20Sopenharmony_ci		       atomic_read(&matrix_dev->available_instances));
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic MDEV_TYPE_ATTR_RO(available_instances);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic ssize_t device_api_show(struct kobject *kobj, struct device *dev,
3868c2ecf20Sopenharmony_ci			       char *buf)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", VFIO_DEVICE_API_AP_STRING);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic MDEV_TYPE_ATTR_RO(device_api);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic struct attribute *vfio_ap_mdev_type_attrs[] = {
3948c2ecf20Sopenharmony_ci	&mdev_type_attr_name.attr,
3958c2ecf20Sopenharmony_ci	&mdev_type_attr_device_api.attr,
3968c2ecf20Sopenharmony_ci	&mdev_type_attr_available_instances.attr,
3978c2ecf20Sopenharmony_ci	NULL,
3988c2ecf20Sopenharmony_ci};
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic struct attribute_group vfio_ap_mdev_hwvirt_type_group = {
4018c2ecf20Sopenharmony_ci	.name = VFIO_AP_MDEV_TYPE_HWVIRT,
4028c2ecf20Sopenharmony_ci	.attrs = vfio_ap_mdev_type_attrs,
4038c2ecf20Sopenharmony_ci};
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic struct attribute_group *vfio_ap_mdev_type_groups[] = {
4068c2ecf20Sopenharmony_ci	&vfio_ap_mdev_hwvirt_type_group,
4078c2ecf20Sopenharmony_ci	NULL,
4088c2ecf20Sopenharmony_ci};
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistruct vfio_ap_queue_reserved {
4118c2ecf20Sopenharmony_ci	unsigned long *apid;
4128c2ecf20Sopenharmony_ci	unsigned long *apqi;
4138c2ecf20Sopenharmony_ci	bool reserved;
4148c2ecf20Sopenharmony_ci};
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci/**
4178c2ecf20Sopenharmony_ci * vfio_ap_has_queue
4188c2ecf20Sopenharmony_ci *
4198c2ecf20Sopenharmony_ci * @dev: an AP queue device
4208c2ecf20Sopenharmony_ci * @data: a struct vfio_ap_queue_reserved reference
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci * Flags whether the AP queue device (@dev) has a queue ID containing the APQN,
4238c2ecf20Sopenharmony_ci * apid or apqi specified in @data:
4248c2ecf20Sopenharmony_ci *
4258c2ecf20Sopenharmony_ci * - If @data contains both an apid and apqi value, then @data will be flagged
4268c2ecf20Sopenharmony_ci *   as reserved if the APID and APQI fields for the AP queue device matches
4278c2ecf20Sopenharmony_ci *
4288c2ecf20Sopenharmony_ci * - If @data contains only an apid value, @data will be flagged as
4298c2ecf20Sopenharmony_ci *   reserved if the APID field in the AP queue device matches
4308c2ecf20Sopenharmony_ci *
4318c2ecf20Sopenharmony_ci * - If @data contains only an apqi value, @data will be flagged as
4328c2ecf20Sopenharmony_ci *   reserved if the APQI field in the AP queue device matches
4338c2ecf20Sopenharmony_ci *
4348c2ecf20Sopenharmony_ci * Returns 0 to indicate the input to function succeeded. Returns -EINVAL if
4358c2ecf20Sopenharmony_ci * @data does not contain either an apid or apqi.
4368c2ecf20Sopenharmony_ci */
4378c2ecf20Sopenharmony_cistatic int vfio_ap_has_queue(struct device *dev, void *data)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct vfio_ap_queue_reserved *qres = data;
4408c2ecf20Sopenharmony_ci	struct ap_queue *ap_queue = to_ap_queue(dev);
4418c2ecf20Sopenharmony_ci	ap_qid_t qid;
4428c2ecf20Sopenharmony_ci	unsigned long id;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (qres->apid && qres->apqi) {
4458c2ecf20Sopenharmony_ci		qid = AP_MKQID(*qres->apid, *qres->apqi);
4468c2ecf20Sopenharmony_ci		if (qid == ap_queue->qid)
4478c2ecf20Sopenharmony_ci			qres->reserved = true;
4488c2ecf20Sopenharmony_ci	} else if (qres->apid && !qres->apqi) {
4498c2ecf20Sopenharmony_ci		id = AP_QID_CARD(ap_queue->qid);
4508c2ecf20Sopenharmony_ci		if (id == *qres->apid)
4518c2ecf20Sopenharmony_ci			qres->reserved = true;
4528c2ecf20Sopenharmony_ci	} else if (!qres->apid && qres->apqi) {
4538c2ecf20Sopenharmony_ci		id = AP_QID_QUEUE(ap_queue->qid);
4548c2ecf20Sopenharmony_ci		if (id == *qres->apqi)
4558c2ecf20Sopenharmony_ci			qres->reserved = true;
4568c2ecf20Sopenharmony_ci	} else {
4578c2ecf20Sopenharmony_ci		return -EINVAL;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return 0;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci/**
4648c2ecf20Sopenharmony_ci * vfio_ap_verify_queue_reserved
4658c2ecf20Sopenharmony_ci *
4668c2ecf20Sopenharmony_ci * @matrix_dev: a mediated matrix device
4678c2ecf20Sopenharmony_ci * @apid: an AP adapter ID
4688c2ecf20Sopenharmony_ci * @apqi: an AP queue index
4698c2ecf20Sopenharmony_ci *
4708c2ecf20Sopenharmony_ci * Verifies that the AP queue with @apid/@apqi is reserved by the VFIO AP device
4718c2ecf20Sopenharmony_ci * driver according to the following rules:
4728c2ecf20Sopenharmony_ci *
4738c2ecf20Sopenharmony_ci * - If both @apid and @apqi are not NULL, then there must be an AP queue
4748c2ecf20Sopenharmony_ci *   device bound to the vfio_ap driver with the APQN identified by @apid and
4758c2ecf20Sopenharmony_ci *   @apqi
4768c2ecf20Sopenharmony_ci *
4778c2ecf20Sopenharmony_ci * - If only @apid is not NULL, then there must be an AP queue device bound
4788c2ecf20Sopenharmony_ci *   to the vfio_ap driver with an APQN containing @apid
4798c2ecf20Sopenharmony_ci *
4808c2ecf20Sopenharmony_ci * - If only @apqi is not NULL, then there must be an AP queue device bound
4818c2ecf20Sopenharmony_ci *   to the vfio_ap driver with an APQN containing @apqi
4828c2ecf20Sopenharmony_ci *
4838c2ecf20Sopenharmony_ci * Returns 0 if the AP queue is reserved; otherwise, returns -EADDRNOTAVAIL.
4848c2ecf20Sopenharmony_ci */
4858c2ecf20Sopenharmony_cistatic int vfio_ap_verify_queue_reserved(unsigned long *apid,
4868c2ecf20Sopenharmony_ci					 unsigned long *apqi)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	int ret;
4898c2ecf20Sopenharmony_ci	struct vfio_ap_queue_reserved qres;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	qres.apid = apid;
4928c2ecf20Sopenharmony_ci	qres.apqi = apqi;
4938c2ecf20Sopenharmony_ci	qres.reserved = false;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	ret = driver_for_each_device(&matrix_dev->vfio_ap_drv->driver, NULL,
4968c2ecf20Sopenharmony_ci				     &qres, vfio_ap_has_queue);
4978c2ecf20Sopenharmony_ci	if (ret)
4988c2ecf20Sopenharmony_ci		return ret;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (qres.reserved)
5018c2ecf20Sopenharmony_ci		return 0;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	return -EADDRNOTAVAIL;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic int
5078c2ecf20Sopenharmony_civfio_ap_mdev_verify_queues_reserved_for_apid(struct ap_matrix_mdev *matrix_mdev,
5088c2ecf20Sopenharmony_ci					     unsigned long apid)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	int ret;
5118c2ecf20Sopenharmony_ci	unsigned long apqi;
5128c2ecf20Sopenharmony_ci	unsigned long nbits = matrix_mdev->matrix.aqm_max + 1;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (find_first_bit_inv(matrix_mdev->matrix.aqm, nbits) >= nbits)
5158c2ecf20Sopenharmony_ci		return vfio_ap_verify_queue_reserved(&apid, NULL);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, nbits) {
5188c2ecf20Sopenharmony_ci		ret = vfio_ap_verify_queue_reserved(&apid, &apqi);
5198c2ecf20Sopenharmony_ci		if (ret)
5208c2ecf20Sopenharmony_ci			return ret;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	return 0;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci/**
5278c2ecf20Sopenharmony_ci * vfio_ap_mdev_verify_no_sharing
5288c2ecf20Sopenharmony_ci *
5298c2ecf20Sopenharmony_ci * Verifies that the APQNs derived from the cross product of the AP adapter IDs
5308c2ecf20Sopenharmony_ci * and AP queue indexes comprising the AP matrix are not configured for another
5318c2ecf20Sopenharmony_ci * mediated device. AP queue sharing is not allowed.
5328c2ecf20Sopenharmony_ci *
5338c2ecf20Sopenharmony_ci * @matrix_mdev: the mediated matrix device
5348c2ecf20Sopenharmony_ci *
5358c2ecf20Sopenharmony_ci * Returns 0 if the APQNs are not shared, otherwise; returns -EADDRINUSE.
5368c2ecf20Sopenharmony_ci */
5378c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_verify_no_sharing(struct ap_matrix_mdev *matrix_mdev)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *lstdev;
5408c2ecf20Sopenharmony_ci	DECLARE_BITMAP(apm, AP_DEVICES);
5418c2ecf20Sopenharmony_ci	DECLARE_BITMAP(aqm, AP_DOMAINS);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	list_for_each_entry(lstdev, &matrix_dev->mdev_list, node) {
5448c2ecf20Sopenharmony_ci		if (matrix_mdev == lstdev)
5458c2ecf20Sopenharmony_ci			continue;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci		memset(apm, 0, sizeof(apm));
5488c2ecf20Sopenharmony_ci		memset(aqm, 0, sizeof(aqm));
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		/*
5518c2ecf20Sopenharmony_ci		 * We work on full longs, as we can only exclude the leftover
5528c2ecf20Sopenharmony_ci		 * bits in non-inverse order. The leftover is all zeros.
5538c2ecf20Sopenharmony_ci		 */
5548c2ecf20Sopenharmony_ci		if (!bitmap_and(apm, matrix_mdev->matrix.apm,
5558c2ecf20Sopenharmony_ci				lstdev->matrix.apm, AP_DEVICES))
5568c2ecf20Sopenharmony_ci			continue;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		if (!bitmap_and(aqm, matrix_mdev->matrix.aqm,
5598c2ecf20Sopenharmony_ci				lstdev->matrix.aqm, AP_DOMAINS))
5608c2ecf20Sopenharmony_ci			continue;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci		return -EADDRINUSE;
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	return 0;
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci/**
5698c2ecf20Sopenharmony_ci * assign_adapter_store
5708c2ecf20Sopenharmony_ci *
5718c2ecf20Sopenharmony_ci * @dev:	the matrix device
5728c2ecf20Sopenharmony_ci * @attr:	the mediated matrix device's assign_adapter attribute
5738c2ecf20Sopenharmony_ci * @buf:	a buffer containing the AP adapter number (APID) to
5748c2ecf20Sopenharmony_ci *		be assigned
5758c2ecf20Sopenharmony_ci * @count:	the number of bytes in @buf
5768c2ecf20Sopenharmony_ci *
5778c2ecf20Sopenharmony_ci * Parses the APID from @buf and sets the corresponding bit in the mediated
5788c2ecf20Sopenharmony_ci * matrix device's APM.
5798c2ecf20Sopenharmony_ci *
5808c2ecf20Sopenharmony_ci * Returns the number of bytes processed if the APID is valid; otherwise,
5818c2ecf20Sopenharmony_ci * returns one of the following errors:
5828c2ecf20Sopenharmony_ci *
5838c2ecf20Sopenharmony_ci *	1. -EINVAL
5848c2ecf20Sopenharmony_ci *	   The APID is not a valid number
5858c2ecf20Sopenharmony_ci *
5868c2ecf20Sopenharmony_ci *	2. -ENODEV
5878c2ecf20Sopenharmony_ci *	   The APID exceeds the maximum value configured for the system
5888c2ecf20Sopenharmony_ci *
5898c2ecf20Sopenharmony_ci *	3. -EADDRNOTAVAIL
5908c2ecf20Sopenharmony_ci *	   An APQN derived from the cross product of the APID being assigned
5918c2ecf20Sopenharmony_ci *	   and the APQIs previously assigned is not bound to the vfio_ap device
5928c2ecf20Sopenharmony_ci *	   driver; or, if no APQIs have yet been assigned, the APID is not
5938c2ecf20Sopenharmony_ci *	   contained in an APQN bound to the vfio_ap device driver.
5948c2ecf20Sopenharmony_ci *
5958c2ecf20Sopenharmony_ci *	4. -EADDRINUSE
5968c2ecf20Sopenharmony_ci *	   An APQN derived from the cross product of the APID being assigned
5978c2ecf20Sopenharmony_ci *	   and the APQIs previously assigned is being used by another mediated
5988c2ecf20Sopenharmony_ci *	   matrix device
5998c2ecf20Sopenharmony_ci */
6008c2ecf20Sopenharmony_cistatic ssize_t assign_adapter_store(struct device *dev,
6018c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
6028c2ecf20Sopenharmony_ci				    const char *buf, size_t count)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	int ret;
6058c2ecf20Sopenharmony_ci	unsigned long apid;
6068c2ecf20Sopenharmony_ci	struct mdev_device *mdev = mdev_from_dev(dev);
6078c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	/* If the guest is running, disallow assignment of adapter */
6108c2ecf20Sopenharmony_ci	if (matrix_mdev->kvm)
6118c2ecf20Sopenharmony_ci		return -EBUSY;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &apid);
6148c2ecf20Sopenharmony_ci	if (ret)
6158c2ecf20Sopenharmony_ci		return ret;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	if (apid > matrix_mdev->matrix.apm_max)
6188c2ecf20Sopenharmony_ci		return -ENODEV;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/*
6218c2ecf20Sopenharmony_ci	 * Set the bit in the AP mask (APM) corresponding to the AP adapter
6228c2ecf20Sopenharmony_ci	 * number (APID). The bits in the mask, from most significant to least
6238c2ecf20Sopenharmony_ci	 * significant bit, correspond to APIDs 0-255.
6248c2ecf20Sopenharmony_ci	 */
6258c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	ret = vfio_ap_mdev_verify_queues_reserved_for_apid(matrix_mdev, apid);
6288c2ecf20Sopenharmony_ci	if (ret)
6298c2ecf20Sopenharmony_ci		goto done;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	set_bit_inv(apid, matrix_mdev->matrix.apm);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	ret = vfio_ap_mdev_verify_no_sharing(matrix_mdev);
6348c2ecf20Sopenharmony_ci	if (ret)
6358c2ecf20Sopenharmony_ci		goto share_err;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	ret = count;
6388c2ecf20Sopenharmony_ci	goto done;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cishare_err:
6418c2ecf20Sopenharmony_ci	clear_bit_inv(apid, matrix_mdev->matrix.apm);
6428c2ecf20Sopenharmony_cidone:
6438c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	return ret;
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(assign_adapter);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci/**
6508c2ecf20Sopenharmony_ci * unassign_adapter_store
6518c2ecf20Sopenharmony_ci *
6528c2ecf20Sopenharmony_ci * @dev:	the matrix device
6538c2ecf20Sopenharmony_ci * @attr:	the mediated matrix device's unassign_adapter attribute
6548c2ecf20Sopenharmony_ci * @buf:	a buffer containing the adapter number (APID) to be unassigned
6558c2ecf20Sopenharmony_ci * @count:	the number of bytes in @buf
6568c2ecf20Sopenharmony_ci *
6578c2ecf20Sopenharmony_ci * Parses the APID from @buf and clears the corresponding bit in the mediated
6588c2ecf20Sopenharmony_ci * matrix device's APM.
6598c2ecf20Sopenharmony_ci *
6608c2ecf20Sopenharmony_ci * Returns the number of bytes processed if the APID is valid; otherwise,
6618c2ecf20Sopenharmony_ci * returns one of the following errors:
6628c2ecf20Sopenharmony_ci *	-EINVAL if the APID is not a number
6638c2ecf20Sopenharmony_ci *	-ENODEV if the APID it exceeds the maximum value configured for the
6648c2ecf20Sopenharmony_ci *		system
6658c2ecf20Sopenharmony_ci */
6668c2ecf20Sopenharmony_cistatic ssize_t unassign_adapter_store(struct device *dev,
6678c2ecf20Sopenharmony_ci				      struct device_attribute *attr,
6688c2ecf20Sopenharmony_ci				      const char *buf, size_t count)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	int ret;
6718c2ecf20Sopenharmony_ci	unsigned long apid;
6728c2ecf20Sopenharmony_ci	struct mdev_device *mdev = mdev_from_dev(dev);
6738c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	/* If the guest is running, disallow un-assignment of adapter */
6768c2ecf20Sopenharmony_ci	if (matrix_mdev->kvm)
6778c2ecf20Sopenharmony_ci		return -EBUSY;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &apid);
6808c2ecf20Sopenharmony_ci	if (ret)
6818c2ecf20Sopenharmony_ci		return ret;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (apid > matrix_mdev->matrix.apm_max)
6848c2ecf20Sopenharmony_ci		return -ENODEV;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
6878c2ecf20Sopenharmony_ci	clear_bit_inv((unsigned long)apid, matrix_mdev->matrix.apm);
6888c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return count;
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(unassign_adapter);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic int
6958c2ecf20Sopenharmony_civfio_ap_mdev_verify_queues_reserved_for_apqi(struct ap_matrix_mdev *matrix_mdev,
6968c2ecf20Sopenharmony_ci					     unsigned long apqi)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	int ret;
6998c2ecf20Sopenharmony_ci	unsigned long apid;
7008c2ecf20Sopenharmony_ci	unsigned long nbits = matrix_mdev->matrix.apm_max + 1;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (find_first_bit_inv(matrix_mdev->matrix.apm, nbits) >= nbits)
7038c2ecf20Sopenharmony_ci		return vfio_ap_verify_queue_reserved(NULL, &apqi);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, nbits) {
7068c2ecf20Sopenharmony_ci		ret = vfio_ap_verify_queue_reserved(&apid, &apqi);
7078c2ecf20Sopenharmony_ci		if (ret)
7088c2ecf20Sopenharmony_ci			return ret;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	return 0;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci/**
7158c2ecf20Sopenharmony_ci * assign_domain_store
7168c2ecf20Sopenharmony_ci *
7178c2ecf20Sopenharmony_ci * @dev:	the matrix device
7188c2ecf20Sopenharmony_ci * @attr:	the mediated matrix device's assign_domain attribute
7198c2ecf20Sopenharmony_ci * @buf:	a buffer containing the AP queue index (APQI) of the domain to
7208c2ecf20Sopenharmony_ci *		be assigned
7218c2ecf20Sopenharmony_ci * @count:	the number of bytes in @buf
7228c2ecf20Sopenharmony_ci *
7238c2ecf20Sopenharmony_ci * Parses the APQI from @buf and sets the corresponding bit in the mediated
7248c2ecf20Sopenharmony_ci * matrix device's AQM.
7258c2ecf20Sopenharmony_ci *
7268c2ecf20Sopenharmony_ci * Returns the number of bytes processed if the APQI is valid; otherwise returns
7278c2ecf20Sopenharmony_ci * one of the following errors:
7288c2ecf20Sopenharmony_ci *
7298c2ecf20Sopenharmony_ci *	1. -EINVAL
7308c2ecf20Sopenharmony_ci *	   The APQI is not a valid number
7318c2ecf20Sopenharmony_ci *
7328c2ecf20Sopenharmony_ci *	2. -ENODEV
7338c2ecf20Sopenharmony_ci *	   The APQI exceeds the maximum value configured for the system
7348c2ecf20Sopenharmony_ci *
7358c2ecf20Sopenharmony_ci *	3. -EADDRNOTAVAIL
7368c2ecf20Sopenharmony_ci *	   An APQN derived from the cross product of the APQI being assigned
7378c2ecf20Sopenharmony_ci *	   and the APIDs previously assigned is not bound to the vfio_ap device
7388c2ecf20Sopenharmony_ci *	   driver; or, if no APIDs have yet been assigned, the APQI is not
7398c2ecf20Sopenharmony_ci *	   contained in an APQN bound to the vfio_ap device driver.
7408c2ecf20Sopenharmony_ci *
7418c2ecf20Sopenharmony_ci *	4. -EADDRINUSE
7428c2ecf20Sopenharmony_ci *	   An APQN derived from the cross product of the APQI being assigned
7438c2ecf20Sopenharmony_ci *	   and the APIDs previously assigned is being used by another mediated
7448c2ecf20Sopenharmony_ci *	   matrix device
7458c2ecf20Sopenharmony_ci */
7468c2ecf20Sopenharmony_cistatic ssize_t assign_domain_store(struct device *dev,
7478c2ecf20Sopenharmony_ci				   struct device_attribute *attr,
7488c2ecf20Sopenharmony_ci				   const char *buf, size_t count)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	int ret;
7518c2ecf20Sopenharmony_ci	unsigned long apqi;
7528c2ecf20Sopenharmony_ci	struct mdev_device *mdev = mdev_from_dev(dev);
7538c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
7548c2ecf20Sopenharmony_ci	unsigned long max_apqi = matrix_mdev->matrix.aqm_max;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	/* If the guest is running, disallow assignment of domain */
7578c2ecf20Sopenharmony_ci	if (matrix_mdev->kvm)
7588c2ecf20Sopenharmony_ci		return -EBUSY;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &apqi);
7618c2ecf20Sopenharmony_ci	if (ret)
7628c2ecf20Sopenharmony_ci		return ret;
7638c2ecf20Sopenharmony_ci	if (apqi > max_apqi)
7648c2ecf20Sopenharmony_ci		return -ENODEV;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	ret = vfio_ap_mdev_verify_queues_reserved_for_apqi(matrix_mdev, apqi);
7698c2ecf20Sopenharmony_ci	if (ret)
7708c2ecf20Sopenharmony_ci		goto done;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	set_bit_inv(apqi, matrix_mdev->matrix.aqm);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	ret = vfio_ap_mdev_verify_no_sharing(matrix_mdev);
7758c2ecf20Sopenharmony_ci	if (ret)
7768c2ecf20Sopenharmony_ci		goto share_err;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	ret = count;
7798c2ecf20Sopenharmony_ci	goto done;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cishare_err:
7828c2ecf20Sopenharmony_ci	clear_bit_inv(apqi, matrix_mdev->matrix.aqm);
7838c2ecf20Sopenharmony_cidone:
7848c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	return ret;
7878c2ecf20Sopenharmony_ci}
7888c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(assign_domain);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci/**
7928c2ecf20Sopenharmony_ci * unassign_domain_store
7938c2ecf20Sopenharmony_ci *
7948c2ecf20Sopenharmony_ci * @dev:	the matrix device
7958c2ecf20Sopenharmony_ci * @attr:	the mediated matrix device's unassign_domain attribute
7968c2ecf20Sopenharmony_ci * @buf:	a buffer containing the AP queue index (APQI) of the domain to
7978c2ecf20Sopenharmony_ci *		be unassigned
7988c2ecf20Sopenharmony_ci * @count:	the number of bytes in @buf
7998c2ecf20Sopenharmony_ci *
8008c2ecf20Sopenharmony_ci * Parses the APQI from @buf and clears the corresponding bit in the
8018c2ecf20Sopenharmony_ci * mediated matrix device's AQM.
8028c2ecf20Sopenharmony_ci *
8038c2ecf20Sopenharmony_ci * Returns the number of bytes processed if the APQI is valid; otherwise,
8048c2ecf20Sopenharmony_ci * returns one of the following errors:
8058c2ecf20Sopenharmony_ci *	-EINVAL if the APQI is not a number
8068c2ecf20Sopenharmony_ci *	-ENODEV if the APQI exceeds the maximum value configured for the system
8078c2ecf20Sopenharmony_ci */
8088c2ecf20Sopenharmony_cistatic ssize_t unassign_domain_store(struct device *dev,
8098c2ecf20Sopenharmony_ci				     struct device_attribute *attr,
8108c2ecf20Sopenharmony_ci				     const char *buf, size_t count)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	int ret;
8138c2ecf20Sopenharmony_ci	unsigned long apqi;
8148c2ecf20Sopenharmony_ci	struct mdev_device *mdev = mdev_from_dev(dev);
8158c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	/* If the guest is running, disallow un-assignment of domain */
8188c2ecf20Sopenharmony_ci	if (matrix_mdev->kvm)
8198c2ecf20Sopenharmony_ci		return -EBUSY;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &apqi);
8228c2ecf20Sopenharmony_ci	if (ret)
8238c2ecf20Sopenharmony_ci		return ret;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	if (apqi > matrix_mdev->matrix.aqm_max)
8268c2ecf20Sopenharmony_ci		return -ENODEV;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
8298c2ecf20Sopenharmony_ci	clear_bit_inv((unsigned long)apqi, matrix_mdev->matrix.aqm);
8308c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	return count;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(unassign_domain);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci/**
8378c2ecf20Sopenharmony_ci * assign_control_domain_store
8388c2ecf20Sopenharmony_ci *
8398c2ecf20Sopenharmony_ci * @dev:	the matrix device
8408c2ecf20Sopenharmony_ci * @attr:	the mediated matrix device's assign_control_domain attribute
8418c2ecf20Sopenharmony_ci * @buf:	a buffer containing the domain ID to be assigned
8428c2ecf20Sopenharmony_ci * @count:	the number of bytes in @buf
8438c2ecf20Sopenharmony_ci *
8448c2ecf20Sopenharmony_ci * Parses the domain ID from @buf and sets the corresponding bit in the mediated
8458c2ecf20Sopenharmony_ci * matrix device's ADM.
8468c2ecf20Sopenharmony_ci *
8478c2ecf20Sopenharmony_ci * Returns the number of bytes processed if the domain ID is valid; otherwise,
8488c2ecf20Sopenharmony_ci * returns one of the following errors:
8498c2ecf20Sopenharmony_ci *	-EINVAL if the ID is not a number
8508c2ecf20Sopenharmony_ci *	-ENODEV if the ID exceeds the maximum value configured for the system
8518c2ecf20Sopenharmony_ci */
8528c2ecf20Sopenharmony_cistatic ssize_t assign_control_domain_store(struct device *dev,
8538c2ecf20Sopenharmony_ci					   struct device_attribute *attr,
8548c2ecf20Sopenharmony_ci					   const char *buf, size_t count)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	int ret;
8578c2ecf20Sopenharmony_ci	unsigned long id;
8588c2ecf20Sopenharmony_ci	struct mdev_device *mdev = mdev_from_dev(dev);
8598c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	/* If the guest is running, disallow assignment of control domain */
8628c2ecf20Sopenharmony_ci	if (matrix_mdev->kvm)
8638c2ecf20Sopenharmony_ci		return -EBUSY;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &id);
8668c2ecf20Sopenharmony_ci	if (ret)
8678c2ecf20Sopenharmony_ci		return ret;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	if (id > matrix_mdev->matrix.adm_max)
8708c2ecf20Sopenharmony_ci		return -ENODEV;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/* Set the bit in the ADM (bitmask) corresponding to the AP control
8738c2ecf20Sopenharmony_ci	 * domain number (id). The bits in the mask, from most significant to
8748c2ecf20Sopenharmony_ci	 * least significant, correspond to IDs 0 up to the one less than the
8758c2ecf20Sopenharmony_ci	 * number of control domains that can be assigned.
8768c2ecf20Sopenharmony_ci	 */
8778c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
8788c2ecf20Sopenharmony_ci	set_bit_inv(id, matrix_mdev->matrix.adm);
8798c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	return count;
8828c2ecf20Sopenharmony_ci}
8838c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(assign_control_domain);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci/**
8868c2ecf20Sopenharmony_ci * unassign_control_domain_store
8878c2ecf20Sopenharmony_ci *
8888c2ecf20Sopenharmony_ci * @dev:	the matrix device
8898c2ecf20Sopenharmony_ci * @attr:	the mediated matrix device's unassign_control_domain attribute
8908c2ecf20Sopenharmony_ci * @buf:	a buffer containing the domain ID to be unassigned
8918c2ecf20Sopenharmony_ci * @count:	the number of bytes in @buf
8928c2ecf20Sopenharmony_ci *
8938c2ecf20Sopenharmony_ci * Parses the domain ID from @buf and clears the corresponding bit in the
8948c2ecf20Sopenharmony_ci * mediated matrix device's ADM.
8958c2ecf20Sopenharmony_ci *
8968c2ecf20Sopenharmony_ci * Returns the number of bytes processed if the domain ID is valid; otherwise,
8978c2ecf20Sopenharmony_ci * returns one of the following errors:
8988c2ecf20Sopenharmony_ci *	-EINVAL if the ID is not a number
8998c2ecf20Sopenharmony_ci *	-ENODEV if the ID exceeds the maximum value configured for the system
9008c2ecf20Sopenharmony_ci */
9018c2ecf20Sopenharmony_cistatic ssize_t unassign_control_domain_store(struct device *dev,
9028c2ecf20Sopenharmony_ci					     struct device_attribute *attr,
9038c2ecf20Sopenharmony_ci					     const char *buf, size_t count)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	int ret;
9068c2ecf20Sopenharmony_ci	unsigned long domid;
9078c2ecf20Sopenharmony_ci	struct mdev_device *mdev = mdev_from_dev(dev);
9088c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
9098c2ecf20Sopenharmony_ci	unsigned long max_domid =  matrix_mdev->matrix.adm_max;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	/* If the guest is running, disallow un-assignment of control domain */
9128c2ecf20Sopenharmony_ci	if (matrix_mdev->kvm)
9138c2ecf20Sopenharmony_ci		return -EBUSY;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &domid);
9168c2ecf20Sopenharmony_ci	if (ret)
9178c2ecf20Sopenharmony_ci		return ret;
9188c2ecf20Sopenharmony_ci	if (domid > max_domid)
9198c2ecf20Sopenharmony_ci		return -ENODEV;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
9228c2ecf20Sopenharmony_ci	clear_bit_inv(domid, matrix_mdev->matrix.adm);
9238c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	return count;
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(unassign_control_domain);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic ssize_t control_domains_show(struct device *dev,
9308c2ecf20Sopenharmony_ci				    struct device_attribute *dev_attr,
9318c2ecf20Sopenharmony_ci				    char *buf)
9328c2ecf20Sopenharmony_ci{
9338c2ecf20Sopenharmony_ci	unsigned long id;
9348c2ecf20Sopenharmony_ci	int nchars = 0;
9358c2ecf20Sopenharmony_ci	int n;
9368c2ecf20Sopenharmony_ci	char *bufpos = buf;
9378c2ecf20Sopenharmony_ci	struct mdev_device *mdev = mdev_from_dev(dev);
9388c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
9398c2ecf20Sopenharmony_ci	unsigned long max_domid = matrix_mdev->matrix.adm_max;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
9428c2ecf20Sopenharmony_ci	for_each_set_bit_inv(id, matrix_mdev->matrix.adm, max_domid + 1) {
9438c2ecf20Sopenharmony_ci		n = sprintf(bufpos, "%04lx\n", id);
9448c2ecf20Sopenharmony_ci		bufpos += n;
9458c2ecf20Sopenharmony_ci		nchars += n;
9468c2ecf20Sopenharmony_ci	}
9478c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	return nchars;
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(control_domains);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_cistatic ssize_t matrix_show(struct device *dev, struct device_attribute *attr,
9548c2ecf20Sopenharmony_ci			   char *buf)
9558c2ecf20Sopenharmony_ci{
9568c2ecf20Sopenharmony_ci	struct mdev_device *mdev = mdev_from_dev(dev);
9578c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
9588c2ecf20Sopenharmony_ci	char *bufpos = buf;
9598c2ecf20Sopenharmony_ci	unsigned long apid;
9608c2ecf20Sopenharmony_ci	unsigned long apqi;
9618c2ecf20Sopenharmony_ci	unsigned long apid1;
9628c2ecf20Sopenharmony_ci	unsigned long apqi1;
9638c2ecf20Sopenharmony_ci	unsigned long napm_bits = matrix_mdev->matrix.apm_max + 1;
9648c2ecf20Sopenharmony_ci	unsigned long naqm_bits = matrix_mdev->matrix.aqm_max + 1;
9658c2ecf20Sopenharmony_ci	int nchars = 0;
9668c2ecf20Sopenharmony_ci	int n;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	apid1 = find_first_bit_inv(matrix_mdev->matrix.apm, napm_bits);
9698c2ecf20Sopenharmony_ci	apqi1 = find_first_bit_inv(matrix_mdev->matrix.aqm, naqm_bits);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if ((apid1 < napm_bits) && (apqi1 < naqm_bits)) {
9748c2ecf20Sopenharmony_ci		for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, napm_bits) {
9758c2ecf20Sopenharmony_ci			for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm,
9768c2ecf20Sopenharmony_ci					     naqm_bits) {
9778c2ecf20Sopenharmony_ci				n = sprintf(bufpos, "%02lx.%04lx\n", apid,
9788c2ecf20Sopenharmony_ci					    apqi);
9798c2ecf20Sopenharmony_ci				bufpos += n;
9808c2ecf20Sopenharmony_ci				nchars += n;
9818c2ecf20Sopenharmony_ci			}
9828c2ecf20Sopenharmony_ci		}
9838c2ecf20Sopenharmony_ci	} else if (apid1 < napm_bits) {
9848c2ecf20Sopenharmony_ci		for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, napm_bits) {
9858c2ecf20Sopenharmony_ci			n = sprintf(bufpos, "%02lx.\n", apid);
9868c2ecf20Sopenharmony_ci			bufpos += n;
9878c2ecf20Sopenharmony_ci			nchars += n;
9888c2ecf20Sopenharmony_ci		}
9898c2ecf20Sopenharmony_ci	} else if (apqi1 < naqm_bits) {
9908c2ecf20Sopenharmony_ci		for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, naqm_bits) {
9918c2ecf20Sopenharmony_ci			n = sprintf(bufpos, ".%04lx\n", apqi);
9928c2ecf20Sopenharmony_ci			bufpos += n;
9938c2ecf20Sopenharmony_ci			nchars += n;
9948c2ecf20Sopenharmony_ci		}
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return nchars;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(matrix);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_cistatic struct attribute *vfio_ap_mdev_attrs[] = {
10048c2ecf20Sopenharmony_ci	&dev_attr_assign_adapter.attr,
10058c2ecf20Sopenharmony_ci	&dev_attr_unassign_adapter.attr,
10068c2ecf20Sopenharmony_ci	&dev_attr_assign_domain.attr,
10078c2ecf20Sopenharmony_ci	&dev_attr_unassign_domain.attr,
10088c2ecf20Sopenharmony_ci	&dev_attr_assign_control_domain.attr,
10098c2ecf20Sopenharmony_ci	&dev_attr_unassign_control_domain.attr,
10108c2ecf20Sopenharmony_ci	&dev_attr_control_domains.attr,
10118c2ecf20Sopenharmony_ci	&dev_attr_matrix.attr,
10128c2ecf20Sopenharmony_ci	NULL,
10138c2ecf20Sopenharmony_ci};
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_cistatic struct attribute_group vfio_ap_mdev_attr_group = {
10168c2ecf20Sopenharmony_ci	.attrs = vfio_ap_mdev_attrs
10178c2ecf20Sopenharmony_ci};
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic const struct attribute_group *vfio_ap_mdev_attr_groups[] = {
10208c2ecf20Sopenharmony_ci	&vfio_ap_mdev_attr_group,
10218c2ecf20Sopenharmony_ci	NULL
10228c2ecf20Sopenharmony_ci};
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci/**
10258c2ecf20Sopenharmony_ci * vfio_ap_mdev_set_kvm
10268c2ecf20Sopenharmony_ci *
10278c2ecf20Sopenharmony_ci * @matrix_mdev: a mediated matrix device
10288c2ecf20Sopenharmony_ci * @kvm: reference to KVM instance
10298c2ecf20Sopenharmony_ci *
10308c2ecf20Sopenharmony_ci * Verifies no other mediated matrix device has @kvm and sets a reference to
10318c2ecf20Sopenharmony_ci * it in @matrix_mdev->kvm.
10328c2ecf20Sopenharmony_ci *
10338c2ecf20Sopenharmony_ci * Return 0 if no other mediated matrix device has a reference to @kvm;
10348c2ecf20Sopenharmony_ci * otherwise, returns an -EPERM.
10358c2ecf20Sopenharmony_ci */
10368c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
10378c2ecf20Sopenharmony_ci				struct kvm *kvm)
10388c2ecf20Sopenharmony_ci{
10398c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *m;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	list_for_each_entry(m, &matrix_dev->mdev_list, node) {
10448c2ecf20Sopenharmony_ci		if ((m != matrix_mdev) && (m->kvm == kvm)) {
10458c2ecf20Sopenharmony_ci			mutex_unlock(&matrix_dev->lock);
10468c2ecf20Sopenharmony_ci			return -EPERM;
10478c2ecf20Sopenharmony_ci		}
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	matrix_mdev->kvm = kvm;
10518c2ecf20Sopenharmony_ci	kvm_get_kvm(kvm);
10528c2ecf20Sopenharmony_ci	kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
10538c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	return 0;
10568c2ecf20Sopenharmony_ci}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci/*
10598c2ecf20Sopenharmony_ci * vfio_ap_mdev_iommu_notifier: IOMMU notifier callback
10608c2ecf20Sopenharmony_ci *
10618c2ecf20Sopenharmony_ci * @nb: The notifier block
10628c2ecf20Sopenharmony_ci * @action: Action to be taken
10638c2ecf20Sopenharmony_ci * @data: data associated with the request
10648c2ecf20Sopenharmony_ci *
10658c2ecf20Sopenharmony_ci * For an UNMAP request, unpin the guest IOVA (the NIB guest address we
10668c2ecf20Sopenharmony_ci * pinned before). Other requests are ignored.
10678c2ecf20Sopenharmony_ci *
10688c2ecf20Sopenharmony_ci */
10698c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_iommu_notifier(struct notifier_block *nb,
10708c2ecf20Sopenharmony_ci				       unsigned long action, void *data)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	matrix_mdev = container_of(nb, struct ap_matrix_mdev, iommu_notifier);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
10778c2ecf20Sopenharmony_ci		struct vfio_iommu_type1_dma_unmap *unmap = data;
10788c2ecf20Sopenharmony_ci		unsigned long g_pfn = unmap->iova >> PAGE_SHIFT;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci		vfio_unpin_pages(mdev_dev(matrix_mdev->mdev), &g_pfn, 1);
10818c2ecf20Sopenharmony_ci		return NOTIFY_OK;
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
10858c2ecf20Sopenharmony_ci}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_group_notifier(struct notifier_block *nb,
10888c2ecf20Sopenharmony_ci				       unsigned long action, void *data)
10898c2ecf20Sopenharmony_ci{
10908c2ecf20Sopenharmony_ci	int ret;
10918c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	if (action != VFIO_GROUP_NOTIFY_SET_KVM)
10948c2ecf20Sopenharmony_ci		return NOTIFY_OK;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	matrix_mdev = container_of(nb, struct ap_matrix_mdev, group_notifier);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	if (!data) {
10998c2ecf20Sopenharmony_ci		matrix_mdev->kvm = NULL;
11008c2ecf20Sopenharmony_ci		return NOTIFY_OK;
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	ret = vfio_ap_mdev_set_kvm(matrix_mdev, data);
11048c2ecf20Sopenharmony_ci	if (ret)
11058c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* If there is no CRYCB pointer, then we can't copy the masks */
11088c2ecf20Sopenharmony_ci	if (!matrix_mdev->kvm->arch.crypto.crycbd)
11098c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	kvm_arch_crypto_set_masks(matrix_mdev->kvm, matrix_mdev->matrix.apm,
11128c2ecf20Sopenharmony_ci				  matrix_mdev->matrix.aqm,
11138c2ecf20Sopenharmony_ci				  matrix_mdev->matrix.adm);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	return NOTIFY_OK;
11168c2ecf20Sopenharmony_ci}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_cistatic struct vfio_ap_queue *vfio_ap_find_queue(int apqn)
11198c2ecf20Sopenharmony_ci{
11208c2ecf20Sopenharmony_ci	struct device *dev;
11218c2ecf20Sopenharmony_ci	struct vfio_ap_queue *q = NULL;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	dev = driver_find_device(&matrix_dev->vfio_ap_drv->driver, NULL,
11248c2ecf20Sopenharmony_ci				 &apqn, match_apqn);
11258c2ecf20Sopenharmony_ci	if (dev) {
11268c2ecf20Sopenharmony_ci		q = dev_get_drvdata(dev);
11278c2ecf20Sopenharmony_ci		put_device(dev);
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	return q;
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ciint vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q,
11348c2ecf20Sopenharmony_ci			     unsigned int retry)
11358c2ecf20Sopenharmony_ci{
11368c2ecf20Sopenharmony_ci	struct ap_queue_status status;
11378c2ecf20Sopenharmony_ci	int ret;
11388c2ecf20Sopenharmony_ci	int retry2 = 2;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	if (!q)
11418c2ecf20Sopenharmony_ci		return 0;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ciretry_zapq:
11448c2ecf20Sopenharmony_ci	status = ap_zapq(q->apqn);
11458c2ecf20Sopenharmony_ci	switch (status.response_code) {
11468c2ecf20Sopenharmony_ci	case AP_RESPONSE_NORMAL:
11478c2ecf20Sopenharmony_ci		ret = 0;
11488c2ecf20Sopenharmony_ci		break;
11498c2ecf20Sopenharmony_ci	case AP_RESPONSE_RESET_IN_PROGRESS:
11508c2ecf20Sopenharmony_ci		if (retry--) {
11518c2ecf20Sopenharmony_ci			msleep(20);
11528c2ecf20Sopenharmony_ci			goto retry_zapq;
11538c2ecf20Sopenharmony_ci		}
11548c2ecf20Sopenharmony_ci		ret = -EBUSY;
11558c2ecf20Sopenharmony_ci		break;
11568c2ecf20Sopenharmony_ci	case AP_RESPONSE_Q_NOT_AVAIL:
11578c2ecf20Sopenharmony_ci	case AP_RESPONSE_DECONFIGURED:
11588c2ecf20Sopenharmony_ci	case AP_RESPONSE_CHECKSTOPPED:
11598c2ecf20Sopenharmony_ci		WARN_ON_ONCE(status.irq_enabled);
11608c2ecf20Sopenharmony_ci		ret = -EBUSY;
11618c2ecf20Sopenharmony_ci		goto free_resources;
11628c2ecf20Sopenharmony_ci	default:
11638c2ecf20Sopenharmony_ci		/* things are really broken, give up */
11648c2ecf20Sopenharmony_ci		WARN(true, "PQAP/ZAPQ completed with invalid rc (%x)\n",
11658c2ecf20Sopenharmony_ci		     status.response_code);
11668c2ecf20Sopenharmony_ci		return -EIO;
11678c2ecf20Sopenharmony_ci	}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	/* wait for the reset to take effect */
11708c2ecf20Sopenharmony_ci	while (retry2--) {
11718c2ecf20Sopenharmony_ci		if (status.queue_empty && !status.irq_enabled)
11728c2ecf20Sopenharmony_ci			break;
11738c2ecf20Sopenharmony_ci		msleep(20);
11748c2ecf20Sopenharmony_ci		status = ap_tapq(q->apqn, NULL);
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci	WARN_ON_ONCE(retry2 <= 0);
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_cifree_resources:
11798c2ecf20Sopenharmony_ci	vfio_ap_free_aqic_resources(q);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	return ret;
11828c2ecf20Sopenharmony_ci}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_reset_queues(struct mdev_device *mdev)
11858c2ecf20Sopenharmony_ci{
11868c2ecf20Sopenharmony_ci	int ret;
11878c2ecf20Sopenharmony_ci	int rc = 0;
11888c2ecf20Sopenharmony_ci	unsigned long apid, apqi;
11898c2ecf20Sopenharmony_ci	struct vfio_ap_queue *q;
11908c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	for_each_set_bit_inv(apid, matrix_mdev->matrix.apm,
11938c2ecf20Sopenharmony_ci			     matrix_mdev->matrix.apm_max + 1) {
11948c2ecf20Sopenharmony_ci		for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm,
11958c2ecf20Sopenharmony_ci				     matrix_mdev->matrix.aqm_max + 1) {
11968c2ecf20Sopenharmony_ci			q = vfio_ap_find_queue(AP_MKQID(apid, apqi));
11978c2ecf20Sopenharmony_ci			ret = vfio_ap_mdev_reset_queue(q, 1);
11988c2ecf20Sopenharmony_ci			/*
11998c2ecf20Sopenharmony_ci			 * Regardless whether a queue turns out to be busy, or
12008c2ecf20Sopenharmony_ci			 * is not operational, we need to continue resetting
12018c2ecf20Sopenharmony_ci			 * the remaining queues.
12028c2ecf20Sopenharmony_ci			 */
12038c2ecf20Sopenharmony_ci			if (ret)
12048c2ecf20Sopenharmony_ci				rc = ret;
12058c2ecf20Sopenharmony_ci		}
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	return rc;
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_open(struct mdev_device *mdev)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
12148c2ecf20Sopenharmony_ci	unsigned long events;
12158c2ecf20Sopenharmony_ci	int ret;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	if (!try_module_get(THIS_MODULE))
12198c2ecf20Sopenharmony_ci		return -ENODEV;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	matrix_mdev->group_notifier.notifier_call = vfio_ap_mdev_group_notifier;
12228c2ecf20Sopenharmony_ci	events = VFIO_GROUP_NOTIFY_SET_KVM;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	ret = vfio_register_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
12258c2ecf20Sopenharmony_ci				     &events, &matrix_mdev->group_notifier);
12268c2ecf20Sopenharmony_ci	if (ret) {
12278c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
12288c2ecf20Sopenharmony_ci		return ret;
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	matrix_mdev->iommu_notifier.notifier_call = vfio_ap_mdev_iommu_notifier;
12328c2ecf20Sopenharmony_ci	events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
12338c2ecf20Sopenharmony_ci	ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
12348c2ecf20Sopenharmony_ci				     &events, &matrix_mdev->iommu_notifier);
12358c2ecf20Sopenharmony_ci	if (!ret)
12368c2ecf20Sopenharmony_ci		return ret;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
12398c2ecf20Sopenharmony_ci				 &matrix_mdev->group_notifier);
12408c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
12418c2ecf20Sopenharmony_ci	return ret;
12428c2ecf20Sopenharmony_ci}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cistatic void vfio_ap_mdev_release(struct mdev_device *mdev)
12458c2ecf20Sopenharmony_ci{
12468c2ecf20Sopenharmony_ci	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
12498c2ecf20Sopenharmony_ci	if (matrix_mdev->kvm) {
12508c2ecf20Sopenharmony_ci		kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
12518c2ecf20Sopenharmony_ci		matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
12528c2ecf20Sopenharmony_ci		vfio_ap_mdev_reset_queues(mdev);
12538c2ecf20Sopenharmony_ci		kvm_put_kvm(matrix_mdev->kvm);
12548c2ecf20Sopenharmony_ci		matrix_mdev->kvm = NULL;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
12598c2ecf20Sopenharmony_ci				 &matrix_mdev->iommu_notifier);
12608c2ecf20Sopenharmony_ci	vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
12618c2ecf20Sopenharmony_ci				 &matrix_mdev->group_notifier);
12628c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
12638c2ecf20Sopenharmony_ci}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic int vfio_ap_mdev_get_device_info(unsigned long arg)
12668c2ecf20Sopenharmony_ci{
12678c2ecf20Sopenharmony_ci	unsigned long minsz;
12688c2ecf20Sopenharmony_ci	struct vfio_device_info info;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	minsz = offsetofend(struct vfio_device_info, num_irqs);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	if (copy_from_user(&info, (void __user *)arg, minsz))
12738c2ecf20Sopenharmony_ci		return -EFAULT;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	if (info.argsz < minsz)
12768c2ecf20Sopenharmony_ci		return -EINVAL;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	info.flags = VFIO_DEVICE_FLAGS_AP | VFIO_DEVICE_FLAGS_RESET;
12798c2ecf20Sopenharmony_ci	info.num_regions = 0;
12808c2ecf20Sopenharmony_ci	info.num_irqs = 0;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
12838c2ecf20Sopenharmony_ci}
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_cistatic ssize_t vfio_ap_mdev_ioctl(struct mdev_device *mdev,
12868c2ecf20Sopenharmony_ci				    unsigned int cmd, unsigned long arg)
12878c2ecf20Sopenharmony_ci{
12888c2ecf20Sopenharmony_ci	int ret;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	mutex_lock(&matrix_dev->lock);
12918c2ecf20Sopenharmony_ci	switch (cmd) {
12928c2ecf20Sopenharmony_ci	case VFIO_DEVICE_GET_INFO:
12938c2ecf20Sopenharmony_ci		ret = vfio_ap_mdev_get_device_info(arg);
12948c2ecf20Sopenharmony_ci		break;
12958c2ecf20Sopenharmony_ci	case VFIO_DEVICE_RESET:
12968c2ecf20Sopenharmony_ci		ret = vfio_ap_mdev_reset_queues(mdev);
12978c2ecf20Sopenharmony_ci		break;
12988c2ecf20Sopenharmony_ci	default:
12998c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
13008c2ecf20Sopenharmony_ci		break;
13018c2ecf20Sopenharmony_ci	}
13028c2ecf20Sopenharmony_ci	mutex_unlock(&matrix_dev->lock);
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	return ret;
13058c2ecf20Sopenharmony_ci}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_cistatic const struct mdev_parent_ops vfio_ap_matrix_ops = {
13088c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
13098c2ecf20Sopenharmony_ci	.supported_type_groups	= vfio_ap_mdev_type_groups,
13108c2ecf20Sopenharmony_ci	.mdev_attr_groups	= vfio_ap_mdev_attr_groups,
13118c2ecf20Sopenharmony_ci	.create			= vfio_ap_mdev_create,
13128c2ecf20Sopenharmony_ci	.remove			= vfio_ap_mdev_remove,
13138c2ecf20Sopenharmony_ci	.open			= vfio_ap_mdev_open,
13148c2ecf20Sopenharmony_ci	.release		= vfio_ap_mdev_release,
13158c2ecf20Sopenharmony_ci	.ioctl			= vfio_ap_mdev_ioctl,
13168c2ecf20Sopenharmony_ci};
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ciint vfio_ap_mdev_register(void)
13198c2ecf20Sopenharmony_ci{
13208c2ecf20Sopenharmony_ci	atomic_set(&matrix_dev->available_instances, MAX_ZDEV_ENTRIES_EXT);
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	return mdev_register_device(&matrix_dev->device, &vfio_ap_matrix_ops);
13238c2ecf20Sopenharmony_ci}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_civoid vfio_ap_mdev_unregister(void)
13268c2ecf20Sopenharmony_ci{
13278c2ecf20Sopenharmony_ci	mdev_unregister_device(&matrix_dev->device);
13288c2ecf20Sopenharmony_ci}
1329