1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved
4 */
5
6 #include <linux/virtio_pci_admin.h>
7 #include "virtio_pci_common.h"
8
9 /*
10 * virtio_pci_admin_has_legacy_io - Checks whether the legacy IO
11 * commands are supported
12 * @dev: VF pci_dev
13 *
14 * Returns true on success.
15 */
virtio_pci_admin_has_legacy_io(struct pci_dev * pdev)16 bool virtio_pci_admin_has_legacy_io(struct pci_dev *pdev)
17 {
18 struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
19 struct virtio_pci_device *vp_dev;
20
21 if (!virtio_dev)
22 return false;
23
24 if (!virtio_has_feature(virtio_dev, VIRTIO_F_ADMIN_VQ))
25 return false;
26
27 vp_dev = to_vp_device(virtio_dev);
28
29 if ((vp_dev->admin_vq.supported_cmds & VIRTIO_LEGACY_ADMIN_CMD_BITMAP) ==
30 VIRTIO_LEGACY_ADMIN_CMD_BITMAP)
31 return true;
32 return false;
33 }
34 EXPORT_SYMBOL_GPL(virtio_pci_admin_has_legacy_io);
35
virtio_pci_admin_legacy_io_write(struct pci_dev * pdev,u16 opcode,u8 offset,u8 size,u8 * buf)36 static int virtio_pci_admin_legacy_io_write(struct pci_dev *pdev, u16 opcode,
37 u8 offset, u8 size, u8 *buf)
38 {
39 struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
40 struct virtio_admin_cmd_legacy_wr_data *data;
41 struct virtio_admin_cmd cmd = {};
42 struct scatterlist data_sg;
43 int vf_id;
44 int ret;
45
46 if (!virtio_dev)
47 return -ENODEV;
48
49 vf_id = pci_iov_vf_id(pdev);
50 if (vf_id < 0)
51 return vf_id;
52
53 data = kzalloc(sizeof(*data) + size, GFP_KERNEL);
54 if (!data)
55 return -ENOMEM;
56
57 data->offset = offset;
58 memcpy(data->registers, buf, size);
59 sg_init_one(&data_sg, data, sizeof(*data) + size);
60 cmd.opcode = cpu_to_le16(opcode);
61 cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
62 cmd.group_member_id = cpu_to_le64(vf_id + 1);
63 cmd.data_sg = &data_sg;
64 ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
65
66 kfree(data);
67 return ret;
68 }
69
70 /*
71 * virtio_pci_admin_legacy_io_write_common - Write legacy common configuration
72 * of a member device
73 * @dev: VF pci_dev
74 * @offset: starting byte offset within the common configuration area to write to
75 * @size: size of the data to write
76 * @buf: buffer which holds the data
77 *
78 * Note: caller must serialize access for the given device.
79 * Returns 0 on success, or negative on failure.
80 */
virtio_pci_admin_legacy_common_io_write(struct pci_dev * pdev,u8 offset,u8 size,u8 * buf)81 int virtio_pci_admin_legacy_common_io_write(struct pci_dev *pdev, u8 offset,
82 u8 size, u8 *buf)
83 {
84 return virtio_pci_admin_legacy_io_write(pdev,
85 VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_WRITE,
86 offset, size, buf);
87 }
88 EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_common_io_write);
89
90 /*
91 * virtio_pci_admin_legacy_io_write_device - Write legacy device configuration
92 * of a member device
93 * @dev: VF pci_dev
94 * @offset: starting byte offset within the device configuration area to write to
95 * @size: size of the data to write
96 * @buf: buffer which holds the data
97 *
98 * Note: caller must serialize access for the given device.
99 * Returns 0 on success, or negative on failure.
100 */
virtio_pci_admin_legacy_device_io_write(struct pci_dev * pdev,u8 offset,u8 size,u8 * buf)101 int virtio_pci_admin_legacy_device_io_write(struct pci_dev *pdev, u8 offset,
102 u8 size, u8 *buf)
103 {
104 return virtio_pci_admin_legacy_io_write(pdev,
105 VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_WRITE,
106 offset, size, buf);
107 }
108 EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_device_io_write);
109
virtio_pci_admin_legacy_io_read(struct pci_dev * pdev,u16 opcode,u8 offset,u8 size,u8 * buf)110 static int virtio_pci_admin_legacy_io_read(struct pci_dev *pdev, u16 opcode,
111 u8 offset, u8 size, u8 *buf)
112 {
113 struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
114 struct virtio_admin_cmd_legacy_rd_data *data;
115 struct scatterlist data_sg, result_sg;
116 struct virtio_admin_cmd cmd = {};
117 int vf_id;
118 int ret;
119
120 if (!virtio_dev)
121 return -ENODEV;
122
123 vf_id = pci_iov_vf_id(pdev);
124 if (vf_id < 0)
125 return vf_id;
126
127 data = kzalloc(sizeof(*data), GFP_KERNEL);
128 if (!data)
129 return -ENOMEM;
130
131 data->offset = offset;
132 sg_init_one(&data_sg, data, sizeof(*data));
133 sg_init_one(&result_sg, buf, size);
134 cmd.opcode = cpu_to_le16(opcode);
135 cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
136 cmd.group_member_id = cpu_to_le64(vf_id + 1);
137 cmd.data_sg = &data_sg;
138 cmd.result_sg = &result_sg;
139 ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
140
141 kfree(data);
142 return ret;
143 }
144
145 /*
146 * virtio_pci_admin_legacy_device_io_read - Read legacy device configuration of
147 * a member device
148 * @dev: VF pci_dev
149 * @offset: starting byte offset within the device configuration area to read from
150 * @size: size of the data to be read
151 * @buf: buffer to hold the returned data
152 *
153 * Note: caller must serialize access for the given device.
154 * Returns 0 on success, or negative on failure.
155 */
virtio_pci_admin_legacy_device_io_read(struct pci_dev * pdev,u8 offset,u8 size,u8 * buf)156 int virtio_pci_admin_legacy_device_io_read(struct pci_dev *pdev, u8 offset,
157 u8 size, u8 *buf)
158 {
159 return virtio_pci_admin_legacy_io_read(pdev,
160 VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ,
161 offset, size, buf);
162 }
163 EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_device_io_read);
164
165 /*
166 * virtio_pci_admin_legacy_common_io_read - Read legacy common configuration of
167 * a member device
168 * @dev: VF pci_dev
169 * @offset: starting byte offset within the common configuration area to read from
170 * @size: size of the data to be read
171 * @buf: buffer to hold the returned data
172 *
173 * Note: caller must serialize access for the given device.
174 * Returns 0 on success, or negative on failure.
175 */
virtio_pci_admin_legacy_common_io_read(struct pci_dev * pdev,u8 offset,u8 size,u8 * buf)176 int virtio_pci_admin_legacy_common_io_read(struct pci_dev *pdev, u8 offset,
177 u8 size, u8 *buf)
178 {
179 return virtio_pci_admin_legacy_io_read(pdev,
180 VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_READ,
181 offset, size, buf);
182 }
183 EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_common_io_read);
184
185 /*
186 * virtio_pci_admin_legacy_io_notify_info - Read the queue notification
187 * information for legacy interface
188 * @dev: VF pci_dev
189 * @req_bar_flags: requested bar flags
190 * @bar: on output the BAR number of the owner or member device
191 * @bar_offset: on output the offset within bar
192 *
193 * Returns 0 on success, or negative on failure.
194 */
virtio_pci_admin_legacy_io_notify_info(struct pci_dev * pdev,u8 req_bar_flags,u8 * bar,u64 * bar_offset)195 int virtio_pci_admin_legacy_io_notify_info(struct pci_dev *pdev,
196 u8 req_bar_flags, u8 *bar,
197 u64 *bar_offset)
198 {
199 struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
200 struct virtio_admin_cmd_notify_info_result *result;
201 struct virtio_admin_cmd cmd = {};
202 struct scatterlist result_sg;
203 int vf_id;
204 int ret;
205
206 if (!virtio_dev)
207 return -ENODEV;
208
209 vf_id = pci_iov_vf_id(pdev);
210 if (vf_id < 0)
211 return vf_id;
212
213 result = kzalloc(sizeof(*result), GFP_KERNEL);
214 if (!result)
215 return -ENOMEM;
216
217 sg_init_one(&result_sg, result, sizeof(*result));
218 cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO);
219 cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
220 cmd.group_member_id = cpu_to_le64(vf_id + 1);
221 cmd.result_sg = &result_sg;
222 ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
223 if (!ret) {
224 struct virtio_admin_cmd_notify_info_data *entry;
225 int i;
226
227 ret = -ENOENT;
228 for (i = 0; i < VIRTIO_ADMIN_CMD_MAX_NOTIFY_INFO; i++) {
229 entry = &result->entries[i];
230 if (entry->flags == VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_END)
231 break;
232 if (entry->flags != req_bar_flags)
233 continue;
234 *bar = entry->bar;
235 *bar_offset = le64_to_cpu(entry->offset);
236 ret = 0;
237 break;
238 }
239 }
240
241 kfree(result);
242 return ret;
243 }
244 EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_io_notify_info);
245