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 | */ |
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(vdev: virtio_dev, VIRTIO_F_ADMIN_VQ)) |
25 | return false; |
26 | |
27 | vp_dev = to_vp_device(vdev: 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 | |
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(dev: pdev); |
50 | if (vf_id < 0) |
51 | return vf_id; |
52 | |
53 | data = kzalloc(size: 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(vdev: virtio_dev, cmd: &cmd); |
65 | |
66 | kfree(objp: 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 | */ |
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 | */ |
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 | |
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(dev: pdev); |
124 | if (vf_id < 0) |
125 | return vf_id; |
126 | |
127 | data = kzalloc(size: 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(vdev: virtio_dev, cmd: &cmd); |
140 | |
141 | kfree(objp: 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 | */ |
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 | */ |
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 | */ |
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(dev: pdev); |
210 | if (vf_id < 0) |
211 | return vf_id; |
212 | |
213 | result = kzalloc(size: 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(vdev: virtio_dev, cmd: &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(objp: result); |
242 | return ret; |
243 | } |
244 | EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_io_notify_info); |
245 | |