hw: Add support for LSI SAS1068 (mptsas) device

This adds the SAS1068 device, a SAS disk controller used in VMware that
is oldish but widely supported and has decent performance.  Unlike
megasas, it presents itself as a SAS controller and not as a RAID
controller.  The device corresponds to the mptsas kernel driver in
Linux.

A few small things in the device setup are based on Don Slutz's old
patch, but the device emulation was written from scratch based on Don's
SeaBIOS patch and on the FreeBSD and Linux drivers.  It is 2400 lines
shorter than Don's patch (and roughly the same size as MegaSAS---also
because it doesn't support the similar SPI controller), implements SCSI
task management functions (with asynchronous cancellation), supports
big-endian hosts, has complete support for migration and follows the
QEMU coding standards much more closely.

To write the driver, I first split Don's patch in two parts, with
the configuration bits in one file and the rest in a separate file.
I first left mptconfig.c in place and rewrote the rest, then deleted
mptconfig.c as well.  The configuration pages are still based mostly on
VirtualBox's, though not exactly the same.  However, the implementation
is completely different.  The contents of the pages themselves should
not be copyrightable.

Signed-off-by: Don Slutz <Don@CloudSwitch.com>
Message-Id: <1347382813-5662-1-git-send-email-Don@CloudSwitch.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2015-10-27 18:26:06 +01:00
parent 9fd7e85938
commit e351b82611
9 changed files with 3827 additions and 0 deletions

View file

@ -15,6 +15,7 @@ CONFIG_ES1370=y
CONFIG_LSI_SCSI_PCI=y
CONFIG_VMW_PVSCSI_SCSI_PCI=y
CONFIG_MEGASAS_SCSI_PCI=y
CONFIG_MPTSAS_SCSI_PCI=y
CONFIG_RTL8139_PCI=y
CONFIG_E1000_PCI=y
CONFIG_VMXNET3_PCI=y

View file

@ -1,6 +1,7 @@
common-obj-y += scsi-disk.o
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
common-obj-$(CONFIG_MPTSAS_SCSI_PCI) += mptsas.o mptconfig.o mptendian.o
common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
common-obj-$(CONFIG_VMW_PVSCSI_SCSI_PCI) += vmw_pvscsi.o
common-obj-$(CONFIG_ESP) += esp.o

1153
hw/scsi/mpi.h Normal file

File diff suppressed because it is too large Load diff

904
hw/scsi/mptconfig.c Normal file
View file

@ -0,0 +1,904 @@
/*
* 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);
}
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);
}

204
hw/scsi/mptendian.c Normal file
View file

@ -0,0 +1,204 @@
/*
* QEMU LSI SAS1068 Host Bus Adapter emulation
* Endianness conversion for MPI data structures
*
* Copyright (c) 2016 Red Hat, Inc.
*
* Authors: Paolo Bonzini <pbonzini@redhat.com>
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "sysemu/dma.h"
#include "sysemu/block-backend.h"
#include "hw/pci/msi.h"
#include "qemu/iov.h"
#include "hw/scsi/scsi.h"
#include "block/scsi.h"
#include "trace.h"
#include "mptsas.h"
#include "mpi.h"
static void mptsas_fix_sgentry_endianness(MPISGEntry *sge)
{
le32_to_cpus(&sge->FlagsLength);
if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
le64_to_cpus(&sge->u.Address64);
} else {
le32_to_cpus(&sge->u.Address32);
}
}
static void mptsas_fix_sgentry_endianness_reply(MPISGEntry *sge)
{
if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
cpu_to_le64s(&sge->u.Address64);
} else {
cpu_to_le32s(&sge->u.Address32);
}
cpu_to_le32s(&sge->FlagsLength);
}
void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req)
{
le32_to_cpus(&req->MsgContext);
le32_to_cpus(&req->Control);
le32_to_cpus(&req->DataLength);
le32_to_cpus(&req->SenseBufferLowAddr);
}
void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply)
{
cpu_to_le32s(&reply->MsgContext);
cpu_to_le16s(&reply->IOCStatus);
cpu_to_le32s(&reply->IOCLogInfo);
cpu_to_le32s(&reply->TransferCount);
cpu_to_le32s(&reply->SenseCount);
cpu_to_le32s(&reply->ResponseInfo);
cpu_to_le16s(&reply->TaskTag);
}
void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req)
{
le32_to_cpus(&req->MsgContext);
le32_to_cpus(&req->TaskMsgContext);
}
void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply)
{
cpu_to_le32s(&reply->MsgContext);
cpu_to_le16s(&reply->IOCStatus);
cpu_to_le32s(&reply->IOCLogInfo);
cpu_to_le32s(&reply->TerminationCount);
}
void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req)
{
le32_to_cpus(&req->MsgContext);
le16_to_cpus(&req->ReplyFrameSize);
le32_to_cpus(&req->HostMfaHighAddr);
le32_to_cpus(&req->SenseBufferHighAddr);
le32_to_cpus(&req->ReplyFifoHostSignalingAddr);
mptsas_fix_sgentry_endianness(&req->HostPageBufferSGE);
le16_to_cpus(&req->MsgVersion);
le16_to_cpus(&req->HeaderVersion);
}
void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply)
{
cpu_to_le32s(&reply->MsgContext);
cpu_to_le16s(&reply->IOCStatus);
cpu_to_le32s(&reply->IOCLogInfo);
}
void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req)
{
le32_to_cpus(&req->MsgContext);
}
void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply)
{
cpu_to_le16s(&reply->MsgVersion);
cpu_to_le16s(&reply->HeaderVersion);
cpu_to_le32s(&reply->MsgContext);
cpu_to_le16s(&reply->IOCExceptions);
cpu_to_le16s(&reply->IOCStatus);
cpu_to_le32s(&reply->IOCLogInfo);
cpu_to_le16s(&reply->ReplyQueueDepth);
cpu_to_le16s(&reply->RequestFrameSize);
cpu_to_le16s(&reply->ProductID);
cpu_to_le32s(&reply->CurrentHostMfaHighAddr);
cpu_to_le16s(&reply->GlobalCredits);
cpu_to_le32s(&reply->CurrentSenseBufferHighAddr);
cpu_to_le16s(&reply->CurReplyFrameSize);
cpu_to_le32s(&reply->FWImageSize);
cpu_to_le32s(&reply->IOCCapabilities);
cpu_to_le16s(&reply->HighPriorityQueueDepth);
mptsas_fix_sgentry_endianness_reply(&reply->HostPageBufferSGE);
cpu_to_le32s(&reply->ReplyFifoHostSignalingAddr);
}
void mptsas_fix_config_endianness(MPIMsgConfig *req)
{
le16_to_cpus(&req->ExtPageLength);
le32_to_cpus(&req->MsgContext);
le32_to_cpus(&req->PageAddress);
mptsas_fix_sgentry_endianness(&req->PageBufferSGE);
}
void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply)
{
cpu_to_le16s(&reply->ExtPageLength);
cpu_to_le32s(&reply->MsgContext);
cpu_to_le16s(&reply->IOCStatus);
cpu_to_le32s(&reply->IOCLogInfo);
}
void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req)
{
le32_to_cpus(&req->MsgContext);
}
void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply)
{
cpu_to_le32s(&reply->MsgContext);
cpu_to_le16s(&reply->IOCStatus);
cpu_to_le32s(&reply->IOCLogInfo);
cpu_to_le16s(&reply->MaxDevices);
cpu_to_le16s(&reply->PortSCSIID);
cpu_to_le16s(&reply->ProtocolFlags);
cpu_to_le16s(&reply->MaxPostedCmdBuffers);
cpu_to_le16s(&reply->MaxPersistentIDs);
cpu_to_le16s(&reply->MaxLanBuckets);
}
void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req)
{
le32_to_cpus(&req->MsgContext);
}
void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply)
{
cpu_to_le32s(&reply->MsgContext);
cpu_to_le16s(&reply->IOCStatus);
cpu_to_le32s(&reply->IOCLogInfo);
}
void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req)
{
le32_to_cpus(&req->MsgContext);
}
void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply)
{
int length = reply->EventDataLength;
int i;
cpu_to_le16s(&reply->EventDataLength);
cpu_to_le32s(&reply->MsgContext);
cpu_to_le16s(&reply->IOCStatus);
cpu_to_le32s(&reply->IOCLogInfo);
cpu_to_le32s(&reply->Event);
cpu_to_le32s(&reply->EventContext);
/* Really depends on the event kind. This will do for now. */
for (i = 0; i < length; i++) {
cpu_to_le32s(&reply->Data[i]);
}
}

1441
hw/scsi/mptsas.c Normal file

File diff suppressed because it is too large Load diff

100
hw/scsi/mptsas.h Normal file
View file

@ -0,0 +1,100 @@
#ifndef MPTSAS_H
#define MPTSAS_H
#include "mpi.h"
#define MPTSAS_NUM_PORTS 8
#define MPTSAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */
#define MPTSAS_REQUEST_QUEUE_DEPTH 128
#define MPTSAS_REPLY_QUEUE_DEPTH 128
#define MPTSAS_MAXIMUM_CHAIN_DEPTH 0x22
typedef struct MPTSASState MPTSASState;
typedef struct MPTSASRequest MPTSASRequest;
enum {
DOORBELL_NONE,
DOORBELL_WRITE,
DOORBELL_READ
};
struct MPTSASState {
PCIDevice dev;
MemoryRegion mmio_io;
MemoryRegion port_io;
MemoryRegion diag_io;
QEMUBH *request_bh;
uint32_t msi_available;
uint64_t sas_addr;
bool msi_in_use;
/* Doorbell register */
uint32_t state;
uint8_t who_init;
uint8_t doorbell_state;
/* Buffer for requests that are sent through the doorbell register. */
uint32_t doorbell_msg[256];
int doorbell_idx;
int doorbell_cnt;
uint16_t doorbell_reply[256];
int doorbell_reply_idx;
int doorbell_reply_size;
/* Other registers */
uint8_t diagnostic_idx;
uint32_t diagnostic;
uint32_t intr_mask;
uint32_t intr_status;
/* Request queues */
uint32_t request_post[MPTSAS_REQUEST_QUEUE_DEPTH + 1];
uint16_t request_post_head;
uint16_t request_post_tail;
uint32_t reply_post[MPTSAS_REPLY_QUEUE_DEPTH + 1];
uint16_t reply_post_head;
uint16_t reply_post_tail;
uint32_t reply_free[MPTSAS_REPLY_QUEUE_DEPTH + 1];
uint16_t reply_free_head;
uint16_t reply_free_tail;
/* IOC Facts */
hwaddr host_mfa_high_addr;
hwaddr sense_buffer_high_addr;
uint16_t max_devices;
uint16_t max_buses;
uint16_t reply_frame_size;
SCSIBus bus;
QTAILQ_HEAD(, MPTSASRequest) pending;
};
void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req);
void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply);
void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req);
void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply);
void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req);
void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply);
void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req);
void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply);
void mptsas_fix_config_endianness(MPIMsgConfig *req);
void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply);
void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req);
void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply);
void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req);
void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply);
void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req);
void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply);
void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply);
void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req);
#endif /* MPTSAS_H */

View file

@ -64,6 +64,7 @@
#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
#define PCI_DEVICE_ID_LSI_53C810 0x0001
#define PCI_DEVICE_ID_LSI_53C895A 0x0012
#define PCI_DEVICE_ID_LSI_SAS1068 0x0054
#define PCI_DEVICE_ID_LSI_SAS1078 0x0060
#define PCI_DEVICE_ID_LSI_SAS0079 0x0079

View file

@ -726,6 +726,28 @@ lm32_uart_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
lm32_uart_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
lm32_uart_irq_state(int level) "irq state %d"
# hw/scsi/mptsas.c
mptsas_command_complete(void *dev, uint32_t ctx, uint32_t status, uint32_t resid) "dev %p context 0x%08x status %x resid %d"
mptsas_diag_read(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%08x"
mptsas_diag_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%08x"
mptsas_irq_intx(void *dev, int level) "dev %p level %d"
mptsas_irq_msi(void *dev) "dev %p "
mptsas_mmio_read(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%x"
mptsas_mmio_unhandled_read(void *dev, uint32_t addr) "dev %p addr 0x%08x"
mptsas_mmio_unhandled_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%x"
mptsas_mmio_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%x"
mptsas_process_message(void *dev, int msg, uint32_t ctx) "dev %p cmd %d context 0x%08x\n"
mptsas_process_scsi_io_request(void *dev, int bus, int target, int lun, uint64_t len) "dev %p dev %d:%d:%d length %"PRIu64""
mptsas_reset(void *dev) "dev %p "
mptsas_scsi_overflow(void *dev, uint32_t ctx, uint64_t req, uint64_t found) "dev %p context 0x%08x: %"PRIu64"/%"PRIu64""
mptsas_sgl_overflow(void *dev, uint32_t ctx, uint64_t req, uint64_t found) "dev %p context 0x%08x: %"PRIu64"/%"PRIu64""
mptsas_unhandled_cmd(void *dev, uint32_t ctx, uint8_t msg_cmd) "dev %p context 0x%08x: Unhandled cmd %x"
mptsas_unhandled_doorbell_cmd(void *dev, int cmd) "dev %p value 0x%08x"
# hw/scsi/mptconfig.c
mptsas_config_sas_device(void *dev, int address, int port, int phy_handle, int dev_handle, int page) "dev %p address %d (port %d, handles: phy %d dev %d) page %d"
mptsas_config_sas_phy(void *dev, int address, int port, int phy_handle, int dev_handle, int page) "dev %p address %d (port %d, handles: phy %d dev %d) page %d"
# hw/scsi/megasas.c
megasas_init_firmware(uint64_t pa) "pa %" PRIx64 " "
megasas_init_queue(uint64_t queue_pa, int queue_len, uint64_t head, uint64_t tail, uint32_t flags) "queue at %" PRIx64 " len %d head %" PRIx64 " tail %" PRIx64 " flags %x"