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