[SeaBIOS] [PATCH] megasas: Add boot support for LSI MegaRAID SAS

Paolo Bonzini pbonzini at redhat.com
Mon Nov 12 18:28:17 CET 2012


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 at suse.de>
> +//
> +// based on virtio-scsi.c which is written by:
> +//  Paolo Bonzini <pbonzini at 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.
> 




More information about the SeaBIOS mailing list