| /* |
| * 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 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/hw.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 < 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->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); |
| } |