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