1 | /* |
2 | * QEMU LSI SAS1068 Host Bus Adapter emulation - configuration pages |
3 | * |
4 | * Copyright (c) 2016 Red Hat, Inc. |
5 | * |
6 | * Author: Paolo Bonzini |
7 | * |
8 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2 of the License, or (at your option) any later version. |
12 | * |
13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | */ |
18 | #include "qemu/osdep.h" |
19 | #include "hw/pci/pci.h" |
20 | #include "hw/scsi/scsi.h" |
21 | |
22 | #include "mptsas.h" |
23 | #include "mpi.h" |
24 | #include "trace.h" |
25 | |
26 | /* Generic functions for marshaling and unmarshaling. */ |
27 | |
28 | #define repl1(x) x |
29 | #define repl2(x) x x |
30 | #define repl3(x) x x x |
31 | #define repl4(x) x x x x |
32 | #define repl5(x) x x x x x |
33 | #define repl6(x) x x x x x x |
34 | #define repl7(x) x x x x x x x |
35 | #define repl8(x) x x x x x x x x |
36 | |
37 | #define repl(n, x) glue(repl, n)(x) |
38 | |
39 | typedef union PackValue { |
40 | uint64_t ll; |
41 | char *str; |
42 | } PackValue; |
43 | |
44 | static size_t vfill(uint8_t *data, size_t size, const char *fmt, va_list ap) |
45 | { |
46 | size_t ofs; |
47 | PackValue val; |
48 | const char *p; |
49 | |
50 | ofs = 0; |
51 | p = fmt; |
52 | while (*p) { |
53 | memset(&val, 0, sizeof(val)); |
54 | switch (*p) { |
55 | case '*': |
56 | p++; |
57 | break; |
58 | case 'b': |
59 | case 'w': |
60 | case 'l': |
61 | val.ll = va_arg(ap, int); |
62 | break; |
63 | case 'q': |
64 | val.ll = va_arg(ap, int64_t); |
65 | break; |
66 | case 's': |
67 | val.str = va_arg(ap, void *); |
68 | break; |
69 | } |
70 | switch (*p++) { |
71 | case 'b': |
72 | if (data) { |
73 | stb_p(data + ofs, val.ll); |
74 | } |
75 | ofs++; |
76 | break; |
77 | case 'w': |
78 | if (data) { |
79 | stw_le_p(data + ofs, val.ll); |
80 | } |
81 | ofs += 2; |
82 | break; |
83 | case 'l': |
84 | if (data) { |
85 | stl_le_p(data + ofs, val.ll); |
86 | } |
87 | ofs += 4; |
88 | break; |
89 | case 'q': |
90 | if (data) { |
91 | stq_le_p(data + ofs, val.ll); |
92 | } |
93 | ofs += 8; |
94 | break; |
95 | case 's': |
96 | { |
97 | int cnt = atoi(p); |
98 | if (data) { |
99 | if (val.str) { |
100 | strncpy((void *)data + ofs, val.str, cnt); |
101 | } else { |
102 | memset((void *)data + ofs, 0, cnt); |
103 | } |
104 | } |
105 | ofs += cnt; |
106 | break; |
107 | } |
108 | } |
109 | } |
110 | |
111 | return ofs; |
112 | } |
113 | |
114 | static size_t vpack(uint8_t **p_data, const char *fmt, va_list ap1) |
115 | { |
116 | size_t size = 0; |
117 | uint8_t *data = NULL; |
118 | |
119 | if (p_data) { |
120 | va_list ap2; |
121 | |
122 | va_copy(ap2, ap1); |
123 | size = vfill(NULL, 0, fmt, ap2); |
124 | *p_data = data = g_malloc(size); |
125 | va_end(ap2); |
126 | } |
127 | return vfill(data, size, fmt, ap1); |
128 | } |
129 | |
130 | static size_t fill(uint8_t *data, size_t size, const char *fmt, ...) |
131 | { |
132 | va_list ap; |
133 | size_t ret; |
134 | |
135 | va_start(ap, fmt); |
136 | ret = vfill(data, size, fmt, ap); |
137 | va_end(ap); |
138 | |
139 | return ret; |
140 | } |
141 | |
142 | /* Functions to build the page header and fill in the length, always used |
143 | * through the macros. |
144 | */ |
145 | |
146 | #define MPTSAS_CONFIG_PACK(number, type, version, fmt, ...) \ |
147 | mptsas_config_pack(data, "b*bbb" fmt, version, number, type, \ |
148 | ## __VA_ARGS__) |
149 | |
150 | static size_t mptsas_config_pack(uint8_t **data, const char *fmt, ...) |
151 | { |
152 | va_list ap; |
153 | size_t ret; |
154 | |
155 | va_start(ap, fmt); |
156 | ret = vpack(data, fmt, ap); |
157 | va_end(ap); |
158 | |
159 | if (data) { |
160 | assert(ret / 4 < 256 && (ret % 4) == 0); |
161 | stb_p(*data + 1, ret / 4); |
162 | } |
163 | return ret; |
164 | } |
165 | |
166 | #define MPTSAS_CONFIG_PACK_EXT(number, type, version, fmt, ...) \ |
167 | mptsas_config_pack_ext(data, "b*bbb*wb*b" fmt, version, number, \ |
168 | MPI_CONFIG_PAGETYPE_EXTENDED, type, ## __VA_ARGS__) |
169 | |
170 | static size_t mptsas_config_pack_ext(uint8_t **data, const char *fmt, ...) |
171 | { |
172 | va_list ap; |
173 | size_t ret; |
174 | |
175 | va_start(ap, fmt); |
176 | ret = vpack(data, fmt, ap); |
177 | va_end(ap); |
178 | |
179 | if (data) { |
180 | assert(ret < 65536 && (ret % 4) == 0); |
181 | stw_le_p(*data + 4, ret / 4); |
182 | } |
183 | return ret; |
184 | } |
185 | |
186 | /* Manufacturing pages */ |
187 | |
188 | static |
189 | size_t mptsas_config_manufacturing_0(MPTSASState *s, uint8_t **data, int address) |
190 | { |
191 | return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, |
192 | "s16s8s16s16s16" , |
193 | "QEMU MPT Fusion" , |
194 | "2.5" , |
195 | "QEMU MPT Fusion" , |
196 | "QEMU" , |
197 | "0000111122223333" ); |
198 | } |
199 | |
200 | static |
201 | size_t mptsas_config_manufacturing_1(MPTSASState *s, uint8_t **data, int address) |
202 | { |
203 | /* VPD - all zeros */ |
204 | return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, |
205 | "*s256" ); |
206 | } |
207 | |
208 | static |
209 | size_t mptsas_config_manufacturing_2(MPTSASState *s, uint8_t **data, int address) |
210 | { |
211 | PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); |
212 | return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, |
213 | "wb*b*l" , |
214 | pcic->device_id, pcic->revision); |
215 | } |
216 | |
217 | static |
218 | size_t mptsas_config_manufacturing_3(MPTSASState *s, uint8_t **data, int address) |
219 | { |
220 | PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); |
221 | return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, |
222 | "wb*b*l" , |
223 | pcic->device_id, pcic->revision); |
224 | } |
225 | |
226 | static |
227 | size_t mptsas_config_manufacturing_4(MPTSASState *s, uint8_t **data, int address) |
228 | { |
229 | /* All zeros */ |
230 | return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x05, |
231 | "*l*b*b*b*b*b*b*w*s56*l*l*l*l*l*l" |
232 | "*b*b*w*b*b*w*l*l" ); |
233 | } |
234 | |
235 | static |
236 | size_t mptsas_config_manufacturing_5(MPTSASState *s, uint8_t **data, int address) |
237 | { |
238 | return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x02, |
239 | "q*b*b*w*l*l" , s->sas_addr); |
240 | } |
241 | |
242 | static |
243 | size_t mptsas_config_manufacturing_6(MPTSASState *s, uint8_t **data, int address) |
244 | { |
245 | return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, |
246 | "*l" ); |
247 | } |
248 | |
249 | static |
250 | size_t mptsas_config_manufacturing_7(MPTSASState *s, uint8_t **data, int address) |
251 | { |
252 | return MPTSAS_CONFIG_PACK(7, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, |
253 | "*l*l*l*s16*b*b*w" , MPTSAS_NUM_PORTS); |
254 | } |
255 | |
256 | static |
257 | size_t mptsas_config_manufacturing_8(MPTSASState *s, uint8_t **data, int address) |
258 | { |
259 | return MPTSAS_CONFIG_PACK(8, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, |
260 | "*l" ); |
261 | } |
262 | |
263 | static |
264 | size_t mptsas_config_manufacturing_9(MPTSASState *s, uint8_t **data, int address) |
265 | { |
266 | return MPTSAS_CONFIG_PACK(9, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, |
267 | "*l" ); |
268 | } |
269 | |
270 | static |
271 | size_t mptsas_config_manufacturing_10(MPTSASState *s, uint8_t **data, int address) |
272 | { |
273 | return MPTSAS_CONFIG_PACK(10, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, |
274 | "*l" ); |
275 | } |
276 | |
277 | /* I/O unit pages */ |
278 | |
279 | static |
280 | size_t mptsas_config_io_unit_0(MPTSASState *s, uint8_t **data, int address) |
281 | { |
282 | PCIDevice *pci = PCI_DEVICE(s); |
283 | uint64_t unique_value = 0x53504D554D4551LL; /* "QEMUMPTx" */ |
284 | |
285 | unique_value |= (uint64_t)pci->devfn << 56; |
286 | return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, |
287 | "q" , unique_value); |
288 | } |
289 | |
290 | static |
291 | size_t mptsas_config_io_unit_1(MPTSASState *s, uint8_t **data, int address) |
292 | { |
293 | return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, "l" , |
294 | 0x41 /* single function, RAID disabled */ ); |
295 | } |
296 | |
297 | static |
298 | size_t mptsas_config_io_unit_2(MPTSASState *s, uint8_t **data, int address) |
299 | { |
300 | PCIDevice *pci = PCI_DEVICE(s); |
301 | uint8_t devfn = pci->devfn; |
302 | return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, |
303 | "llbbw*b*b*w*b*b*w*b*b*w*l" , |
304 | 0, 0x100, 0 /* pci bus? */, devfn, 0); |
305 | } |
306 | |
307 | static |
308 | size_t mptsas_config_io_unit_3(MPTSASState *s, uint8_t **data, int address) |
309 | { |
310 | return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x01, |
311 | "*b*b*w*l" ); |
312 | } |
313 | |
314 | static |
315 | size_t mptsas_config_io_unit_4(MPTSASState *s, uint8_t **data, int address) |
316 | { |
317 | return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, "*l*l*q" ); |
318 | } |
319 | |
320 | /* I/O controller pages */ |
321 | |
322 | static |
323 | size_t mptsas_config_ioc_0(MPTSASState *s, uint8_t **data, int address) |
324 | { |
325 | PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); |
326 | |
327 | return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IOC, 0x01, |
328 | "*l*lwwb*b*b*blww" , |
329 | pcic->vendor_id, pcic->device_id, pcic->revision, |
330 | pcic->class_id, pcic->subsystem_vendor_id, |
331 | pcic->subsystem_id); |
332 | } |
333 | |
334 | static |
335 | size_t mptsas_config_ioc_1(MPTSASState *s, uint8_t **data, int address) |
336 | { |
337 | return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IOC, 0x03, |
338 | "*l*l*b*b*b*b" ); |
339 | } |
340 | |
341 | static |
342 | size_t mptsas_config_ioc_2(MPTSASState *s, uint8_t **data, int address) |
343 | { |
344 | return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IOC, 0x04, |
345 | "*l*b*b*b*b" ); |
346 | } |
347 | |
348 | static |
349 | size_t mptsas_config_ioc_3(MPTSASState *s, uint8_t **data, int address) |
350 | { |
351 | return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IOC, 0x00, |
352 | "*b*b*w" ); |
353 | } |
354 | |
355 | static |
356 | size_t mptsas_config_ioc_4(MPTSASState *s, uint8_t **data, int address) |
357 | { |
358 | return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IOC, 0x00, |
359 | "*b*b*w" ); |
360 | } |
361 | |
362 | static |
363 | size_t mptsas_config_ioc_5(MPTSASState *s, uint8_t **data, int address) |
364 | { |
365 | return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_IOC, 0x00, |
366 | "*l*b*b*w" ); |
367 | } |
368 | |
369 | static |
370 | size_t mptsas_config_ioc_6(MPTSASState *s, uint8_t **data, int address) |
371 | { |
372 | return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_IOC, 0x01, |
373 | "*l*b*b*b*b*b*b*b*b*b*b*w*l*l*l*l*b*b*w" |
374 | "*w*w*w*w*l*l*l" ); |
375 | } |
376 | |
377 | /* SAS I/O unit pages (extended) */ |
378 | |
379 | #define MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE 16 |
380 | |
381 | #define MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION 0x02 |
382 | #define MPI_SAS_IOUNIT0_RATE_1_5 0x08 |
383 | #define MPI_SAS_IOUNIT0_RATE_3_0 0x09 |
384 | |
385 | #define MPI_SAS_DEVICE_INFO_NO_DEVICE 0x00000000 |
386 | #define MPI_SAS_DEVICE_INFO_END_DEVICE 0x00000001 |
387 | #define MPI_SAS_DEVICE_INFO_SSP_TARGET 0x00000400 |
388 | |
389 | #define MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS 0x00 |
390 | |
391 | #define MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT 0x0001 |
392 | #define MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED 0x0002 |
393 | #define MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT 0x0004 |
394 | |
395 | |
396 | |
397 | static SCSIDevice *mptsas_phy_get_device(MPTSASState *s, int i, |
398 | int *phy_handle, int *dev_handle) |
399 | { |
400 | SCSIDevice *d = scsi_device_find(&s->bus, 0, i, 0); |
401 | |
402 | if (phy_handle) { |
403 | *phy_handle = i + 1; |
404 | } |
405 | if (dev_handle) { |
406 | *dev_handle = d ? i + 1 + MPTSAS_NUM_PORTS : 0; |
407 | } |
408 | return d; |
409 | } |
410 | |
411 | static |
412 | size_t mptsas_config_sas_io_unit_0(MPTSASState *s, uint8_t **data, int address) |
413 | { |
414 | size_t size = MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x04, |
415 | "*w*wb*b*w" |
416 | repl(MPTSAS_NUM_PORTS, "*s16" ), |
417 | MPTSAS_NUM_PORTS); |
418 | |
419 | if (data) { |
420 | size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE; |
421 | int i; |
422 | |
423 | for (i = 0; i < MPTSAS_NUM_PORTS; i++) { |
424 | int phy_handle, dev_handle; |
425 | SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); |
426 | |
427 | fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE, |
428 | "bbbblwwl" , i, 0, 0, |
429 | (dev |
430 | ? MPI_SAS_IOUNIT0_RATE_3_0 |
431 | : MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION), |
432 | (dev |
433 | ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET |
434 | : MPI_SAS_DEVICE_INFO_NO_DEVICE), |
435 | dev_handle, |
436 | dev_handle, |
437 | 0); |
438 | ofs += MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE; |
439 | } |
440 | assert(ofs == size); |
441 | } |
442 | return size; |
443 | } |
444 | |
445 | #define MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE 12 |
446 | |
447 | static |
448 | size_t mptsas_config_sas_io_unit_1(MPTSASState *s, uint8_t **data, int address) |
449 | { |
450 | size_t size = MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x07, |
451 | "*w*w*w*wb*b*b*b" |
452 | repl(MPTSAS_NUM_PORTS, "*s12" ), |
453 | MPTSAS_NUM_PORTS); |
454 | |
455 | if (data) { |
456 | size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE; |
457 | int i; |
458 | |
459 | for (i = 0; i < MPTSAS_NUM_PORTS; i++) { |
460 | SCSIDevice *dev = mptsas_phy_get_device(s, i, NULL, NULL); |
461 | fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE, |
462 | "bbbblww" , i, 0, 0, |
463 | (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5, |
464 | (dev |
465 | ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET |
466 | : MPI_SAS_DEVICE_INFO_NO_DEVICE), |
467 | 0, 0); |
468 | ofs += MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE; |
469 | } |
470 | assert(ofs == size); |
471 | } |
472 | return size; |
473 | } |
474 | |
475 | static |
476 | size_t mptsas_config_sas_io_unit_2(MPTSASState *s, uint8_t **data, int address) |
477 | { |
478 | return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06, |
479 | "*b*b*w*w*w*b*b*w" ); |
480 | } |
481 | |
482 | static |
483 | size_t mptsas_config_sas_io_unit_3(MPTSASState *s, uint8_t **data, int address) |
484 | { |
485 | return MPTSAS_CONFIG_PACK_EXT(3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06, |
486 | "*l*l*l*l*l*l*l*l*l" ); |
487 | } |
488 | |
489 | /* SAS PHY pages (extended) */ |
490 | |
491 | static int mptsas_phy_addr_get(MPTSASState *s, int address) |
492 | { |
493 | int i; |
494 | if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 0) { |
495 | i = address & 255; |
496 | } else if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 1) { |
497 | i = address & 65535; |
498 | } else { |
499 | return -EINVAL; |
500 | } |
501 | |
502 | if (i >= MPTSAS_NUM_PORTS) { |
503 | return -EINVAL; |
504 | } |
505 | |
506 | return i; |
507 | } |
508 | |
509 | static |
510 | size_t mptsas_config_phy_0(MPTSASState *s, uint8_t **data, int address) |
511 | { |
512 | int phy_handle = -1; |
513 | int dev_handle = -1; |
514 | int i = mptsas_phy_addr_get(s, address); |
515 | SCSIDevice *dev; |
516 | |
517 | if (i < 0) { |
518 | trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0); |
519 | return i; |
520 | } |
521 | |
522 | dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); |
523 | trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0); |
524 | |
525 | return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01, |
526 | "w*wqwb*blbb*b*b*l" , |
527 | dev_handle, s->sas_addr, dev_handle, i, |
528 | (dev |
529 | ? MPI_SAS_DEVICE_INFO_END_DEVICE /* | MPI_SAS_DEVICE_INFO_SSP_TARGET?? */ |
530 | : MPI_SAS_DEVICE_INFO_NO_DEVICE), |
531 | (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5, |
532 | (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5); |
533 | } |
534 | |
535 | static |
536 | size_t mptsas_config_phy_1(MPTSASState *s, uint8_t **data, int address) |
537 | { |
538 | int phy_handle = -1; |
539 | int dev_handle = -1; |
540 | int i = mptsas_phy_addr_get(s, address); |
541 | |
542 | if (i < 0) { |
543 | trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1); |
544 | return i; |
545 | } |
546 | |
547 | (void) mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); |
548 | trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1); |
549 | |
550 | return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01, |
551 | "*l*l*l*l*l" ); |
552 | } |
553 | |
554 | /* SAS device pages (extended) */ |
555 | |
556 | static int mptsas_device_addr_get(MPTSASState *s, int address) |
557 | { |
558 | uint32_t handle, i; |
559 | uint32_t form = address >> MPI_SAS_PHY_PGAD_FORM_SHIFT; |
560 | if (form == MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) { |
561 | handle = address & MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK; |
562 | do { |
563 | if (handle == 65535) { |
564 | handle = MPTSAS_NUM_PORTS + 1; |
565 | } else { |
566 | ++handle; |
567 | } |
568 | i = handle - 1 - MPTSAS_NUM_PORTS; |
569 | } while (i < MPTSAS_NUM_PORTS && !scsi_device_find(&s->bus, 0, i, 0)); |
570 | |
571 | } else if (form == MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID) { |
572 | if (address & MPI_SAS_DEVICE_PGAD_BT_BUS_MASK) { |
573 | return -EINVAL; |
574 | } |
575 | i = address & MPI_SAS_DEVICE_PGAD_BT_TID_MASK; |
576 | |
577 | } else if (form == MPI_SAS_DEVICE_PGAD_FORM_HANDLE) { |
578 | handle = address & MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK; |
579 | i = handle - 1 - MPTSAS_NUM_PORTS; |
580 | |
581 | } else { |
582 | return -EINVAL; |
583 | } |
584 | |
585 | if (i >= MPTSAS_NUM_PORTS) { |
586 | return -EINVAL; |
587 | } |
588 | |
589 | return i; |
590 | } |
591 | |
592 | static |
593 | size_t mptsas_config_sas_device_0(MPTSASState *s, uint8_t **data, int address) |
594 | { |
595 | int phy_handle = -1; |
596 | int dev_handle = -1; |
597 | int i = mptsas_device_addr_get(s, address); |
598 | SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); |
599 | |
600 | trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 0); |
601 | if (!dev) { |
602 | return -ENOENT; |
603 | } |
604 | |
605 | return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x05, |
606 | "*w*wqwbbwbblwb*b" , |
607 | dev->wwn, phy_handle, i, |
608 | MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS, |
609 | dev_handle, i, 0, |
610 | MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET, |
611 | (MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT | |
612 | MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED | |
613 | MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT), i); |
614 | } |
615 | |
616 | static |
617 | size_t mptsas_config_sas_device_1(MPTSASState *s, uint8_t **data, int address) |
618 | { |
619 | int phy_handle = -1; |
620 | int dev_handle = -1; |
621 | int i = mptsas_device_addr_get(s, address); |
622 | SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); |
623 | |
624 | trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 1); |
625 | if (!dev) { |
626 | return -ENOENT; |
627 | } |
628 | |
629 | return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x00, |
630 | "*lq*lwbb*s20" , |
631 | dev->wwn, dev_handle, i, 0); |
632 | } |
633 | |
634 | static |
635 | size_t mptsas_config_sas_device_2(MPTSASState *s, uint8_t **data, int address) |
636 | { |
637 | int phy_handle = -1; |
638 | int dev_handle = -1; |
639 | int i = mptsas_device_addr_get(s, address); |
640 | SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); |
641 | |
642 | trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 2); |
643 | if (!dev) { |
644 | return -ENOENT; |
645 | } |
646 | |
647 | return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x01, |
648 | "ql" , dev->wwn, 0); |
649 | } |
650 | |
651 | typedef struct MPTSASConfigPage { |
652 | uint8_t number; |
653 | uint8_t type; |
654 | size_t (*mpt_config_build)(MPTSASState *s, uint8_t **data, int address); |
655 | } MPTSASConfigPage; |
656 | |
657 | static const MPTSASConfigPage mptsas_config_pages[] = { |
658 | { |
659 | 0, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
660 | mptsas_config_manufacturing_0, |
661 | }, { |
662 | 1, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
663 | mptsas_config_manufacturing_1, |
664 | }, { |
665 | 2, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
666 | mptsas_config_manufacturing_2, |
667 | }, { |
668 | 3, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
669 | mptsas_config_manufacturing_3, |
670 | }, { |
671 | 4, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
672 | mptsas_config_manufacturing_4, |
673 | }, { |
674 | 5, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
675 | mptsas_config_manufacturing_5, |
676 | }, { |
677 | 6, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
678 | mptsas_config_manufacturing_6, |
679 | }, { |
680 | 7, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
681 | mptsas_config_manufacturing_7, |
682 | }, { |
683 | 8, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
684 | mptsas_config_manufacturing_8, |
685 | }, { |
686 | 9, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
687 | mptsas_config_manufacturing_9, |
688 | }, { |
689 | 10, MPI_CONFIG_PAGETYPE_MANUFACTURING, |
690 | mptsas_config_manufacturing_10, |
691 | }, { |
692 | 0, MPI_CONFIG_PAGETYPE_IO_UNIT, |
693 | mptsas_config_io_unit_0, |
694 | }, { |
695 | 1, MPI_CONFIG_PAGETYPE_IO_UNIT, |
696 | mptsas_config_io_unit_1, |
697 | }, { |
698 | 2, MPI_CONFIG_PAGETYPE_IO_UNIT, |
699 | mptsas_config_io_unit_2, |
700 | }, { |
701 | 3, MPI_CONFIG_PAGETYPE_IO_UNIT, |
702 | mptsas_config_io_unit_3, |
703 | }, { |
704 | 4, MPI_CONFIG_PAGETYPE_IO_UNIT, |
705 | mptsas_config_io_unit_4, |
706 | }, { |
707 | 0, MPI_CONFIG_PAGETYPE_IOC, |
708 | mptsas_config_ioc_0, |
709 | }, { |
710 | 1, MPI_CONFIG_PAGETYPE_IOC, |
711 | mptsas_config_ioc_1, |
712 | }, { |
713 | 2, MPI_CONFIG_PAGETYPE_IOC, |
714 | mptsas_config_ioc_2, |
715 | }, { |
716 | 3, MPI_CONFIG_PAGETYPE_IOC, |
717 | mptsas_config_ioc_3, |
718 | }, { |
719 | 4, MPI_CONFIG_PAGETYPE_IOC, |
720 | mptsas_config_ioc_4, |
721 | }, { |
722 | 5, MPI_CONFIG_PAGETYPE_IOC, |
723 | mptsas_config_ioc_5, |
724 | }, { |
725 | 6, MPI_CONFIG_PAGETYPE_IOC, |
726 | mptsas_config_ioc_6, |
727 | }, { |
728 | 0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, |
729 | mptsas_config_sas_io_unit_0, |
730 | }, { |
731 | 1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, |
732 | mptsas_config_sas_io_unit_1, |
733 | }, { |
734 | 2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, |
735 | mptsas_config_sas_io_unit_2, |
736 | }, { |
737 | 3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, |
738 | mptsas_config_sas_io_unit_3, |
739 | }, { |
740 | 0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, |
741 | mptsas_config_phy_0, |
742 | }, { |
743 | 1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, |
744 | mptsas_config_phy_1, |
745 | }, { |
746 | 0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, |
747 | mptsas_config_sas_device_0, |
748 | }, { |
749 | 1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, |
750 | mptsas_config_sas_device_1, |
751 | }, { |
752 | 2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, |
753 | mptsas_config_sas_device_2, |
754 | } |
755 | }; |
756 | |
757 | static const MPTSASConfigPage *mptsas_find_config_page(int type, int number) |
758 | { |
759 | const MPTSASConfigPage *page; |
760 | int i; |
761 | |
762 | for (i = 0; i < ARRAY_SIZE(mptsas_config_pages); i++) { |
763 | page = &mptsas_config_pages[i]; |
764 | if (page->type == type && page->number == number) { |
765 | return page; |
766 | } |
767 | } |
768 | |
769 | return NULL; |
770 | } |
771 | |
772 | void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req) |
773 | { |
774 | PCIDevice *pci = PCI_DEVICE(s); |
775 | |
776 | MPIMsgConfigReply reply; |
777 | const MPTSASConfigPage *page; |
778 | size_t length; |
779 | uint8_t type; |
780 | uint8_t *data = NULL; |
781 | uint32_t flags_and_length; |
782 | uint32_t dmalen; |
783 | uint64_t pa; |
784 | |
785 | mptsas_fix_config_endianness(req); |
786 | |
787 | QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); |
788 | QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); |
789 | |
790 | /* Copy common bits from the request into the reply. */ |
791 | memset(&reply, 0, sizeof(reply)); |
792 | reply.Action = req->Action; |
793 | reply.Function = req->Function; |
794 | reply.MsgContext = req->MsgContext; |
795 | reply.MsgLength = sizeof(reply) / 4; |
796 | reply.PageType = req->PageType; |
797 | reply.PageNumber = req->PageNumber; |
798 | reply.PageLength = req->PageLength; |
799 | reply.PageVersion = req->PageVersion; |
800 | |
801 | type = req->PageType & MPI_CONFIG_PAGETYPE_MASK; |
802 | if (type == MPI_CONFIG_PAGETYPE_EXTENDED) { |
803 | type = req->ExtPageType; |
804 | if (type <= MPI_CONFIG_PAGETYPE_MASK) { |
805 | reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE; |
806 | goto out; |
807 | } |
808 | |
809 | reply.ExtPageType = req->ExtPageType; |
810 | } |
811 | |
812 | page = mptsas_find_config_page(type, req->PageNumber); |
813 | |
814 | switch(req->Action) { |
815 | case MPI_CONFIG_ACTION_PAGE_DEFAULT: |
816 | case MPI_CONFIG_ACTION_PAGE_HEADER: |
817 | case MPI_CONFIG_ACTION_PAGE_READ_NVRAM: |
818 | case MPI_CONFIG_ACTION_PAGE_READ_CURRENT: |
819 | case MPI_CONFIG_ACTION_PAGE_READ_DEFAULT: |
820 | case MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT: |
821 | case MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM: |
822 | break; |
823 | |
824 | default: |
825 | reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_ACTION; |
826 | goto out; |
827 | } |
828 | |
829 | if (!page) { |
830 | page = mptsas_find_config_page(type, 1); |
831 | if (page) { |
832 | reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; |
833 | } else { |
834 | reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE; |
835 | } |
836 | goto out; |
837 | } |
838 | |
839 | if (req->Action == MPI_CONFIG_ACTION_PAGE_DEFAULT || |
840 | req->Action == MPI_CONFIG_ACTION_PAGE_HEADER) { |
841 | length = page->mpt_config_build(s, NULL, req->PageAddress); |
842 | if ((ssize_t)length < 0) { |
843 | reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; |
844 | goto out; |
845 | } else { |
846 | goto done; |
847 | } |
848 | } |
849 | |
850 | if (req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT || |
851 | req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) { |
852 | length = page->mpt_config_build(s, NULL, req->PageAddress); |
853 | if ((ssize_t)length < 0) { |
854 | reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; |
855 | } else { |
856 | reply.IOCStatus = MPI_IOCSTATUS_CONFIG_CANT_COMMIT; |
857 | } |
858 | goto out; |
859 | } |
860 | |
861 | flags_and_length = req->PageBufferSGE.FlagsLength; |
862 | dmalen = flags_and_length & MPI_SGE_LENGTH_MASK; |
863 | if (dmalen == 0) { |
864 | length = page->mpt_config_build(s, NULL, req->PageAddress); |
865 | if ((ssize_t)length < 0) { |
866 | reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; |
867 | goto out; |
868 | } else { |
869 | goto done; |
870 | } |
871 | } |
872 | |
873 | if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { |
874 | pa = req->PageBufferSGE.u.Address64; |
875 | } else { |
876 | pa = req->PageBufferSGE.u.Address32; |
877 | } |
878 | |
879 | /* Only read actions left. */ |
880 | length = page->mpt_config_build(s, &data, req->PageAddress); |
881 | if ((ssize_t)length < 0) { |
882 | reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; |
883 | goto out; |
884 | } else { |
885 | assert(data[2] == page->number); |
886 | pci_dma_write(pci, pa, data, MIN(length, dmalen)); |
887 | goto done; |
888 | } |
889 | |
890 | abort(); |
891 | |
892 | done: |
893 | if (type > MPI_CONFIG_PAGETYPE_MASK) { |
894 | reply.ExtPageLength = length / 4; |
895 | reply.ExtPageType = req->ExtPageType; |
896 | } else { |
897 | reply.PageLength = length / 4; |
898 | } |
899 | |
900 | out: |
901 | mptsas_fix_config_reply_endianness(&reply); |
902 | mptsas_reply(s, (MPIDefaultReply *)&reply); |
903 | g_free(data); |
904 | } |
905 | |