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 <[email protected]> > +// > +// based on virtio-scsi.c which is written by: > +// Paolo Bonzini <[email protected]> > +// > +// 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. > _______________________________________________ SeaBIOS mailing list [email protected] http://www.seabios.org/mailman/listinfo/seabios
