qemu/hw/scsi/mptconfig.c
<<
>>
Prefs
   1/*
   2 * QEMU LSI SAS1068 Host Bus Adapter emulation - configuration pages
   3 *
   4 * Copyright (c) 2016 Red Hat, Inc.
   5 *
   6 * Author: Paolo Bonzini
   7 *
   8 * This library is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU Lesser General Public
  10 * License as published by the Free Software Foundation; either
  11 * version 2.1 of the License, or (at your option) any later version.
  12 *
  13 * This library is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 * Lesser General Public License for more details.
  17 */
  18#include "qemu/osdep.h"
  19#include "hw/pci/pci.h"
  20#include "hw/scsi/scsi.h"
  21
  22#include "mptsas.h"
  23#include "mpi.h"
  24#include "trace.h"
  25
  26/* Generic functions for marshaling and unmarshaling.  */
  27
  28#define repl1(x) x
  29#define repl2(x) x x
  30#define repl3(x) x x x
  31#define repl4(x) x x x x
  32#define repl5(x) x x x x x
  33#define repl6(x) x x x x x x
  34#define repl7(x) x x x x x x x
  35#define repl8(x) x x x x x x x x
  36
  37#define repl(n, x) glue(repl, n)(x)
  38
  39typedef union PackValue {
  40    uint64_t ll;
  41    char *str;
  42} PackValue;
  43
  44static size_t vfill(uint8_t *data, size_t size, const char *fmt, va_list ap)
  45{
  46    size_t ofs;
  47    PackValue val;
  48    const char *p;
  49
  50    ofs = 0;
  51    p = fmt;
  52    while (*p) {
  53        memset(&val, 0, sizeof(val));
  54        switch (*p) {
  55        case '*':
  56            p++;
  57            break;
  58        case 'b':
  59        case 'w':
  60        case 'l':
  61            val.ll = va_arg(ap, int);
  62            break;
  63        case 'q':
  64            val.ll = va_arg(ap, int64_t);
  65            break;
  66        case 's':
  67            val.str = va_arg(ap, void *);
  68            break;
  69        }
  70        switch (*p++) {
  71        case 'b':
  72            if (data) {
  73                stb_p(data + ofs, val.ll);
  74            }
  75            ofs++;
  76            break;
  77        case 'w':
  78            if (data) {
  79                stw_le_p(data + ofs, val.ll);
  80            }
  81            ofs += 2;
  82            break;
  83        case 'l':
  84            if (data) {
  85                stl_le_p(data + ofs, val.ll);
  86            }
  87            ofs += 4;
  88            break;
  89        case 'q':
  90            if (data) {
  91                stq_le_p(data + ofs, val.ll);
  92            }
  93            ofs += 8;
  94            break;
  95        case 's':
  96            {
  97                int cnt = atoi(p);
  98                if (data) {
  99                    if (val.str) {
 100                        strncpy((void *)data + ofs, val.str, cnt);
 101                    } else {
 102                        memset((void *)data + ofs, 0, cnt);
 103                    }
 104                }
 105                ofs += cnt;
 106                break;
 107            }
 108        }
 109    }
 110
 111    return ofs;
 112}
 113
 114static size_t vpack(uint8_t **p_data, const char *fmt, va_list ap1)
 115{
 116    size_t size = 0;
 117    uint8_t *data = NULL;
 118
 119    if (p_data) {
 120        va_list ap2;
 121
 122        va_copy(ap2, ap1);
 123        size = vfill(NULL, 0, fmt, ap2);
 124        *p_data = data = g_malloc(size);
 125        va_end(ap2);
 126    }
 127    return vfill(data, size, fmt, ap1);
 128}
 129
 130static size_t fill(uint8_t *data, size_t size, const char *fmt, ...)
 131{
 132    va_list ap;
 133    size_t ret;
 134
 135    va_start(ap, fmt);
 136    ret = vfill(data, size, fmt, ap);
 137    va_end(ap);
 138
 139    return ret;
 140}
 141
 142/* Functions to build the page header and fill in the length, always used
 143 * through the macros.
 144 */
 145
 146#define MPTSAS_CONFIG_PACK(number, type, version, fmt, ...)                  \
 147    mptsas_config_pack(data, "b*bbb" fmt, version, number, type,             \
 148                       ## __VA_ARGS__)
 149
 150static size_t mptsas_config_pack(uint8_t **data, const char *fmt, ...)
 151{
 152    va_list ap;
 153    size_t ret;
 154
 155    va_start(ap, fmt);
 156    ret = vpack(data, fmt, ap);
 157    va_end(ap);
 158
 159    if (data) {
 160        assert(ret / 4 < 256 && (ret % 4) == 0);
 161        stb_p(*data + 1, ret / 4);
 162    }
 163    return ret;
 164}
 165
 166#define MPTSAS_CONFIG_PACK_EXT(number, type, version, fmt, ...)              \
 167    mptsas_config_pack_ext(data, "b*bbb*wb*b" fmt, version, number,          \
 168                           MPI_CONFIG_PAGETYPE_EXTENDED, type, ## __VA_ARGS__)
 169
 170static size_t mptsas_config_pack_ext(uint8_t **data, const char *fmt, ...)
 171{
 172    va_list ap;
 173    size_t ret;
 174
 175    va_start(ap, fmt);
 176    ret = vpack(data, fmt, ap);
 177    va_end(ap);
 178
 179    if (data) {
 180        assert(ret < 65536 && (ret % 4) == 0);
 181        stw_le_p(*data + 4, ret / 4);
 182    }
 183    return ret;
 184}
 185
 186/* Manufacturing pages */
 187
 188static
 189size_t mptsas_config_manufacturing_0(MPTSASState *s, uint8_t **data, int address)
 190{
 191    return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
 192                              "s16s8s16s16s16",
 193                              "QEMU MPT Fusion",
 194                              "2.5",
 195                              "QEMU MPT Fusion",
 196                              "QEMU",
 197                              "0000111122223333");
 198}
 199
 200static
 201size_t mptsas_config_manufacturing_1(MPTSASState *s, uint8_t **data, int address)
 202{
 203    /* VPD - all zeros */
 204    return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
 205                              "*s256");
 206}
 207
 208static
 209size_t mptsas_config_manufacturing_2(MPTSASState *s, uint8_t **data, int address)
 210{
 211    PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s);
 212    return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
 213                              "wb*b*l",
 214                              pcic->device_id, pcic->revision);
 215}
 216
 217static
 218size_t mptsas_config_manufacturing_3(MPTSASState *s, uint8_t **data, int address)
 219{
 220    PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s);
 221    return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
 222                              "wb*b*l",
 223                              pcic->device_id, pcic->revision);
 224}
 225
 226static
 227size_t mptsas_config_manufacturing_4(MPTSASState *s, uint8_t **data, int address)
 228{
 229    /* All zeros */
 230    return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x05,
 231                              "*l*b*b*b*b*b*b*w*s56*l*l*l*l*l*l"
 232                              "*b*b*w*b*b*w*l*l");
 233}
 234
 235static
 236size_t mptsas_config_manufacturing_5(MPTSASState *s, uint8_t **data, int address)
 237{
 238    return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x02,
 239                              "q*b*b*w*l*l", s->sas_addr);
 240}
 241
 242static
 243size_t mptsas_config_manufacturing_6(MPTSASState *s, uint8_t **data, int address)
 244{
 245    return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
 246                              "*l");
 247}
 248
 249static
 250size_t mptsas_config_manufacturing_7(MPTSASState *s, uint8_t **data, int address)
 251{
 252    return MPTSAS_CONFIG_PACK(7, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
 253                              "*l*l*l*s16*b*b*w", MPTSAS_NUM_PORTS);
 254}
 255
 256static
 257size_t mptsas_config_manufacturing_8(MPTSASState *s, uint8_t **data, int address)
 258{
 259    return MPTSAS_CONFIG_PACK(8, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
 260                              "*l");
 261}
 262
 263static
 264size_t mptsas_config_manufacturing_9(MPTSASState *s, uint8_t **data, int address)
 265{
 266    return MPTSAS_CONFIG_PACK(9, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
 267                              "*l");
 268}
 269
 270static
 271size_t mptsas_config_manufacturing_10(MPTSASState *s, uint8_t **data, int address)
 272{
 273    return MPTSAS_CONFIG_PACK(10, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
 274                              "*l");
 275}
 276
 277/* I/O unit pages */
 278
 279static
 280size_t mptsas_config_io_unit_0(MPTSASState *s, uint8_t **data, int address)
 281{
 282    PCIDevice *pci = PCI_DEVICE(s);
 283    uint64_t unique_value = 0x53504D554D4551LL;  /* "QEMUMPTx" */
 284
 285    unique_value |= (uint64_t)pci->devfn << 56;
 286    return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00,
 287                              "q", unique_value);
 288}
 289
 290static
 291size_t mptsas_config_io_unit_1(MPTSASState *s, uint8_t **data, int address)
 292{
 293    return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, "l",
 294                              0x41 /* single function, RAID disabled */ );
 295}
 296
 297static
 298size_t mptsas_config_io_unit_2(MPTSASState *s, uint8_t **data, int address)
 299{
 300    PCIDevice *pci = PCI_DEVICE(s);
 301    uint8_t devfn = pci->devfn;
 302    return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02,
 303                              "llbbw*b*b*w*b*b*w*b*b*w*l",
 304                              0, 0x100, 0 /* pci bus? */, devfn, 0);
 305}
 306
 307static
 308size_t mptsas_config_io_unit_3(MPTSASState *s, uint8_t **data, int address)
 309{
 310    return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x01,
 311                              "*b*b*w*l");
 312}
 313
 314static
 315size_t mptsas_config_io_unit_4(MPTSASState *s, uint8_t **data, int address)
 316{
 317    return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, "*l*l*q");
 318}
 319
 320/* I/O controller pages */
 321
 322static
 323size_t mptsas_config_ioc_0(MPTSASState *s, uint8_t **data, int address)
 324{
 325    PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s);
 326
 327    return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IOC, 0x01,
 328                              "*l*lwwb*b*b*blww",
 329                              pcic->vendor_id, pcic->device_id, pcic->revision,
 330                              pcic->class_id, pcic->subsystem_vendor_id,
 331                              pcic->subsystem_id);
 332}
 333
 334static
 335size_t mptsas_config_ioc_1(MPTSASState *s, uint8_t **data, int address)
 336{
 337    return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IOC, 0x03,
 338                              "*l*l*b*b*b*b");
 339}
 340
 341static
 342size_t mptsas_config_ioc_2(MPTSASState *s, uint8_t **data, int address)
 343{
 344    return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IOC, 0x04,
 345                              "*l*b*b*b*b");
 346}
 347
 348static
 349size_t mptsas_config_ioc_3(MPTSASState *s, uint8_t **data, int address)
 350{
 351    return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IOC, 0x00,
 352                              "*b*b*w");
 353}
 354
 355static
 356size_t mptsas_config_ioc_4(MPTSASState *s, uint8_t **data, int address)
 357{
 358    return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IOC, 0x00,
 359                              "*b*b*w");
 360}
 361
 362static
 363size_t mptsas_config_ioc_5(MPTSASState *s, uint8_t **data, int address)
 364{
 365    return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_IOC, 0x00,
 366                              "*l*b*b*w");
 367}
 368
 369static
 370size_t mptsas_config_ioc_6(MPTSASState *s, uint8_t **data, int address)
 371{
 372    return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_IOC, 0x01,
 373                              "*l*b*b*b*b*b*b*b*b*b*b*w*l*l*l*l*b*b*w"
 374                              "*w*w*w*w*l*l*l");
 375}
 376
 377/* SAS I/O unit pages (extended) */
 378
 379#define MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE 16
 380
 381#define MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION 0x02
 382#define MPI_SAS_IOUNIT0_RATE_1_5                      0x08
 383#define MPI_SAS_IOUNIT0_RATE_3_0                      0x09
 384
 385#define MPI_SAS_DEVICE_INFO_NO_DEVICE                 0x00000000
 386#define MPI_SAS_DEVICE_INFO_END_DEVICE                0x00000001
 387#define MPI_SAS_DEVICE_INFO_SSP_TARGET                0x00000400
 388
 389#define MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS             0x00
 390
 391#define MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT          0x0001
 392#define MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED           0x0002
 393#define MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT      0x0004
 394
 395
 396
 397static SCSIDevice *mptsas_phy_get_device(MPTSASState *s, int i,
 398                                         int *phy_handle, int *dev_handle)
 399{
 400    SCSIDevice *d = scsi_device_find(&s->bus, 0, i, 0);
 401
 402    if (phy_handle) {
 403        *phy_handle = i + 1;
 404    }
 405    if (dev_handle) {
 406        *dev_handle = d ? i + 1 + MPTSAS_NUM_PORTS : 0;
 407    }
 408    return d;
 409}
 410
 411static
 412size_t mptsas_config_sas_io_unit_0(MPTSASState *s, uint8_t **data, int address)
 413{
 414    size_t size = MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x04,
 415                                         "*w*wb*b*w"
 416                                         repl(MPTSAS_NUM_PORTS, "*s16"),
 417                                         MPTSAS_NUM_PORTS);
 418
 419    if (data) {
 420        size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE;
 421        int i;
 422
 423        for (i = 0; i < MPTSAS_NUM_PORTS; i++) {
 424            int phy_handle, dev_handle;
 425            SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
 426
 427            fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE,
 428                 "bbbblwwl", i, 0, 0,
 429                 (dev
 430                  ? MPI_SAS_IOUNIT0_RATE_3_0
 431                  : MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION),
 432                 (dev
 433                  ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET
 434                  : MPI_SAS_DEVICE_INFO_NO_DEVICE),
 435                 dev_handle,
 436                 dev_handle,
 437                 0);
 438            ofs += MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE;
 439        }
 440        assert(ofs == size);
 441    }
 442    return size;
 443}
 444
 445#define MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE 12
 446
 447static
 448size_t mptsas_config_sas_io_unit_1(MPTSASState *s, uint8_t **data, int address)
 449{
 450    size_t size = MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x07,
 451                                         "*w*w*w*wb*b*b*b"
 452                                         repl(MPTSAS_NUM_PORTS, "*s12"),
 453                                         MPTSAS_NUM_PORTS);
 454
 455    if (data) {
 456        size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE;
 457        int i;
 458
 459        for (i = 0; i < MPTSAS_NUM_PORTS; i++) {
 460            SCSIDevice *dev = mptsas_phy_get_device(s, i, NULL, NULL);
 461            fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE,
 462                 "bbbblww", i, 0, 0,
 463                 (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5,
 464                 (dev
 465                  ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET
 466                  : MPI_SAS_DEVICE_INFO_NO_DEVICE),
 467                 0, 0);
 468            ofs += MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE;
 469        }
 470        assert(ofs == size);
 471    }
 472    return size;
 473}
 474
 475static
 476size_t mptsas_config_sas_io_unit_2(MPTSASState *s, uint8_t **data, int address)
 477{
 478    return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06,
 479                                  "*b*b*w*w*w*b*b*w");
 480}
 481
 482static
 483size_t mptsas_config_sas_io_unit_3(MPTSASState *s, uint8_t **data, int address)
 484{
 485    return MPTSAS_CONFIG_PACK_EXT(3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06,
 486                                  "*l*l*l*l*l*l*l*l*l");
 487}
 488
 489/* SAS PHY pages (extended) */
 490
 491static int mptsas_phy_addr_get(MPTSASState *s, int address)
 492{
 493    int i;
 494    if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 0) {
 495        i = address & 255;
 496    } else if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 1) {
 497        i = address & 65535;
 498    } else {
 499        return -EINVAL;
 500    }
 501
 502    if (i >= MPTSAS_NUM_PORTS) {
 503        return -EINVAL;
 504    }
 505
 506    return i;
 507}
 508
 509static
 510size_t mptsas_config_phy_0(MPTSASState *s, uint8_t **data, int address)
 511{
 512    int phy_handle = -1;
 513    int dev_handle = -1;
 514    int i = mptsas_phy_addr_get(s, address);
 515    SCSIDevice *dev;
 516
 517    if (i < 0) {
 518        trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0);
 519        return i;
 520    }
 521
 522    dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
 523    trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0);
 524
 525    return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01,
 526                                  "w*wqwb*blbb*b*b*l",
 527                                  dev_handle, s->sas_addr, dev_handle, i,
 528                                  (dev
 529                                   ? MPI_SAS_DEVICE_INFO_END_DEVICE /* | MPI_SAS_DEVICE_INFO_SSP_TARGET?? */
 530                                   : MPI_SAS_DEVICE_INFO_NO_DEVICE),
 531                                  (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5,
 532                                  (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5);
 533}
 534
 535static
 536size_t mptsas_config_phy_1(MPTSASState *s, uint8_t **data, int address)
 537{
 538    int phy_handle = -1;
 539    int dev_handle = -1;
 540    int i = mptsas_phy_addr_get(s, address);
 541
 542    if (i < 0) {
 543        trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1);
 544        return i;
 545    }
 546
 547    (void) mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
 548    trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1);
 549
 550    return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01,
 551                                  "*l*l*l*l*l");
 552}
 553
 554/* SAS device pages (extended) */
 555
 556static int mptsas_device_addr_get(MPTSASState *s, int address)
 557{
 558    uint32_t handle, i;
 559    uint32_t form = address >> MPI_SAS_PHY_PGAD_FORM_SHIFT;
 560    if (form == MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) {
 561        handle = address & MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK;
 562        do {
 563            if (handle == 65535) {
 564                handle = MPTSAS_NUM_PORTS + 1;
 565            } else {
 566                ++handle;
 567            }
 568            i = handle - 1 - MPTSAS_NUM_PORTS;
 569        } while (i < MPTSAS_NUM_PORTS && !scsi_device_find(&s->bus, 0, i, 0));
 570
 571    } else if (form == MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID) {
 572        if (address & MPI_SAS_DEVICE_PGAD_BT_BUS_MASK) {
 573            return -EINVAL;
 574        }
 575        i = address & MPI_SAS_DEVICE_PGAD_BT_TID_MASK;
 576
 577    } else if (form == MPI_SAS_DEVICE_PGAD_FORM_HANDLE) {
 578        handle = address & MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK;
 579        i = handle - 1 - MPTSAS_NUM_PORTS;
 580
 581    } else {
 582        return -EINVAL;
 583    }
 584
 585    if (i >= MPTSAS_NUM_PORTS) {
 586        return -EINVAL;
 587    }
 588
 589    return i;
 590}
 591
 592static
 593size_t mptsas_config_sas_device_0(MPTSASState *s, uint8_t **data, int address)
 594{
 595    int phy_handle = -1;
 596    int dev_handle = -1;
 597    int i = mptsas_device_addr_get(s, address);
 598    SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
 599
 600    trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 0);
 601    if (!dev) {
 602        return -ENOENT;
 603    }
 604
 605    return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x05,
 606                                  "*w*wqwbbwbblwb*b",
 607                                  dev->wwn, phy_handle, i,
 608                                  MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS,
 609                                  dev_handle, i, 0,
 610                                  MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET,
 611                                  (MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT |
 612                                   MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED |
 613                                   MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT), i);
 614}
 615
 616static
 617size_t mptsas_config_sas_device_1(MPTSASState *s, uint8_t **data, int address)
 618{
 619    int phy_handle = -1;
 620    int dev_handle = -1;
 621    int i = mptsas_device_addr_get(s, address);
 622    SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
 623
 624    trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 1);
 625    if (!dev) {
 626        return -ENOENT;
 627    }
 628
 629    return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x00,
 630                                  "*lq*lwbb*s20",
 631                                  dev->wwn, dev_handle, i, 0);
 632}
 633
 634static
 635size_t mptsas_config_sas_device_2(MPTSASState *s, uint8_t **data, int address)
 636{
 637    int phy_handle = -1;
 638    int dev_handle = -1;
 639    int i = mptsas_device_addr_get(s, address);
 640    SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
 641
 642    trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 2);
 643    if (!dev) {
 644        return -ENOENT;
 645    }
 646
 647    return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x01,
 648                                  "ql", dev->wwn, 0);
 649}
 650
 651typedef struct MPTSASConfigPage {
 652    uint8_t number;
 653    uint8_t type;
 654    size_t (*mpt_config_build)(MPTSASState *s, uint8_t **data, int address);
 655} MPTSASConfigPage;
 656
 657static const MPTSASConfigPage mptsas_config_pages[] = {
 658    {
 659        0, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 660        mptsas_config_manufacturing_0,
 661    }, {
 662        1, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 663        mptsas_config_manufacturing_1,
 664    }, {
 665        2, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 666        mptsas_config_manufacturing_2,
 667    }, {
 668        3, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 669        mptsas_config_manufacturing_3,
 670    }, {
 671        4, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 672        mptsas_config_manufacturing_4,
 673    }, {
 674        5, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 675        mptsas_config_manufacturing_5,
 676    }, {
 677        6, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 678        mptsas_config_manufacturing_6,
 679    }, {
 680        7, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 681        mptsas_config_manufacturing_7,
 682    }, {
 683        8, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 684        mptsas_config_manufacturing_8,
 685    }, {
 686        9, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 687        mptsas_config_manufacturing_9,
 688    }, {
 689        10, MPI_CONFIG_PAGETYPE_MANUFACTURING,
 690        mptsas_config_manufacturing_10,
 691    }, {
 692        0, MPI_CONFIG_PAGETYPE_IO_UNIT,
 693        mptsas_config_io_unit_0,
 694    }, {
 695        1, MPI_CONFIG_PAGETYPE_IO_UNIT,
 696        mptsas_config_io_unit_1,
 697    }, {
 698        2, MPI_CONFIG_PAGETYPE_IO_UNIT,
 699        mptsas_config_io_unit_2,
 700    }, {
 701        3, MPI_CONFIG_PAGETYPE_IO_UNIT,
 702        mptsas_config_io_unit_3,
 703    }, {
 704        4, MPI_CONFIG_PAGETYPE_IO_UNIT,
 705        mptsas_config_io_unit_4,
 706    }, {
 707        0, MPI_CONFIG_PAGETYPE_IOC,
 708        mptsas_config_ioc_0,
 709    }, {
 710        1, MPI_CONFIG_PAGETYPE_IOC,
 711        mptsas_config_ioc_1,
 712    }, {
 713        2, MPI_CONFIG_PAGETYPE_IOC,
 714        mptsas_config_ioc_2,
 715    }, {
 716        3, MPI_CONFIG_PAGETYPE_IOC,
 717        mptsas_config_ioc_3,
 718    }, {
 719        4, MPI_CONFIG_PAGETYPE_IOC,
 720        mptsas_config_ioc_4,
 721    }, {
 722        5, MPI_CONFIG_PAGETYPE_IOC,
 723        mptsas_config_ioc_5,
 724    }, {
 725        6, MPI_CONFIG_PAGETYPE_IOC,
 726        mptsas_config_ioc_6,
 727    }, {
 728        0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
 729        mptsas_config_sas_io_unit_0,
 730    }, {
 731        1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
 732        mptsas_config_sas_io_unit_1,
 733    }, {
 734        2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
 735        mptsas_config_sas_io_unit_2,
 736    }, {
 737        3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
 738        mptsas_config_sas_io_unit_3,
 739    }, {
 740        0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY,
 741        mptsas_config_phy_0,
 742    }, {
 743        1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY,
 744        mptsas_config_phy_1,
 745    }, {
 746        0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE,
 747        mptsas_config_sas_device_0,
 748    }, {
 749        1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE,
 750        mptsas_config_sas_device_1,
 751    }, {
 752       2,  MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE,
 753        mptsas_config_sas_device_2,
 754    }
 755};
 756
 757static const MPTSASConfigPage *mptsas_find_config_page(int type, int number)
 758{
 759    const MPTSASConfigPage *page;
 760    int i;
 761
 762    for (i = 0; i < ARRAY_SIZE(mptsas_config_pages); i++) {
 763        page = &mptsas_config_pages[i];
 764        if (page->type == type && page->number == number) {
 765            return page;
 766        }
 767    }
 768
 769    return NULL;
 770}
 771
 772void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req)
 773{
 774    PCIDevice *pci = PCI_DEVICE(s);
 775
 776    MPIMsgConfigReply reply;
 777    const MPTSASConfigPage *page;
 778    size_t length;
 779    uint8_t type;
 780    uint8_t *data = NULL;
 781    uint32_t flags_and_length;
 782    uint32_t dmalen;
 783    uint64_t pa;
 784
 785    mptsas_fix_config_endianness(req);
 786
 787    QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
 788    QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
 789
 790    /* Copy common bits from the request into the reply. */
 791    memset(&reply, 0, sizeof(reply));
 792    reply.Action      = req->Action;
 793    reply.Function    = req->Function;
 794    reply.MsgContext  = req->MsgContext;
 795    reply.MsgLength   = sizeof(reply) / 4;
 796    reply.PageType    = req->PageType;
 797    reply.PageNumber  = req->PageNumber;
 798    reply.PageLength  = req->PageLength;
 799    reply.PageVersion = req->PageVersion;
 800
 801    type = req->PageType & MPI_CONFIG_PAGETYPE_MASK;
 802    if (type == MPI_CONFIG_PAGETYPE_EXTENDED) {
 803        type = req->ExtPageType;
 804        if (type <= MPI_CONFIG_PAGETYPE_MASK) {
 805            reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE;
 806            goto out;
 807        }
 808
 809        reply.ExtPageType = req->ExtPageType;
 810    }
 811
 812    page = mptsas_find_config_page(type, req->PageNumber);
 813
 814    switch(req->Action) {
 815    case MPI_CONFIG_ACTION_PAGE_DEFAULT:
 816    case MPI_CONFIG_ACTION_PAGE_HEADER:
 817    case MPI_CONFIG_ACTION_PAGE_READ_NVRAM:
 818    case MPI_CONFIG_ACTION_PAGE_READ_CURRENT:
 819    case MPI_CONFIG_ACTION_PAGE_READ_DEFAULT:
 820    case MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT:
 821    case MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM:
 822        break;
 823
 824    default:
 825        reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_ACTION;
 826        goto out;
 827    }
 828
 829    if (!page) {
 830        page = mptsas_find_config_page(type, 1);
 831        if (page) {
 832            reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
 833        } else {
 834            reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE;
 835        }
 836        goto out;
 837    }
 838
 839    if (req->Action == MPI_CONFIG_ACTION_PAGE_DEFAULT ||
 840        req->Action == MPI_CONFIG_ACTION_PAGE_HEADER) {
 841        length = page->mpt_config_build(s, NULL, req->PageAddress);
 842        if ((ssize_t)length < 0) {
 843            reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
 844            goto out;
 845        } else {
 846            goto done;
 847        }
 848    }
 849
 850    if (req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
 851        req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
 852        length = page->mpt_config_build(s, NULL, req->PageAddress);
 853        if ((ssize_t)length < 0) {
 854            reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
 855        } else {
 856            reply.IOCStatus = MPI_IOCSTATUS_CONFIG_CANT_COMMIT;
 857        }
 858        goto out;
 859    }
 860
 861    flags_and_length = req->PageBufferSGE.FlagsLength;
 862    dmalen = flags_and_length & MPI_SGE_LENGTH_MASK;
 863    if (dmalen == 0) {
 864        length = page->mpt_config_build(s, NULL, req->PageAddress);
 865        if ((ssize_t)length < 0) {
 866            reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
 867            goto out;
 868        } else {
 869            goto done;
 870        }
 871    }
 872
 873    if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
 874        pa = req->PageBufferSGE.u.Address64;
 875    } else {
 876        pa = req->PageBufferSGE.u.Address32;
 877    }
 878
 879    /* Only read actions left.  */
 880    length = page->mpt_config_build(s, &data, req->PageAddress);
 881    if ((ssize_t)length < 0) {
 882        reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
 883        goto out;
 884    } else {
 885        assert(data[2] == page->number);
 886        pci_dma_write(pci, pa, data, MIN(length, dmalen));
 887        goto done;
 888    }
 889
 890    abort();
 891
 892done:
 893    if (type > MPI_CONFIG_PAGETYPE_MASK) {
 894        reply.ExtPageLength = length / 4;
 895        reply.ExtPageType   = req->ExtPageType;
 896    } else {
 897        reply.PageLength    = length / 4;
 898    }
 899
 900out:
 901    mptsas_fix_config_reply_endianness(&reply);
 902    mptsas_reply(s, (MPIDefaultReply *)&reply);
 903    g_free(data);
 904}
 905