 65a8e1f641
			
		
	
	
		65a8e1f641
		
	
	
	
	
		
			
			These issues cause respectively a QEMU crash and a leak of 2 bytes of stack. They were discovered by VictorV of 360 Marvel Team. Reported-by: Tom Victor <i-tangtianwen@360.cm> Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
		
			
				
	
	
		
			906 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			906 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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);
 | |
| }
 |