[SeaBIOS] [PATCH] Support for booting from LSI Logic LSI53C1030, SAS1068, SAS1068e
Kevin O'Connor
kevin at koconnor.net
Wed Jan 27 16:57:23 CET 2016
On Wed, Jan 27, 2016 at 03:51:02PM +0100, Paolo Bonzini wrote:
> From: Don Slutz <Don.Slutz at Gmail.com>
>
> Also known as Fusion MPT disk; this controller model is supported
> by VirtualBox and VMware, and QEMU support patches have been
> posted.
>
> Signed-off-by: Don Slutz <Don.Slutz at Gmail.com>
> Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Thanks. Is the upstream support for this in QEMU now?
See my comments below.
> ---
> Makefile | 2 +-
> src/Kconfig | 6 ++
> src/block.c | 3 +
> src/block.h | 1 +
> src/hw/mpt-scsi.c | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> src/hw/mpt-scsi.h | 8 ++
> src/post.c | 2 +
> 7 files changed, 328 insertions(+), 1 deletion(-)
> create mode 100644 src/hw/mpt-scsi.c
> create mode 100644 src/hw/mpt-scsi.h
>
> diff --git a/Makefile b/Makefile
> index 3a0d2e8..fcbd88a 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -34,7 +34,7 @@ SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \
> hw/usb.c hw/usb-uhci.c hw/usb-ohci.c hw/usb-ehci.c \
> hw/usb-hid.c hw/usb-msc.c hw/usb-uas.c \
> hw/blockcmd.c hw/floppy.c hw/ata.c hw/ramdisk.c \
> - hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c
> + hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c hw/mpt-scsi.c
> SRC16=$(SRCBOTH)
> SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c romfile.c x86.c optionroms.c \
> pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c tcgbios.c sha1.c \
> diff --git a/src/Kconfig b/src/Kconfig
> index b873cd3..8250702 100644
> --- a/src/Kconfig
> +++ b/src/Kconfig
> @@ -208,6 +208,12 @@ menu "Hardware support"
> default y
> help
> Support boot from LSI MegaRAID SAS scsi storage.
> + config MPT_SCSI
> + depends on DRIVES
> + bool "LSI MPT Fusion controllers"
> + default y
> + help
> + Support boot from LSI MPT Fusion scsi storage.
Unless there is good reason to believe this will work on real
hardware, I think this should depend on QEMU_HARDWARE and the
mpt_scsi_setup() function should check runningOnQEMU().
> config FLOPPY
> depends on DRIVES && HARDWARE_IRQ
> bool "Floppy controller"
> diff --git a/src/block.c b/src/block.c
> index 97e05fa..ef1cd9f 100644
> --- a/src/block.c
> +++ b/src/block.c
> @@ -13,6 +13,7 @@
> #include "hw/esp-scsi.h" // esp_scsi_process_op
> #include "hw/lsi-scsi.h" // lsi_scsi_process_op
> #include "hw/megasas.h" // megasas_process_op
> +#include "hw/mpt-scsi.h" // mpt_scsi_process_op
> #include "hw/pci.h" // pci_bdf_to_bus
> #include "hw/pvscsi.h" // pvscsi_process_op
> #include "hw/rtc.h" // rtc_read
> @@ -521,6 +522,8 @@ process_op_both(struct disk_op_s *op)
> return esp_scsi_process_op(op);
> case DTYPE_MEGASAS:
> return megasas_process_op(op);
> + case DTYPE_MPT_SCSI:
> + return mpt_scsi_process_op(op);
> default:
> if (!MODESEGMENT)
> return DISK_RET_EPARAM;
> diff --git a/src/block.h b/src/block.h
> index 2ff359f..7de6f54 100644
> --- a/src/block.h
> +++ b/src/block.h
> @@ -80,6 +80,7 @@ struct drive_s {
> #define DTYPE_ESP_SCSI 0x81
> #define DTYPE_MEGASAS 0x82
> #define DTYPE_PVSCSI 0x83
> +#define DTYPE_MPT_SCSI 0x84
> #define DTYPE_SDCARD 0x90
>
> #define MAXDESCSIZE 80
> diff --git a/src/hw/mpt-scsi.c b/src/hw/mpt-scsi.c
> new file mode 100644
> index 0000000..c3b227b
> --- /dev/null
> +++ b/src/hw/mpt-scsi.c
> @@ -0,0 +1,307 @@
> +// MPT Fusion boot support.
> +//
> +// This file may be distributed under the terms of the GNU LGPLv3 license.
I think there should be an explicit copyright statement - for example:
// Copyright (C) 2016 Some One <someone at example.org>
> +#include "biosvar.h" // GET_GLOBALFLAT
> +#include "block.h" // struct drive_s
> +#include "blockcmd.h" // scsi_drive_setup
> +#include "config.h" // CONFIG_*
> +#include "malloc.h" // free
> +#include "output.h" // dprintf
> +#include "pci.h" // foreachpci
> +#include "pci_ids.h" // PCI_DEVICE_ID
> +#include "pci_regs.h" // PCI_VENDOR_ID
> +#include "std/disk.h" // DISK_RET_SUCCESS
> +#include "string.h" // memset
> +#include "util.h" // usleep
> +
> +#define MPT_REG_DOORBELL 0x00
> +#define MPT_REG_WRITE_SEQ 0x04
> +#define MPT_REG_HOST_DIAG 0x08
> +#define MPT_REG_TEST 0x0c
> +#define MPT_REG_DIAG_DATA 0x10
> +#define MPT_REG_DIAG_ADDR 0x14
> +#define MPT_REG_ISTATUS 0x30
> +#define MPT_REG_IMASK 0x34
> +#define MPT_REG_REQ_Q 0x40
> +#define MPT_REG_REP_Q 0x44
> +
> +#define MPT_DOORBELL_MSG_RESET 0x40
> +#define MPT_DOORBELL_HANDSHAKE 0x42
> +
> +#define MPT_IMASK_DOORBELL 0x01
> +#define MPT_IMASK_REPLY 0x08
> +
> +struct mpt_lun_s {
> + struct drive_s drive;
> + struct pci_device *pci;
> + u32 iobase;
> + u8 target;
> + u8 lun;
> +};
> +
> +u8 reply_msg[4] __attribute((aligned(4))) VARFSEG;
If I'm reading this correctly, this variable is a target of DMA. I
don't think it is a good idea to attempt DMA to the f-segment (eg, the
f-segment is read-only at runtime). If this needs to be a global
variable (instead of stack allocated) then VARLOW should work.
> +#define MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST (0x00)
> +#define MPT_MESSAGE_HDR_FUNCTION_IOC_INIT (0x02)
> +
> +static struct MptIOCInitRequest
> +{
> + u8 WhoInit; /* Which system sent this init request. */
> + u8 Reserved1; /* Reserved */
> + u8 ChainOffset; /* Chain offset in the SG list. */
> + u8 Function; /* Function to execute. */
> + u8 Flags; /* Flags */
> + u8 MaxDevices; /* Max devices the driver can handle. */
> + u8 MaxBuses; /* Max buses the driver can handle. */
> + u8 MessageFlags; /* Message flags. */
> + u32 MessageContext; /* Message context ID. */
> + u16 ReplyFrameSize; /* Reply frame size. */
> + u16 Reserved2; /* Reserved */
> + u32 HostMfaHighAddr; /* Upper 32bit of the message frames. */
> + u32 SenseBufferHighAddr; /* Upper 32bit of the sense buffer. */
> +} MptIOCInitRequest = {
> + .WhoInit = 2,
> + .Function = MPT_MESSAGE_HDR_FUNCTION_IOC_INIT,
> + .MaxDevices = 8,
> + .MaxBuses = 1,
> + .ReplyFrameSize = sizeof(reply_msg),
> + .HostMfaHighAddr = 0,
> + .SenseBufferHighAddr = 0
> +};
> +
> +struct MptIOCInitReply {
> + u8 WhoInit; /* Which subsystem sent this init request. */
> + u8 Reserved1; /* Reserved */
> + u8 MessageLength; /* Message length */
> + u8 Function; /* Function. */
> + u8 Flags; /* Flags */
> + u8 MaxDevices; /* Maximum number of devices the driver can handle. */
> + u8 MaxBuses; /* Maximum number of busses the driver can handle. */
> + u8 MessageFlags; /* Message flags. */
> + u32 MessageContext; /* Message context ID */
> + u16 Reserved2; /* Reserved */
> + u16 IOCStatus; /* IO controller status. */
> + u32 IOCLogInfo; /* IO controller log information. */
> +};
> +
> +typedef struct MptSCSIIORequest {
> + u8 TargetID; /* Target ID */
> + u8 Bus; /* Bus number */
> + u8 ChainOffset; /* Chain offset */
> + u8 Function; /* Function number. */
> + u8 CDBLength; /* CDB length. */
> + u8 SenseBufferLength; /* Sense buffer length. */
> + u8 Reserved; /* Reserved */
> + u8 MessageFlags; /* Message flags. */
> + u32 MessageContext; /* Message context ID. */
> + u8 LUN[8]; /* LUN */
> + u32 Control; /* Control values. */
> + u8 CDB[16]; /* The CDB. */
> + u32 DataLength; /* Data length. */
> + u32 SenseBufferLowAddr; /* Sense buffer low 32bit address. */
> +} __attribute__((packed)) MptSCSIIORequest_t;
> +
> +#define MPT_CONTEXT_MAGIC 0x5555aaaa
> +
> +typedef struct MptSGEntrySimple32 {
> + u32 FlagsLength;
> + u32 DataBufferAddressLow;
> +} __attribute__((packed)) MptSGEntrySimple32_t;
> +
> +static int
> +mpt_scsi_cmd(u32 iobase, struct disk_op_s *op,
> + u8 *cdb, u16 target, u16 lun, u16 blocksize)
> +{
> + if (lun != 0)
> + return DISK_RET_ENOTREADY;
> +
> + u8 sense_buf[18];
> + struct scsi_req {
> + MptSCSIIORequest_t scsi_io;
> + MptSGEntrySimple32_t sge;
> + } __attribute__((packed, aligned(4))) req = {
> + .scsi_io = {
> + .TargetID = target,
> + .Bus = 0,
> + .Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST,
> + .CDBLength = 16,
> + .SenseBufferLength = 18,
> + .MessageContext = MPT_CONTEXT_MAGIC,
> + .DataLength = op->count * blocksize,
> + .SenseBufferLowAddr = (u32)MAKE_FLATPTR(GET_SEG(SS), &sense_buf[0]),
> + },
> + .sge = {
> + /* end of list, simple entry, end of buffer, last element */
> + .FlagsLength = (op->count * blocksize) | 0xD1000000,
> + .DataBufferAddressLow = (u32)op->buf_fl,
> + }
> + };
> +
> + req.scsi_io.LUN[1] = lun;
> + memcpy(req.scsi_io.CDB, cdb, 16);
> + if (blocksize) {
> + if (scsi_is_read(op)) {
> + req.scsi_io.Control = 2 << 24;
> + } else {
> + req.scsi_io.Control = 1 << 24;
> + req.sge.FlagsLength |= 0x04000000;
> + }
> + }
> +
> + outl((u32)MAKE_FLATPTR(GET_SEG(SS), &req), iobase + MPT_REG_REQ_Q);
> +
> + for (;;) {
> + u32 istatus = inl(iobase + MPT_REG_ISTATUS);
> + if (istatus & MPT_IMASK_REPLY) {
> + u32 resp = inl(iobase + MPT_REG_REP_Q);
> + /* another read to turn interrupt off */
> + inl(iobase + MPT_REG_REP_Q);
> + if (resp == MPT_CONTEXT_MAGIC) {
> + return DISK_RET_SUCCESS;
> + } else if (resp & 0x80000000) {
> + outl((u32)&reply_msg[0], iobase + MPT_REG_REP_Q);
> + return DISK_RET_EBADTRACK;
> + }
> + }
> + usleep(50);
> + }
This could loop forever on a hardware error. I know other SeaBIOS
drivers do this as well, but I'd prefer if new code would have some
eventual timeout (eg, 30 seconds) and fail the request.
-Kevin
More information about the SeaBIOS
mailing list