]>
Commit | Line | Data |
---|---|---|
3a977dee AO |
1 | /* |
2 | * QEMU Intel 82576 SR/IOV Ethernet Controller Emulation | |
3 | * | |
4 | * Datasheet: | |
5 | * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82576eg-gbe-datasheet.pdf | |
6 | * | |
7 | * Copyright (c) 2020-2023 Red Hat, Inc. | |
8 | * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) | |
9 | * Developed by Daynix Computing LTD (http://www.daynix.com) | |
10 | * | |
11 | * Authors: | |
12 | * Akihiko Odaki <akihiko.odaki@daynix.com> | |
13 | * Gal Hammmer <gal.hammer@sap.com> | |
14 | * Marcel Apfelbaum <marcel.apfelbaum@gmail.com> | |
15 | * Dmitry Fleytman <dmitry@daynix.com> | |
16 | * Leonid Bloch <leonid@daynix.com> | |
17 | * Yan Vugenfirer <yan@daynix.com> | |
18 | * | |
19 | * Based on work done by: | |
20 | * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. | |
21 | * Copyright (c) 2008 Qumranet | |
22 | * Based on work done by: | |
23 | * Copyright (c) 2007 Dan Aloni | |
24 | * Copyright (c) 2004 Antony T Curtis | |
25 | * | |
26 | * This library is free software; you can redistribute it and/or | |
27 | * modify it under the terms of the GNU Lesser General Public | |
28 | * License as published by the Free Software Foundation; either | |
29 | * version 2.1 of the License, or (at your option) any later version. | |
30 | * | |
31 | * This library is distributed in the hope that it will be useful, | |
32 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
33 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
34 | * Lesser General Public License for more details. | |
35 | * | |
36 | * You should have received a copy of the GNU Lesser General Public | |
37 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
38 | */ | |
39 | ||
40 | #include "qemu/osdep.h" | |
41 | #include "hw/hw.h" | |
42 | #include "hw/net/mii.h" | |
43 | #include "hw/pci/pci_device.h" | |
44 | #include "hw/pci/pcie.h" | |
45 | #include "hw/pci/msix.h" | |
46 | #include "net/eth.h" | |
47 | #include "net/net.h" | |
48 | #include "igb_common.h" | |
49 | #include "igb_core.h" | |
50 | #include "trace.h" | |
51 | #include "qapi/error.h" | |
52 | ||
3a977dee AO |
53 | OBJECT_DECLARE_SIMPLE_TYPE(IgbVfState, IGBVF) |
54 | ||
3a977dee AO |
55 | struct IgbVfState { |
56 | PCIDevice parent_obj; | |
57 | ||
58 | MemoryRegion mmio; | |
59 | MemoryRegion msix; | |
60 | }; | |
61 | ||
62 | static hwaddr vf_to_pf_addr(hwaddr addr, uint16_t vfn, bool write) | |
63 | { | |
64 | switch (addr) { | |
65 | case E1000_CTRL: | |
66 | case E1000_CTRL_DUP: | |
67 | return E1000_PVTCTRL(vfn); | |
68 | case E1000_EICS: | |
69 | return E1000_PVTEICS(vfn); | |
70 | case E1000_EIMS: | |
71 | return E1000_PVTEIMS(vfn); | |
72 | case E1000_EIMC: | |
73 | return E1000_PVTEIMC(vfn); | |
74 | case E1000_EIAC: | |
75 | return E1000_PVTEIAC(vfn); | |
76 | case E1000_EIAM: | |
77 | return E1000_PVTEIAM(vfn); | |
78 | case E1000_EICR: | |
79 | return E1000_PVTEICR(vfn); | |
80 | case E1000_EITR(0): | |
81 | case E1000_EITR(1): | |
82 | case E1000_EITR(2): | |
83 | return E1000_EITR(22) + (addr - E1000_EITR(0)) - vfn * 0xC; | |
84 | case E1000_IVAR0: | |
85 | return E1000_VTIVAR + vfn * 4; | |
86 | case E1000_IVAR_MISC: | |
87 | return E1000_VTIVAR_MISC + vfn * 4; | |
88 | case 0x0F04: /* PBACL */ | |
89 | return E1000_PBACLR; | |
90 | case 0x0F0C: /* PSRTYPE */ | |
91 | return E1000_PSRTYPE(vfn); | |
92 | case E1000_V2PMAILBOX(0): | |
93 | return E1000_V2PMAILBOX(vfn); | |
94 | case E1000_VMBMEM(0) ... E1000_VMBMEM(0) + 0x3F: | |
95 | return addr + vfn * 0x40; | |
96 | case E1000_RDBAL_A(0): | |
97 | return E1000_RDBAL(vfn); | |
98 | case E1000_RDBAL_A(1): | |
99 | return E1000_RDBAL(vfn + IGB_MAX_VF_FUNCTIONS); | |
100 | case E1000_RDBAH_A(0): | |
101 | return E1000_RDBAH(vfn); | |
102 | case E1000_RDBAH_A(1): | |
103 | return E1000_RDBAH(vfn + IGB_MAX_VF_FUNCTIONS); | |
104 | case E1000_RDLEN_A(0): | |
105 | return E1000_RDLEN(vfn); | |
106 | case E1000_RDLEN_A(1): | |
107 | return E1000_RDLEN(vfn + IGB_MAX_VF_FUNCTIONS); | |
108 | case E1000_SRRCTL_A(0): | |
109 | return E1000_SRRCTL(vfn); | |
110 | case E1000_SRRCTL_A(1): | |
111 | return E1000_SRRCTL(vfn + IGB_MAX_VF_FUNCTIONS); | |
112 | case E1000_RDH_A(0): | |
113 | return E1000_RDH(vfn); | |
114 | case E1000_RDH_A(1): | |
115 | return E1000_RDH(vfn + IGB_MAX_VF_FUNCTIONS); | |
116 | case E1000_RXCTL_A(0): | |
117 | return E1000_RXCTL(vfn); | |
118 | case E1000_RXCTL_A(1): | |
119 | return E1000_RXCTL(vfn + IGB_MAX_VF_FUNCTIONS); | |
120 | case E1000_RDT_A(0): | |
121 | return E1000_RDT(vfn); | |
122 | case E1000_RDT_A(1): | |
123 | return E1000_RDT(vfn + IGB_MAX_VF_FUNCTIONS); | |
124 | case E1000_RXDCTL_A(0): | |
125 | return E1000_RXDCTL(vfn); | |
126 | case E1000_RXDCTL_A(1): | |
127 | return E1000_RXDCTL(vfn + IGB_MAX_VF_FUNCTIONS); | |
128 | case E1000_RQDPC_A(0): | |
129 | return E1000_RQDPC(vfn); | |
130 | case E1000_RQDPC_A(1): | |
131 | return E1000_RQDPC(vfn + IGB_MAX_VF_FUNCTIONS); | |
132 | case E1000_TDBAL_A(0): | |
133 | return E1000_TDBAL(vfn); | |
134 | case E1000_TDBAL_A(1): | |
135 | return E1000_TDBAL(vfn + IGB_MAX_VF_FUNCTIONS); | |
136 | case E1000_TDBAH_A(0): | |
137 | return E1000_TDBAH(vfn); | |
138 | case E1000_TDBAH_A(1): | |
139 | return E1000_TDBAH(vfn + IGB_MAX_VF_FUNCTIONS); | |
140 | case E1000_TDLEN_A(0): | |
141 | return E1000_TDLEN(vfn); | |
142 | case E1000_TDLEN_A(1): | |
143 | return E1000_TDLEN(vfn + IGB_MAX_VF_FUNCTIONS); | |
144 | case E1000_TDH_A(0): | |
145 | return E1000_TDH(vfn); | |
146 | case E1000_TDH_A(1): | |
147 | return E1000_TDH(vfn + IGB_MAX_VF_FUNCTIONS); | |
148 | case E1000_TXCTL_A(0): | |
149 | return E1000_TXCTL(vfn); | |
150 | case E1000_TXCTL_A(1): | |
151 | return E1000_TXCTL(vfn + IGB_MAX_VF_FUNCTIONS); | |
152 | case E1000_TDT_A(0): | |
153 | return E1000_TDT(vfn); | |
154 | case E1000_TDT_A(1): | |
155 | return E1000_TDT(vfn + IGB_MAX_VF_FUNCTIONS); | |
156 | case E1000_TXDCTL_A(0): | |
157 | return E1000_TXDCTL(vfn); | |
158 | case E1000_TXDCTL_A(1): | |
159 | return E1000_TXDCTL(vfn + IGB_MAX_VF_FUNCTIONS); | |
160 | case E1000_TDWBAL_A(0): | |
161 | return E1000_TDWBAL(vfn); | |
162 | case E1000_TDWBAL_A(1): | |
163 | return E1000_TDWBAL(vfn + IGB_MAX_VF_FUNCTIONS); | |
164 | case E1000_TDWBAH_A(0): | |
165 | return E1000_TDWBAH(vfn); | |
166 | case E1000_TDWBAH_A(1): | |
167 | return E1000_TDWBAH(vfn + IGB_MAX_VF_FUNCTIONS); | |
168 | case E1000_VFGPRC: | |
169 | return E1000_PVFGPRC(vfn); | |
170 | case E1000_VFGPTC: | |
171 | return E1000_PVFGPTC(vfn); | |
172 | case E1000_VFGORC: | |
173 | return E1000_PVFGORC(vfn); | |
174 | case E1000_VFGOTC: | |
175 | return E1000_PVFGOTC(vfn); | |
176 | case E1000_VFMPRC: | |
177 | return E1000_PVFMPRC(vfn); | |
178 | case E1000_VFGPRLBC: | |
179 | return E1000_PVFGPRLBC(vfn); | |
180 | case E1000_VFGPTLBC: | |
181 | return E1000_PVFGPTLBC(vfn); | |
182 | case E1000_VFGORLBC: | |
183 | return E1000_PVFGORLBC(vfn); | |
184 | case E1000_VFGOTLBC: | |
185 | return E1000_PVFGOTLBC(vfn); | |
186 | case E1000_STATUS: | |
187 | case E1000_FRTIMER: | |
188 | if (write) { | |
189 | return HWADDR_MAX; | |
190 | } | |
191 | /* fallthrough */ | |
192 | case 0x34E8: /* PBTWAC */ | |
193 | case 0x24E8: /* PBRWAC */ | |
194 | return addr; | |
195 | } | |
196 | ||
197 | trace_igbvf_wrn_io_addr_unknown(addr); | |
198 | ||
199 | return HWADDR_MAX; | |
200 | } | |
201 | ||
202 | static void igbvf_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, | |
203 | int len) | |
204 | { | |
205 | trace_igbvf_write_config(addr, val, len); | |
206 | pci_default_write_config(dev, addr, val, len); | |
207 | } | |
208 | ||
209 | static uint64_t igbvf_mmio_read(void *opaque, hwaddr addr, unsigned size) | |
210 | { | |
211 | PCIDevice *vf = PCI_DEVICE(opaque); | |
212 | PCIDevice *pf = pcie_sriov_get_pf(vf); | |
213 | ||
214 | addr = vf_to_pf_addr(addr, pcie_sriov_vf_number(vf), false); | |
215 | return addr == HWADDR_MAX ? 0 : igb_mmio_read(pf, addr, size); | |
216 | } | |
217 | ||
218 | static void igbvf_mmio_write(void *opaque, hwaddr addr, uint64_t val, | |
219 | unsigned size) | |
220 | { | |
221 | PCIDevice *vf = PCI_DEVICE(opaque); | |
222 | PCIDevice *pf = pcie_sriov_get_pf(vf); | |
223 | ||
224 | addr = vf_to_pf_addr(addr, pcie_sriov_vf_number(vf), true); | |
225 | if (addr != HWADDR_MAX) { | |
226 | igb_mmio_write(pf, addr, val, size); | |
227 | } | |
228 | } | |
229 | ||
230 | static const MemoryRegionOps mmio_ops = { | |
231 | .read = igbvf_mmio_read, | |
232 | .write = igbvf_mmio_write, | |
233 | .endianness = DEVICE_LITTLE_ENDIAN, | |
234 | .impl = { | |
235 | .min_access_size = 4, | |
236 | .max_access_size = 4, | |
237 | }, | |
238 | }; | |
239 | ||
240 | static void igbvf_pci_realize(PCIDevice *dev, Error **errp) | |
241 | { | |
242 | IgbVfState *s = IGBVF(dev); | |
243 | int ret; | |
244 | int i; | |
245 | ||
246 | dev->config_write = igbvf_write_config; | |
247 | ||
248 | memory_region_init_io(&s->mmio, OBJECT(dev), &mmio_ops, s, "igbvf-mmio", | |
249 | IGBVF_MMIO_SIZE); | |
250 | pcie_sriov_vf_register_bar(dev, IGBVF_MMIO_BAR_IDX, &s->mmio); | |
251 | ||
252 | memory_region_init(&s->msix, OBJECT(dev), "igbvf-msix", IGBVF_MSIX_SIZE); | |
253 | pcie_sriov_vf_register_bar(dev, IGBVF_MSIX_BAR_IDX, &s->msix); | |
254 | ||
255 | ret = msix_init(dev, IGBVF_MSIX_VEC_NUM, &s->msix, IGBVF_MSIX_BAR_IDX, 0, | |
256 | &s->msix, IGBVF_MSIX_BAR_IDX, 0x2000, 0x70, errp); | |
257 | if (ret) { | |
258 | return; | |
259 | } | |
260 | ||
261 | for (i = 0; i < IGBVF_MSIX_VEC_NUM; i++) { | |
262 | msix_vector_use(dev, i); | |
263 | } | |
264 | ||
265 | if (pcie_endpoint_cap_init(dev, 0xa0) < 0) { | |
266 | hw_error("Failed to initialize PCIe capability"); | |
267 | } | |
268 | ||
269 | if (pcie_aer_init(dev, 1, 0x100, 0x40, errp) < 0) { | |
270 | hw_error("Failed to initialize AER capability"); | |
271 | } | |
272 | ||
273 | pcie_ari_init(dev, 0x150, 1); | |
274 | } | |
275 | ||
276 | static void igbvf_pci_uninit(PCIDevice *dev) | |
277 | { | |
278 | IgbVfState *s = IGBVF(dev); | |
279 | ||
280 | pcie_aer_exit(dev); | |
281 | pcie_cap_exit(dev); | |
282 | msix_unuse_all_vectors(dev); | |
283 | msix_uninit(dev, &s->msix, &s->msix); | |
284 | } | |
285 | ||
286 | static void igbvf_class_init(ObjectClass *class, void *data) | |
287 | { | |
288 | DeviceClass *dc = DEVICE_CLASS(class); | |
289 | PCIDeviceClass *c = PCI_DEVICE_CLASS(class); | |
290 | ||
291 | c->realize = igbvf_pci_realize; | |
292 | c->exit = igbvf_pci_uninit; | |
293 | c->vendor_id = PCI_VENDOR_ID_INTEL; | |
294 | c->device_id = E1000_DEV_ID_82576_VF; | |
295 | c->revision = 1; | |
296 | c->class_id = PCI_CLASS_NETWORK_ETHERNET; | |
297 | ||
298 | dc->desc = "Intel 82576 Virtual Function"; | |
299 | dc->user_creatable = false; | |
300 | ||
301 | set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); | |
302 | } | |
303 | ||
304 | static const TypeInfo igbvf_info = { | |
305 | .name = TYPE_IGBVF, | |
306 | .parent = TYPE_PCI_DEVICE, | |
307 | .instance_size = sizeof(IgbVfState), | |
308 | .class_init = igbvf_class_init, | |
309 | .interfaces = (InterfaceInfo[]) { | |
310 | { INTERFACE_PCIE_DEVICE }, | |
311 | { } | |
312 | }, | |
313 | }; | |
314 | ||
315 | static void igb_register_types(void) | |
316 | { | |
317 | type_register_static(&igbvf_info); | |
318 | } | |
319 | ||
320 | type_init(igb_register_types) |