mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.1 KiB
138 lines
3.1 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* VFIO ZPCI devices support |
|
* |
|
* Copyright (C) IBM Corp. 2020. All rights reserved. |
|
* Author(s): Pierre Morel <[email protected]> |
|
* Matthew Rosato <[email protected]> |
|
*/ |
|
#include <linux/io.h> |
|
#include <linux/pci.h> |
|
#include <linux/uaccess.h> |
|
#include <linux/vfio.h> |
|
#include <linux/vfio_zdev.h> |
|
#include <asm/pci_clp.h> |
|
#include <asm/pci_io.h> |
|
|
|
#include <linux/vfio_pci_core.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 = 1, |
|
.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 |
|
}; |
|
|
|
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 = 1, |
|
.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 |
|
}; |
|
|
|
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; |
|
}
|
|
|