| /* | 
 |  * QEMU LSI SAS1068 Host Bus Adapter emulation - configuration pages | 
 |  * | 
 |  * Copyright (c) 2016 Red Hat, Inc. | 
 |  * | 
 |  * Author: Paolo Bonzini | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  */ | 
 | #include "qemu/osdep.h" | 
 | #include "hw/pci/pci.h" | 
 | #include "hw/scsi/scsi.h" | 
 |  | 
 | #include "mptsas.h" | 
 | #include "mpi.h" | 
 | #include "trace.h" | 
 |  | 
 | /* Generic functions for marshaling and unmarshaling.  */ | 
 |  | 
 | #define repl1(x) x | 
 | #define repl2(x) x x | 
 | #define repl3(x) x x x | 
 | #define repl4(x) x x x x | 
 | #define repl5(x) x x x x x | 
 | #define repl6(x) x x x x x x | 
 | #define repl7(x) x x x x x x x | 
 | #define repl8(x) x x x x x x x x | 
 |  | 
 | #define repl(n, x) glue(repl, n)(x) | 
 |  | 
 | typedef union PackValue { | 
 |     uint64_t ll; | 
 |     char *str; | 
 | } PackValue; | 
 |  | 
 | static size_t vfill(uint8_t *data, size_t size, const char *fmt, va_list ap) | 
 | { | 
 |     size_t ofs; | 
 |     PackValue val; | 
 |     const char *p; | 
 |  | 
 |     ofs = 0; | 
 |     p = fmt; | 
 |     while (*p) { | 
 |         memset(&val, 0, sizeof(val)); | 
 |         switch (*p) { | 
 |         case '*': | 
 |             p++; | 
 |             break; | 
 |         case 'b': | 
 |         case 'w': | 
 |         case 'l': | 
 |             val.ll = va_arg(ap, int); | 
 |             break; | 
 |         case 'q': | 
 |             val.ll = va_arg(ap, int64_t); | 
 |             break; | 
 |         case 's': | 
 |             val.str = va_arg(ap, void *); | 
 |             break; | 
 |         } | 
 |         switch (*p++) { | 
 |         case 'b': | 
 |             if (data) { | 
 |                 stb_p(data + ofs, val.ll); | 
 |             } | 
 |             ofs++; | 
 |             break; | 
 |         case 'w': | 
 |             if (data) { | 
 |                 stw_le_p(data + ofs, val.ll); | 
 |             } | 
 |             ofs += 2; | 
 |             break; | 
 |         case 'l': | 
 |             if (data) { | 
 |                 stl_le_p(data + ofs, val.ll); | 
 |             } | 
 |             ofs += 4; | 
 |             break; | 
 |         case 'q': | 
 |             if (data) { | 
 |                 stq_le_p(data + ofs, val.ll); | 
 |             } | 
 |             ofs += 8; | 
 |             break; | 
 |         case 's': | 
 |             { | 
 |                 int cnt = atoi(p); | 
 |                 if (data) { | 
 |                     if (val.str) { | 
 |                         strncpy((void *)data + ofs, val.str, cnt); | 
 |                     } else { | 
 |                         memset((void *)data + ofs, 0, cnt); | 
 |                     } | 
 |                 } | 
 |                 ofs += cnt; | 
 |                 break; | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     return ofs; | 
 | } | 
 |  | 
 | static size_t vpack(uint8_t **p_data, const char *fmt, va_list ap1) | 
 | { | 
 |     size_t size = 0; | 
 |     uint8_t *data = NULL; | 
 |  | 
 |     if (p_data) { | 
 |         va_list ap2; | 
 |  | 
 |         va_copy(ap2, ap1); | 
 |         size = vfill(NULL, 0, fmt, ap2); | 
 |         *p_data = data = g_malloc(size); | 
 |         va_end(ap2); | 
 |     } | 
 |     return vfill(data, size, fmt, ap1); | 
 | } | 
 |  | 
 | static size_t fill(uint8_t *data, size_t size, const char *fmt, ...) | 
 | { | 
 |     va_list ap; | 
 |     size_t ret; | 
 |  | 
 |     va_start(ap, fmt); | 
 |     ret = vfill(data, size, fmt, ap); | 
 |     va_end(ap); | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | /* Functions to build the page header and fill in the length, always used | 
 |  * through the macros. | 
 |  */ | 
 |  | 
 | #define MPTSAS_CONFIG_PACK(number, type, version, fmt, ...)                  \ | 
 |     mptsas_config_pack(data, "b*bbb" fmt, version, number, type,             \ | 
 |                        ## __VA_ARGS__) | 
 |  | 
 | static size_t mptsas_config_pack(uint8_t **data, const char *fmt, ...) | 
 | { | 
 |     va_list ap; | 
 |     size_t ret; | 
 |  | 
 |     va_start(ap, fmt); | 
 |     ret = vpack(data, fmt, ap); | 
 |     va_end(ap); | 
 |  | 
 |     if (data) { | 
 |         assert(ret / 4 < 256 && (ret % 4) == 0); | 
 |         stb_p(*data + 1, ret / 4); | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | #define MPTSAS_CONFIG_PACK_EXT(number, type, version, fmt, ...)              \ | 
 |     mptsas_config_pack_ext(data, "b*bbb*wb*b" fmt, version, number,          \ | 
 |                            MPI_CONFIG_PAGETYPE_EXTENDED, type, ## __VA_ARGS__) | 
 |  | 
 | static size_t mptsas_config_pack_ext(uint8_t **data, const char *fmt, ...) | 
 | { | 
 |     va_list ap; | 
 |     size_t ret; | 
 |  | 
 |     va_start(ap, fmt); | 
 |     ret = vpack(data, fmt, ap); | 
 |     va_end(ap); | 
 |  | 
 |     if (data) { | 
 |         assert(ret < 65536 && (ret % 4) == 0); | 
 |         stw_le_p(*data + 4, ret / 4); | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | /* Manufacturing pages */ | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_0(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, | 
 |                               "s16s8s16s16s16", | 
 |                               "QEMU MPT Fusion", | 
 |                               "2.5", | 
 |                               "QEMU MPT Fusion", | 
 |                               "QEMU", | 
 |                               "0000111122223333"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_1(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     /* VPD - all zeros */ | 
 |     return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, | 
 |                               "*s256"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_2(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); | 
 |     return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, | 
 |                               "wb*b*l", | 
 |                               pcic->device_id, pcic->revision); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_3(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); | 
 |     return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, | 
 |                               "wb*b*l", | 
 |                               pcic->device_id, pcic->revision); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_4(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     /* All zeros */ | 
 |     return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x05, | 
 |                               "*l*b*b*b*b*b*b*w*s56*l*l*l*l*l*l" | 
 |                               "*b*b*w*b*b*w*l*l"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_5(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x02, | 
 |                               "q*b*b*w*l*l", s->sas_addr); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_6(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, | 
 |                               "*l"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_7(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(7, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, | 
 |                               "*l*l*l*s16*b*b*w", MPTSAS_NUM_PORTS); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_8(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(8, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, | 
 |                               "*l"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_9(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(9, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, | 
 |                               "*l"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_manufacturing_10(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(10, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, | 
 |                               "*l"); | 
 | } | 
 |  | 
 | /* I/O unit pages */ | 
 |  | 
 | static | 
 | size_t mptsas_config_io_unit_0(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     PCIDevice *pci = PCI_DEVICE(s); | 
 |     uint64_t unique_value = 0x53504D554D4551LL;  /* "QEMUMPTx" */ | 
 |  | 
 |     unique_value |= (uint64_t)pci->devfn << 56; | 
 |     return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, | 
 |                               "q", unique_value); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_io_unit_1(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, "l", | 
 |                               0x41 /* single function, RAID disabled */ ); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_io_unit_2(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     PCIDevice *pci = PCI_DEVICE(s); | 
 |     uint8_t devfn = pci->devfn; | 
 |     return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, | 
 |                               "llbbw*b*b*w*b*b*w*b*b*w*l", | 
 |                               0, 0x100, 0 /* pci bus? */, devfn, 0); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_io_unit_3(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x01, | 
 |                               "*b*b*w*l"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_io_unit_4(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, "*l*l*q"); | 
 | } | 
 |  | 
 | /* I/O controller pages */ | 
 |  | 
 | static | 
 | size_t mptsas_config_ioc_0(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); | 
 |  | 
 |     return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IOC, 0x01, | 
 |                               "*l*lwwb*b*b*blww", | 
 |                               pcic->vendor_id, pcic->device_id, pcic->revision, | 
 |                               pcic->class_id, pcic->subsystem_vendor_id, | 
 |                               pcic->subsystem_id); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_ioc_1(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IOC, 0x03, | 
 |                               "*l*l*b*b*b*b"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_ioc_2(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IOC, 0x04, | 
 |                               "*l*b*b*b*b"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_ioc_3(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IOC, 0x00, | 
 |                               "*b*b*w"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_ioc_4(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IOC, 0x00, | 
 |                               "*b*b*w"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_ioc_5(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_IOC, 0x00, | 
 |                               "*l*b*b*w"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_ioc_6(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_IOC, 0x01, | 
 |                               "*l*b*b*b*b*b*b*b*b*b*b*w*l*l*l*l*b*b*w" | 
 |                               "*w*w*w*w*l*l*l"); | 
 | } | 
 |  | 
 | /* SAS I/O unit pages (extended) */ | 
 |  | 
 | #define MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE 16 | 
 |  | 
 | #define MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION 0x02 | 
 | #define MPI_SAS_IOUNIT0_RATE_1_5                      0x08 | 
 | #define MPI_SAS_IOUNIT0_RATE_3_0                      0x09 | 
 |  | 
 | #define MPI_SAS_DEVICE_INFO_NO_DEVICE                 0x00000000 | 
 | #define MPI_SAS_DEVICE_INFO_END_DEVICE                0x00000001 | 
 | #define MPI_SAS_DEVICE_INFO_SSP_TARGET                0x00000400 | 
 |  | 
 | #define MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS             0x00 | 
 |  | 
 | #define MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT          0x0001 | 
 | #define MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED           0x0002 | 
 | #define MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT      0x0004 | 
 |  | 
 |  | 
 |  | 
 | static SCSIDevice *mptsas_phy_get_device(MPTSASState *s, int i, | 
 |                                          int *phy_handle, int *dev_handle) | 
 | { | 
 |     SCSIDevice *d = scsi_device_find(&s->bus, 0, i, 0); | 
 |  | 
 |     if (phy_handle) { | 
 |         *phy_handle = i + 1; | 
 |     } | 
 |     if (dev_handle) { | 
 |         *dev_handle = d ? i + 1 + MPTSAS_NUM_PORTS : 0; | 
 |     } | 
 |     return d; | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_sas_io_unit_0(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     size_t size = MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x04, | 
 |                                          "*w*wb*b*w" | 
 |                                          repl(MPTSAS_NUM_PORTS, "*s16"), | 
 |                                          MPTSAS_NUM_PORTS); | 
 |  | 
 |     if (data) { | 
 |         size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE; | 
 |         int i; | 
 |  | 
 |         for (i = 0; i < MPTSAS_NUM_PORTS; i++) { | 
 |             int phy_handle, dev_handle; | 
 |             SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); | 
 |  | 
 |             fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE, | 
 |                  "bbbblwwl", i, 0, 0, | 
 |                  (dev | 
 |                   ? MPI_SAS_IOUNIT0_RATE_3_0 | 
 |                   : MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION), | 
 |                  (dev | 
 |                   ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET | 
 |                   : MPI_SAS_DEVICE_INFO_NO_DEVICE), | 
 |                  dev_handle, | 
 |                  dev_handle, | 
 |                  0); | 
 |             ofs += MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE; | 
 |         } | 
 |         assert(ofs == size); | 
 |     } | 
 |     return size; | 
 | } | 
 |  | 
 | #define MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE 12 | 
 |  | 
 | static | 
 | size_t mptsas_config_sas_io_unit_1(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     size_t size = MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x07, | 
 |                                          "*w*w*w*wb*b*b*b" | 
 |                                          repl(MPTSAS_NUM_PORTS, "*s12"), | 
 |                                          MPTSAS_NUM_PORTS); | 
 |  | 
 |     if (data) { | 
 |         size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE; | 
 |         int i; | 
 |  | 
 |         for (i = 0; i < MPTSAS_NUM_PORTS; i++) { | 
 |             SCSIDevice *dev = mptsas_phy_get_device(s, i, NULL, NULL); | 
 |             fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE, | 
 |                  "bbbblww", i, 0, 0, | 
 |                  (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5, | 
 |                  (dev | 
 |                   ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET | 
 |                   : MPI_SAS_DEVICE_INFO_NO_DEVICE), | 
 |                  0, 0); | 
 |             ofs += MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE; | 
 |         } | 
 |         assert(ofs == size); | 
 |     } | 
 |     return size; | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_sas_io_unit_2(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06, | 
 |                                   "*b*b*w*w*w*b*b*w"); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_sas_io_unit_3(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     return MPTSAS_CONFIG_PACK_EXT(3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06, | 
 |                                   "*l*l*l*l*l*l*l*l*l"); | 
 | } | 
 |  | 
 | /* SAS PHY pages (extended) */ | 
 |  | 
 | static int mptsas_phy_addr_get(MPTSASState *s, int address) | 
 | { | 
 |     int i; | 
 |     if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 0) { | 
 |         i = address & 255; | 
 |     } else if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 1) { | 
 |         i = address & 65535; | 
 |     } else { | 
 |         return -EINVAL; | 
 |     } | 
 |  | 
 |     if (i >= MPTSAS_NUM_PORTS) { | 
 |         return -EINVAL; | 
 |     } | 
 |  | 
 |     return i; | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_phy_0(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     int phy_handle = -1; | 
 |     int dev_handle = -1; | 
 |     int i = mptsas_phy_addr_get(s, address); | 
 |     SCSIDevice *dev; | 
 |  | 
 |     if (i < 0) { | 
 |         trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0); | 
 |         return i; | 
 |     } | 
 |  | 
 |     dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); | 
 |     trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0); | 
 |  | 
 |     return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01, | 
 |                                   "w*wqwb*blbb*b*b*l", | 
 |                                   dev_handle, s->sas_addr, dev_handle, i, | 
 |                                   (dev | 
 |                                    ? MPI_SAS_DEVICE_INFO_END_DEVICE /* | MPI_SAS_DEVICE_INFO_SSP_TARGET?? */ | 
 |                                    : MPI_SAS_DEVICE_INFO_NO_DEVICE), | 
 |                                   (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5, | 
 |                                   (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_phy_1(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     int phy_handle = -1; | 
 |     int dev_handle = -1; | 
 |     int i = mptsas_phy_addr_get(s, address); | 
 |  | 
 |     if (i < 0) { | 
 |         trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1); | 
 |         return i; | 
 |     } | 
 |  | 
 |     (void) mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); | 
 |     trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1); | 
 |  | 
 |     return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01, | 
 |                                   "*l*l*l*l*l"); | 
 | } | 
 |  | 
 | /* SAS device pages (extended) */ | 
 |  | 
 | static int mptsas_device_addr_get(MPTSASState *s, int address) | 
 | { | 
 |     uint32_t handle, i; | 
 |     uint32_t form = address >> MPI_SAS_PHY_PGAD_FORM_SHIFT; | 
 |     if (form == MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) { | 
 |         handle = address & MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK; | 
 |         do { | 
 |             if (handle == 65535) { | 
 |                 handle = MPTSAS_NUM_PORTS + 1; | 
 |             } else { | 
 |                 ++handle; | 
 |             } | 
 |             i = handle - 1 - MPTSAS_NUM_PORTS; | 
 |         } while (i < MPTSAS_NUM_PORTS && !scsi_device_find(&s->bus, 0, i, 0)); | 
 |  | 
 |     } else if (form == MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID) { | 
 |         if (address & MPI_SAS_DEVICE_PGAD_BT_BUS_MASK) { | 
 |             return -EINVAL; | 
 |         } | 
 |         i = address & MPI_SAS_DEVICE_PGAD_BT_TID_MASK; | 
 |  | 
 |     } else if (form == MPI_SAS_DEVICE_PGAD_FORM_HANDLE) { | 
 |         handle = address & MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK; | 
 |         i = handle - 1 - MPTSAS_NUM_PORTS; | 
 |  | 
 |     } else { | 
 |         return -EINVAL; | 
 |     } | 
 |  | 
 |     if (i >= MPTSAS_NUM_PORTS) { | 
 |         return -EINVAL; | 
 |     } | 
 |  | 
 |     return i; | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_sas_device_0(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     int phy_handle = -1; | 
 |     int dev_handle = -1; | 
 |     int i = mptsas_device_addr_get(s, address); | 
 |     SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); | 
 |  | 
 |     trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 0); | 
 |     if (!dev) { | 
 |         return -ENOENT; | 
 |     } | 
 |  | 
 |     return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x05, | 
 |                                   "*w*wqwbbwbblwb*b", | 
 |                                   dev->wwn, phy_handle, i, | 
 |                                   MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS, | 
 |                                   dev_handle, i, 0, | 
 |                                   MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET, | 
 |                                   (MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT | | 
 |                                    MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED | | 
 |                                    MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT), i); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_sas_device_1(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     int phy_handle = -1; | 
 |     int dev_handle = -1; | 
 |     int i = mptsas_device_addr_get(s, address); | 
 |     SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); | 
 |  | 
 |     trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 1); | 
 |     if (!dev) { | 
 |         return -ENOENT; | 
 |     } | 
 |  | 
 |     return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x00, | 
 |                                   "*lq*lwbb*s20", | 
 |                                   dev->wwn, dev_handle, i, 0); | 
 | } | 
 |  | 
 | static | 
 | size_t mptsas_config_sas_device_2(MPTSASState *s, uint8_t **data, int address) | 
 | { | 
 |     int phy_handle = -1; | 
 |     int dev_handle = -1; | 
 |     int i = mptsas_device_addr_get(s, address); | 
 |     SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); | 
 |  | 
 |     trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 2); | 
 |     if (!dev) { | 
 |         return -ENOENT; | 
 |     } | 
 |  | 
 |     return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x01, | 
 |                                   "ql", dev->wwn, 0); | 
 | } | 
 |  | 
 | typedef struct MPTSASConfigPage { | 
 |     uint8_t number; | 
 |     uint8_t type; | 
 |     size_t (*mpt_config_build)(MPTSASState *s, uint8_t **data, int address); | 
 | } MPTSASConfigPage; | 
 |  | 
 | static const MPTSASConfigPage mptsas_config_pages[] = { | 
 |     { | 
 |         0, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_0, | 
 |     }, { | 
 |         1, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_1, | 
 |     }, { | 
 |         2, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_2, | 
 |     }, { | 
 |         3, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_3, | 
 |     }, { | 
 |         4, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_4, | 
 |     }, { | 
 |         5, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_5, | 
 |     }, { | 
 |         6, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_6, | 
 |     }, { | 
 |         7, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_7, | 
 |     }, { | 
 |         8, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_8, | 
 |     }, { | 
 |         9, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_9, | 
 |     }, { | 
 |         10, MPI_CONFIG_PAGETYPE_MANUFACTURING, | 
 |         mptsas_config_manufacturing_10, | 
 |     }, { | 
 |         0, MPI_CONFIG_PAGETYPE_IO_UNIT, | 
 |         mptsas_config_io_unit_0, | 
 |     }, { | 
 |         1, MPI_CONFIG_PAGETYPE_IO_UNIT, | 
 |         mptsas_config_io_unit_1, | 
 |     }, { | 
 |         2, MPI_CONFIG_PAGETYPE_IO_UNIT, | 
 |         mptsas_config_io_unit_2, | 
 |     }, { | 
 |         3, MPI_CONFIG_PAGETYPE_IO_UNIT, | 
 |         mptsas_config_io_unit_3, | 
 |     }, { | 
 |         4, MPI_CONFIG_PAGETYPE_IO_UNIT, | 
 |         mptsas_config_io_unit_4, | 
 |     }, { | 
 |         0, MPI_CONFIG_PAGETYPE_IOC, | 
 |         mptsas_config_ioc_0, | 
 |     }, { | 
 |         1, MPI_CONFIG_PAGETYPE_IOC, | 
 |         mptsas_config_ioc_1, | 
 |     }, { | 
 |         2, MPI_CONFIG_PAGETYPE_IOC, | 
 |         mptsas_config_ioc_2, | 
 |     }, { | 
 |         3, MPI_CONFIG_PAGETYPE_IOC, | 
 |         mptsas_config_ioc_3, | 
 |     }, { | 
 |         4, MPI_CONFIG_PAGETYPE_IOC, | 
 |         mptsas_config_ioc_4, | 
 |     }, { | 
 |         5, MPI_CONFIG_PAGETYPE_IOC, | 
 |         mptsas_config_ioc_5, | 
 |     }, { | 
 |         6, MPI_CONFIG_PAGETYPE_IOC, | 
 |         mptsas_config_ioc_6, | 
 |     }, { | 
 |         0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, | 
 |         mptsas_config_sas_io_unit_0, | 
 |     }, { | 
 |         1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, | 
 |         mptsas_config_sas_io_unit_1, | 
 |     }, { | 
 |         2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, | 
 |         mptsas_config_sas_io_unit_2, | 
 |     }, { | 
 |         3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, | 
 |         mptsas_config_sas_io_unit_3, | 
 |     }, { | 
 |         0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, | 
 |         mptsas_config_phy_0, | 
 |     }, { | 
 |         1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, | 
 |         mptsas_config_phy_1, | 
 |     }, { | 
 |         0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, | 
 |         mptsas_config_sas_device_0, | 
 |     }, { | 
 |         1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, | 
 |         mptsas_config_sas_device_1, | 
 |     }, { | 
 |        2,  MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, | 
 |         mptsas_config_sas_device_2, | 
 |     } | 
 | }; | 
 |  | 
 | static const MPTSASConfigPage *mptsas_find_config_page(int type, int number) | 
 | { | 
 |     const MPTSASConfigPage *page; | 
 |     int i; | 
 |  | 
 |     for (i = 0; i < ARRAY_SIZE(mptsas_config_pages); i++) { | 
 |         page = &mptsas_config_pages[i]; | 
 |         if (page->type == type && page->number == number) { | 
 |             return page; | 
 |         } | 
 |     } | 
 |  | 
 |     return NULL; | 
 | } | 
 |  | 
 | void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req) | 
 | { | 
 |     PCIDevice *pci = PCI_DEVICE(s); | 
 |  | 
 |     MPIMsgConfigReply reply; | 
 |     const MPTSASConfigPage *page; | 
 |     size_t length; | 
 |     uint8_t type; | 
 |     uint8_t *data = NULL; | 
 |     uint32_t flags_and_length; | 
 |     uint32_t dmalen; | 
 |     uint64_t pa; | 
 |  | 
 |     mptsas_fix_config_endianness(req); | 
 |  | 
 |     QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); | 
 |     QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); | 
 |  | 
 |     /* Copy common bits from the request into the reply. */ | 
 |     memset(&reply, 0, sizeof(reply)); | 
 |     reply.Action      = req->Action; | 
 |     reply.Function    = req->Function; | 
 |     reply.MsgContext  = req->MsgContext; | 
 |     reply.MsgLength   = sizeof(reply) / 4; | 
 |     reply.PageType    = req->PageType; | 
 |     reply.PageNumber  = req->PageNumber; | 
 |     reply.PageLength  = req->PageLength; | 
 |     reply.PageVersion = req->PageVersion; | 
 |  | 
 |     type = req->PageType & MPI_CONFIG_PAGETYPE_MASK; | 
 |     if (type == MPI_CONFIG_PAGETYPE_EXTENDED) { | 
 |         type = req->ExtPageType; | 
 |         if (type <= MPI_CONFIG_PAGETYPE_MASK) { | 
 |             reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE; | 
 |             goto out; | 
 |         } | 
 |  | 
 |         reply.ExtPageType = req->ExtPageType; | 
 |     } | 
 |  | 
 |     page = mptsas_find_config_page(type, req->PageNumber); | 
 |  | 
 |     switch(req->Action) { | 
 |     case MPI_CONFIG_ACTION_PAGE_DEFAULT: | 
 |     case MPI_CONFIG_ACTION_PAGE_HEADER: | 
 |     case MPI_CONFIG_ACTION_PAGE_READ_NVRAM: | 
 |     case MPI_CONFIG_ACTION_PAGE_READ_CURRENT: | 
 |     case MPI_CONFIG_ACTION_PAGE_READ_DEFAULT: | 
 |     case MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT: | 
 |     case MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM: | 
 |         break; | 
 |  | 
 |     default: | 
 |         reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_ACTION; | 
 |         goto out; | 
 |     } | 
 |  | 
 |     if (!page) { | 
 |         page = mptsas_find_config_page(type, 1); | 
 |         if (page) { | 
 |             reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; | 
 |         } else { | 
 |             reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE; | 
 |         } | 
 |         goto out; | 
 |     } | 
 |  | 
 |     if (req->Action == MPI_CONFIG_ACTION_PAGE_DEFAULT || | 
 |         req->Action == MPI_CONFIG_ACTION_PAGE_HEADER) { | 
 |         length = page->mpt_config_build(s, NULL, req->PageAddress); | 
 |         if ((ssize_t)length < 0) { | 
 |             reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; | 
 |             goto out; | 
 |         } else { | 
 |             goto done; | 
 |         } | 
 |     } | 
 |  | 
 |     if (req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT || | 
 |         req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) { | 
 |         length = page->mpt_config_build(s, NULL, req->PageAddress); | 
 |         if ((ssize_t)length < 0) { | 
 |             reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; | 
 |         } else { | 
 |             reply.IOCStatus = MPI_IOCSTATUS_CONFIG_CANT_COMMIT; | 
 |         } | 
 |         goto out; | 
 |     } | 
 |  | 
 |     flags_and_length = req->PageBufferSGE.FlagsLength; | 
 |     dmalen = flags_and_length & MPI_SGE_LENGTH_MASK; | 
 |     if (dmalen == 0) { | 
 |         length = page->mpt_config_build(s, NULL, req->PageAddress); | 
 |         if ((ssize_t)length < 0) { | 
 |             reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; | 
 |             goto out; | 
 |         } else { | 
 |             goto done; | 
 |         } | 
 |     } | 
 |  | 
 |     if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { | 
 |         pa = req->PageBufferSGE.u.Address64; | 
 |     } else { | 
 |         pa = req->PageBufferSGE.u.Address32; | 
 |     } | 
 |  | 
 |     /* Only read actions left.  */ | 
 |     length = page->mpt_config_build(s, &data, req->PageAddress); | 
 |     if ((ssize_t)length < 0) { | 
 |         reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; | 
 |         goto out; | 
 |     } else { | 
 |         assert(data[2] == page->number); | 
 |         pci_dma_write(pci, pa, data, MIN(length, dmalen)); | 
 |         goto done; | 
 |     } | 
 |  | 
 |     abort(); | 
 |  | 
 | done: | 
 |     if (type > MPI_CONFIG_PAGETYPE_MASK) { | 
 |         reply.ExtPageLength = length / 4; | 
 |         reply.ExtPageType   = req->ExtPageType; | 
 |     } else { | 
 |         reply.PageLength    = length / 4; | 
 |     } | 
 |  | 
 | out: | 
 |     mptsas_fix_config_reply_endianness(&reply); | 
 |     mptsas_reply(s, (MPIDefaultReply *)&reply); | 
 |     g_free(data); | 
 | } |