root/drivers/vfio/pci/vfio_pci_zdev.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * VFIO ZPCI devices support
 *
 * Copyright (C) IBM Corp. 2020.  All rights reserved.
 *      Author(s): Pierre Morel <pmorel@linux.ibm.com>
 *                 Matthew Rosato <mjrosato@linux.ibm.com>
 */
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <linux/vfio.h>
#include <linux/vfio_zdev.h>
#include <linux/kvm_host.h>
#include <asm/pci_clp.h>
#include <asm/pci_io.h>

#include "vfio_pci_priv.h"

/*
 * Add the Base PCI Function information to the device info region.
 */
static int zpci_base_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps)
{
        struct vfio_device_info_cap_zpci_base cap = {
                .header.id = VFIO_DEVICE_INFO_CAP_ZPCI_BASE,
                .header.version = 2,
                .start_dma = zdev->start_dma,
                .end_dma = zdev->end_dma,
                .pchid = zdev->pchid,
                .vfn = zdev->vfn,
                .fmb_length = zdev->fmb_length,
                .pft = zdev->pft,
                .gid = zdev->pfgid,
                .fh = zdev->fh
        };

        return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
}

/*
 * Add the Base PCI Function Group information to the device info region.
 */
static int zpci_group_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps)
{
        struct vfio_device_info_cap_zpci_group cap = {
                .header.id = VFIO_DEVICE_INFO_CAP_ZPCI_GROUP,
                .header.version = 2,
                .dasm = zdev->dma_mask,
                .msi_addr = zdev->msi_addr,
                .flags = VFIO_DEVICE_INFO_ZPCI_FLAG_REFRESH,
                .mui = zdev->fmb_update,
                .noi = zdev->max_msi,
                .maxstbl = ZPCI_MAX_WRITE_SIZE,
                .version = zdev->version,
                .reserved = 0,
                .imaxstbl = zdev->maxstbl
        };

        return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
}

/*
 * Add the device utility string to the device info region.
 */
static int zpci_util_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps)
{
        struct vfio_device_info_cap_zpci_util *cap;
        int cap_size = sizeof(*cap) + CLP_UTIL_STR_LEN;
        int ret;

        cap = kmalloc(cap_size, GFP_KERNEL);
        if (!cap)
                return -ENOMEM;

        cap->header.id = VFIO_DEVICE_INFO_CAP_ZPCI_UTIL;
        cap->header.version = 1;
        cap->size = CLP_UTIL_STR_LEN;
        memcpy(cap->util_str, zdev->util_str, cap->size);

        ret = vfio_info_add_capability(caps, &cap->header, cap_size);

        kfree(cap);

        return ret;
}

/*
 * Add the function path string to the device info region.
 */
static int zpci_pfip_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps)
{
        struct vfio_device_info_cap_zpci_pfip *cap;
        int cap_size = sizeof(*cap) + CLP_PFIP_NR_SEGMENTS;
        int ret;

        cap = kmalloc(cap_size, GFP_KERNEL);
        if (!cap)
                return -ENOMEM;

        cap->header.id = VFIO_DEVICE_INFO_CAP_ZPCI_PFIP;
        cap->header.version = 1;
        cap->size = CLP_PFIP_NR_SEGMENTS;
        memcpy(cap->pfip, zdev->pfip, cap->size);

        ret = vfio_info_add_capability(caps, &cap->header, cap_size);

        kfree(cap);

        return ret;
}

/*
 * Add all supported capabilities to the VFIO_DEVICE_GET_INFO capability chain.
 */
int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev,
                                struct vfio_info_cap *caps)
{
        struct zpci_dev *zdev = to_zpci(vdev->pdev);
        int ret;

        if (!zdev)
                return -ENODEV;

        ret = zpci_base_cap(zdev, caps);
        if (ret)
                return ret;

        ret = zpci_group_cap(zdev, caps);
        if (ret)
                return ret;

        if (zdev->util_str_avail) {
                ret = zpci_util_cap(zdev, caps);
                if (ret)
                        return ret;
        }

        ret = zpci_pfip_cap(zdev, caps);

        return ret;
}

int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev)
{
        struct zpci_dev *zdev = to_zpci(vdev->pdev);

        if (!zdev)
                return -ENODEV;

        if (!vdev->vdev.kvm)
                return 0;

        if (zpci_kvm_hook.kvm_register)
                return zpci_kvm_hook.kvm_register(zdev, vdev->vdev.kvm);

        return -ENOENT;
}

void vfio_pci_zdev_close_device(struct vfio_pci_core_device *vdev)
{
        struct zpci_dev *zdev = to_zpci(vdev->pdev);

        if (!zdev || !vdev->vdev.kvm)
                return;

        if (zpci_kvm_hook.kvm_unregister)
                zpci_kvm_hook.kvm_unregister(zdev);
}