Il 12/11/2012 15:45, Hannes Reinecke ha scritto:
This patch adds support for LSI MegaRAID SAS controllers. Currently only 8708EM2 is supported.
Did you test it on real hardware? For example via PCI passthrough.
diff --git a/src/megasas.c b/src/megasas.c new file mode 100644 index 0000000..4b81334 --- /dev/null +++ b/src/megasas.c @@ -0,0 +1,311 @@ +// (qemu-emulated) megaraid_sas boot support.
If also "real" MegaSAS is supported, please fix the comment. Otherwise make it depend on QEMU too.
I know nil about the adapter, so I have not really reviewed the rest of the code. Seems sane though, thanks for this work.
Paolo
+// +// Copyright (C) 2012 Hannes Reinecke, SUSE Linux Products GmbH +// +// Authors: +// Hannes Reinecke hare@suse.de +// +// based on virtio-scsi.c which is written by: +// Paolo Bonzini pbonzini@redhat.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license.
+#include "util.h" // dprintf +#include "pci.h" // foreachpci +#include "config.h" // CONFIG_* +#include "biosvar.h" // GET_GLOBAL +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "boot.h" // bootprio_find_scsi_device +#include "blockcmd.h" // scsi_init_drive +#include "disk.h"
+#define MFI_OMSG0 0x18 // Outbound message 0 +#define MFI_ODB 0x2c // Outbound doorbell +#define MFI_IQP 0x40 // Inbound queue port
+#define MFI_STATE_MASK 0xf0000000 +#define MFI_STATE_READY 0xb0000000 +#define MFI_STATE_OPERATIONAL 0xc0000000
+/* MFI Commands */ +typedef enum {
- MFI_CMD_INIT = 0x00,
- MFI_CMD_LD_READ,
- MFI_CMD_LD_WRITE,
- MFI_CMD_LD_SCSI_IO,
- MFI_CMD_PD_SCSI_IO,
- MFI_CMD_DCMD,
- MFI_CMD_ABORT,
- MFI_CMD_SMP,
- MFI_CMD_STP
+} mfi_cmd_t;
+struct megasas_cmd_frame {
- u8 cmd; /*00h */
- u8 sense_len; /*01h */
- u8 cmd_status; /*02h */
- u8 scsi_status; /*03h */
- u8 target_id; /*04h */
- u8 lun; /*05h */
- u8 cdb_len; /*06h */
- u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 context_64; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
- u32 data_xfer_len; /*14h */
- union {
struct {
u32 opcode; /*18h */
u8 mbox[12]; /*1Ch */
u32 sgl_addr; /*28h */
u32 sgl_len; /*32h */
u32 pad; /*34h */
} dcmd;
struct {
u32 sense_buf_lo; /*18h */
u32 sense_buf_hi; /*1Ch */
u8 cdb[16]; /*20h */
u32 sgl_addr; /*30h */
u32 sgl_len; /*34h */
} pthru;
struct {
u8 pad[22]; /*18h */
} gen;
- };
+} __attribute__ ((packed));
+struct mfi_ld_list_s {
- u32 count;
- u32 reserved_0;
- struct {
u8 target;
u8 lun;
u16 seq;
u8 state;
u8 reserved_1[3];
u64 size;
- } lds[64];
+} __attribute__ ((packed));
+#define MEGASAS_POLL_TIMEOUT 60000 // 60 seconds polling timeout
+struct megasas_lun_s {
- struct drive_s drive;
- struct pci_device *pci;
- struct megasas_cmd_frame *frame;
- u32 iobase;
- u8 target;
- u8 lun;
+};
+static int megasas_fire_cmd(u32 ioaddr, struct megasas_cmd_frame *frame) +{
- u32 frame_addr = (u32)frame;
- int frame_count = 1;
- u8 cmd_state;
- u64 end;
- dprintf(1, "Frame 0x%x\n", frame_addr);
- outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQP);
- end = calc_future_tsc(MEGASAS_POLL_TIMEOUT);
- do {
for (;;) {
cmd_state = GET_LOWFLAT(frame->cmd_status);
if (cmd_state != 0xff)
break;
if (check_tsc(end)) {
warn_timeout();
return -1;
}
yield();
}
- } while (cmd_state == 0xff);
- if (cmd_state == 0 || cmd_state == 0x2d)
return 0;
- dprintf(1, "Frame 0x%x, status 0x%x\n", frame_addr, cmd_state);
- return -1;
+}
+int +megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{
- struct megasas_lun_s *mlun =
container_of(op->drive_g, struct megasas_lun_s, drive);
- u8 *cdb = cdbcmd;
- struct megasas_cmd_frame *frame = GET_GLOBAL(mlun->frame);
- int i;
- if (!CONFIG_MEGASAS)
return DISK_RET_EBADTRACK;
- memset_fl(frame, 0, sizeof(*frame));
- SET_LOWFLAT(frame->cmd, MFI_CMD_LD_SCSI_IO);
- SET_LOWFLAT(frame->cmd_status, 0xFF);
- SET_LOWFLAT(frame->target_id, GET_GLOBAL(mlun->target));
- SET_LOWFLAT(frame->lun, GET_GLOBAL(mlun->lun));
- SET_LOWFLAT(frame->flags, 0x0001);
- SET_LOWFLAT(frame->data_xfer_len, op->count * blocksize);
- SET_LOWFLAT(frame->cdb_len, 16);
- for (i = 0; i < 16; i++) {
SET_LOWFLAT(frame->pthru.cdb[i], cdb[i]);
- }
- dprintf(1, "pthru cmd 0x%x count %d bs %d\n",
cdb[0], op->count, blocksize);
- if (op->count) {
SET_LOWFLAT(frame->pthru.sgl_addr, (u32)op->buf_fl);
SET_LOWFLAT(frame->pthru.sgl_len, op->count * blocksize);
SET_LOWFLAT(frame->sge_count, 1);
- }
- SET_LOWFLAT(frame->context, (u32)frame);
- if (megasas_fire_cmd(GET_GLOBAL(mlun->iobase), frame) == 0)
return DISK_RET_SUCCESS;
- dprintf(1, "pthru cmd 0x%x failed\n", cdb[0]);
- return DISK_RET_EBADTRACK;
+}
+static int +megasas_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{
- struct megasas_lun_s *mlun = malloc_fseg(sizeof(*mlun));
- char *name;
- int prio, ret = 0;
- if (!mlun) {
warn_noalloc();
return -1;
- }
- memset(mlun, 0, sizeof(*mlun));
- mlun->drive.type = DTYPE_MEGASAS;
- mlun->drive.cntl_id = pci->bdf;
- mlun->pci = pci;
- mlun->target = target;
- mlun->lun = lun;
- mlun->iobase = iobase;
- mlun->frame = memalign_high(256, sizeof(struct megasas_cmd_frame));
- if (!mlun->frame) {
warn_noalloc();
free(mlun);
return -1;
- }
- name = znprintf(36, "MegaRAID SAS (PCI %02x:%02x.%x) LD %d:%d",
pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
pci_bdf_to_fn(pci->bdf), target, lun);
- prio = bootprio_find_scsi_device(pci, target, lun);
- ret = scsi_init_drive(&mlun->drive, name, prio);
- free(name);
- if (ret) {
free(mlun->frame);
free(mlun);
ret = -1;
- }
- return ret;
+}
+static void megasas_scan_target(struct pci_device *pci, u32 iobase) +{
- struct mfi_ld_list_s ld_list;
- struct megasas_cmd_frame *frame = memalign_high(256, sizeof(*frame));
- int i;
- memset(&ld_list, 0, sizeof(ld_list));
- memset(frame, 0, sizeof(*frame));
- frame->cmd = MFI_CMD_DCMD;
- frame->cmd_status = 0xFF;
- frame->sge_count = 1;
- frame->flags = 0x0011;
- frame->data_xfer_len = sizeof(ld_list);
- frame->dcmd.opcode = 0x03010000;
- frame->dcmd.sgl_addr = (u32)MAKE_FLATPTR(GET_SEG(SS), &ld_list);
- frame->dcmd.sgl_len = sizeof(ld_list);
- frame->context = (u32)frame;
- if (megasas_fire_cmd(iobase, frame) != 0)
return;
- dprintf(1, "%d LD found\n", ld_list.count);
- for (i = 0; i < ld_list.count; i++) {
dprintf(1, "LD %d:%d state 0x%x\n",
ld_list.lds[i].target, ld_list.lds[i].lun,
ld_list.lds[i].state);
if (ld_list.lds[i].state != 0) {
megasas_add_lun(pci, iobase,
ld_list.lds[i].target, ld_list.lds[i].lun);
}
- }
- free(frame);
+}
+static int megasas_transition_to_ready(u32 ioaddr) +{
- u32 fw_state, i = 0;
- fw_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK;
- while (i < MEGASAS_POLL_TIMEOUT) {
if (fw_state == MFI_STATE_OPERATIONAL)
outw(ioaddr + MFI_ODB, 0x07);
if (fw_state == MFI_STATE_READY)
break;
msleep(1);
fw_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK;
i++;
- }
- if (fw_state == MFI_STATE_READY) {
dprintf(1, "MegaRAID SAS fw ready\n");
return 0;
- }
- dprintf(1, "ERROR: fw in state %x\n", fw_state & MFI_STATE_MASK);
- return -1;
+}
+static void +init_megasas(struct pci_device *pci) +{
- u16 bdf = pci->bdf;
- u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_2)
& PCI_BASE_ADDRESS_IO_MASK;
- dprintf(1, "found MegaRAID SAS at %02x:%02x.%x, io @ %x\n",
pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
pci_bdf_to_fn(bdf), iobase);
- pci_config_maskw(pci->bdf, PCI_COMMAND, 0,
PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
- // reset
- if (megasas_transition_to_ready(iobase) == 0)
megasas_scan_target(pci, iobase);
- return;
+}
+void +megasas_setup(void) +{
- ASSERT32FLAT();
- if (!CONFIG_MEGASAS)
return;
- dprintf(3, "init megasas\n");
- struct pci_device *pci;
- foreachpci(pci) {
if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC
|| pci->device !=PCI_DEVICE_ID_LSI_SAS1078)
continue;
init_megasas(pci);
- }
+} diff --git a/src/megasas.h b/src/megasas.h new file mode 100644 index 0000000..124042e --- /dev/null +++ b/src/megasas.h @@ -0,0 +1,8 @@ +#ifndef __MEGASAS_H +#define __MEGASAS_H
+struct disk_op_s; +int megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void megasas_setup(void);
+#endif /* __MEGASAS_H */ diff --git a/src/post.c b/src/post.c index 924b311..0133f75 100644 --- a/src/post.c +++ b/src/post.c @@ -29,7 +29,7 @@ #include "virtio-scsi.h" // virtio_scsi_setup #include "lsi-scsi.h" // lsi_scsi_setup #include "esp-scsi.h" // esp_scsi_setup
+#include "megasas.h" // megasas_setup
/****************************************************************
- BIOS init
@@ -198,6 +198,7 @@ init_hw(void) virtio_scsi_setup(); lsi_scsi_setup(); esp_scsi_setup();
- megasas_setup();
}
// Begin the boot process by invoking an int0x19 in 16bit mode.