1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * VFIO ZPCI devices support |
4 | * |
5 | * Copyright (C) IBM Corp. 2020. All rights reserved. |
6 | * Author(s): Pierre Morel <pmorel@linux.ibm.com> |
7 | * Matthew Rosato <mjrosato@linux.ibm.com> |
8 | */ |
9 | #include <linux/io.h> |
10 | #include <linux/pci.h> |
11 | #include <linux/uaccess.h> |
12 | #include <linux/vfio.h> |
13 | #include <linux/vfio_zdev.h> |
14 | #include <linux/kvm_host.h> |
15 | #include <asm/pci_clp.h> |
16 | #include <asm/pci_io.h> |
17 | |
18 | #include "vfio_pci_priv.h" |
19 | |
20 | /* |
21 | * Add the Base PCI Function information to the device info region. |
22 | */ |
23 | static int zpci_base_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps) |
24 | { |
25 | struct vfio_device_info_cap_zpci_base cap = { |
26 | .header.id = VFIO_DEVICE_INFO_CAP_ZPCI_BASE, |
27 | .header.version = 2, |
28 | .start_dma = zdev->start_dma, |
29 | .end_dma = zdev->end_dma, |
30 | .pchid = zdev->pchid, |
31 | .vfn = zdev->vfn, |
32 | .fmb_length = zdev->fmb_length, |
33 | .pft = zdev->pft, |
34 | .gid = zdev->pfgid, |
35 | .fh = zdev->fh |
36 | }; |
37 | |
38 | return vfio_info_add_capability(caps, cap: &cap.header, size: sizeof(cap)); |
39 | } |
40 | |
41 | /* |
42 | * Add the Base PCI Function Group information to the device info region. |
43 | */ |
44 | static int zpci_group_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps) |
45 | { |
46 | struct vfio_device_info_cap_zpci_group cap = { |
47 | .header.id = VFIO_DEVICE_INFO_CAP_ZPCI_GROUP, |
48 | .header.version = 2, |
49 | .dasm = zdev->dma_mask, |
50 | .msi_addr = zdev->msi_addr, |
51 | .flags = VFIO_DEVICE_INFO_ZPCI_FLAG_REFRESH, |
52 | .mui = zdev->fmb_update, |
53 | .noi = zdev->max_msi, |
54 | .maxstbl = ZPCI_MAX_WRITE_SIZE, |
55 | .version = zdev->version, |
56 | .reserved = 0, |
57 | .imaxstbl = zdev->maxstbl |
58 | }; |
59 | |
60 | return vfio_info_add_capability(caps, cap: &cap.header, size: sizeof(cap)); |
61 | } |
62 | |
63 | /* |
64 | * Add the device utility string to the device info region. |
65 | */ |
66 | static int zpci_util_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps) |
67 | { |
68 | struct vfio_device_info_cap_zpci_util *cap; |
69 | int cap_size = sizeof(*cap) + CLP_UTIL_STR_LEN; |
70 | int ret; |
71 | |
72 | cap = kmalloc(size: cap_size, GFP_KERNEL); |
73 | if (!cap) |
74 | return -ENOMEM; |
75 | |
76 | cap->header.id = VFIO_DEVICE_INFO_CAP_ZPCI_UTIL; |
77 | cap->header.version = 1; |
78 | cap->size = CLP_UTIL_STR_LEN; |
79 | memcpy(cap->util_str, zdev->util_str, cap->size); |
80 | |
81 | ret = vfio_info_add_capability(caps, cap: &cap->header, size: cap_size); |
82 | |
83 | kfree(objp: cap); |
84 | |
85 | return ret; |
86 | } |
87 | |
88 | /* |
89 | * Add the function path string to the device info region. |
90 | */ |
91 | static int zpci_pfip_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps) |
92 | { |
93 | struct vfio_device_info_cap_zpci_pfip *cap; |
94 | int cap_size = sizeof(*cap) + CLP_PFIP_NR_SEGMENTS; |
95 | int ret; |
96 | |
97 | cap = kmalloc(size: cap_size, GFP_KERNEL); |
98 | if (!cap) |
99 | return -ENOMEM; |
100 | |
101 | cap->header.id = VFIO_DEVICE_INFO_CAP_ZPCI_PFIP; |
102 | cap->header.version = 1; |
103 | cap->size = CLP_PFIP_NR_SEGMENTS; |
104 | memcpy(cap->pfip, zdev->pfip, cap->size); |
105 | |
106 | ret = vfio_info_add_capability(caps, cap: &cap->header, size: cap_size); |
107 | |
108 | kfree(objp: cap); |
109 | |
110 | return ret; |
111 | } |
112 | |
113 | /* |
114 | * Add all supported capabilities to the VFIO_DEVICE_GET_INFO capability chain. |
115 | */ |
116 | int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev, |
117 | struct vfio_info_cap *caps) |
118 | { |
119 | struct zpci_dev *zdev = to_zpci(vdev->pdev); |
120 | int ret; |
121 | |
122 | if (!zdev) |
123 | return -ENODEV; |
124 | |
125 | ret = zpci_base_cap(zdev, caps); |
126 | if (ret) |
127 | return ret; |
128 | |
129 | ret = zpci_group_cap(zdev, caps); |
130 | if (ret) |
131 | return ret; |
132 | |
133 | if (zdev->util_str_avail) { |
134 | ret = zpci_util_cap(zdev, caps); |
135 | if (ret) |
136 | return ret; |
137 | } |
138 | |
139 | ret = zpci_pfip_cap(zdev, caps); |
140 | |
141 | return ret; |
142 | } |
143 | |
144 | int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev) |
145 | { |
146 | struct zpci_dev *zdev = to_zpci(vdev->pdev); |
147 | |
148 | if (!zdev) |
149 | return -ENODEV; |
150 | |
151 | if (!vdev->vdev.kvm) |
152 | return 0; |
153 | |
154 | if (zpci_kvm_hook.kvm_register) |
155 | return zpci_kvm_hook.kvm_register(zdev, vdev->vdev.kvm); |
156 | |
157 | return -ENOENT; |
158 | } |
159 | |
160 | void vfio_pci_zdev_close_device(struct vfio_pci_core_device *vdev) |
161 | { |
162 | struct zpci_dev *zdev = to_zpci(vdev->pdev); |
163 | |
164 | if (!zdev || !vdev->vdev.kvm) |
165 | return; |
166 | |
167 | if (zpci_kvm_hook.kvm_unregister) |
168 | zpci_kvm_hook.kvm_unregister(zdev); |
169 | } |
170 | |