This is a driver for an AMD PCscsi (am53c974) SCSI card. It can be used together with DOS or old operating systems such as Windows NT 3.1, Windows 3.1 or Windows 98.
Cc: Hervé Poussineau hpoussin@reactos.org Signed-off-by: Paolo Bonzini pbonzini@redhat.com --- Makefile | 2 +- src/Kconfig | 6 ++ src/block.c | 1 + src/blockcmd.c | 3 + src/disk.h | 1 + src/esp-scsi.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/post.c | 2 + 7 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 src/esp-scsi.c
diff --git a/Makefile b/Makefile index 1167799..4dce77b 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SRCBOTH=misc.c stacks.c pmm.c output.c util.c block.c floppy.c ata.c mouse.c \ pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \ usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \ virtio-ring.c virtio-pci.c virtio-blk.c virtio-scsi.c apm.c ahci.c \ - usb-uas.c lsi-scsi.c + usb-uas.c lsi-scsi.c esp-scsi.c SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ diff --git a/src/Kconfig b/src/Kconfig index bc343ee..cee3bc5 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -119,6 +119,12 @@ menu "Hardware support" default y help Support boot from virtio-scsi storage. + config ESP_SCSI + depends on DRIVES + bool "AMD PCscsi controllers" + default y + help + Support boot from AMD PCscsi storage. config LSI_SCSI depends on DRIVES && !COREBOOT bool "lsi53c895a scsi controllers" diff --git a/src/block.c b/src/block.c index 1c200cc..243428e 100644 --- a/src/block.c +++ b/src/block.c @@ -335,6 +335,7 @@ process_op(struct disk_op_s *op) case DTYPE_UAS: case DTYPE_VIRTIO_SCSI: case DTYPE_LSI_SCSI: + case DTYPE_ESP_SCSI: return process_scsi_op(op); default: op->count = 0; diff --git a/src/blockcmd.c b/src/blockcmd.c index 4365650..97c72a6 100644 --- a/src/blockcmd.c +++ b/src/blockcmd.c @@ -15,6 +15,7 @@ #include "usb-uas.h" // usb_cmd_data #include "virtio-scsi.h" // virtio_scsi_cmd_data #include "lsi-scsi.h" // lsi_scsi_cmd_data +#include "esp-scsi.h" // esp_scsi_cmd_data #include "boot.h" // boot_add_hd
// Route command to low-level handler. @@ -35,6 +36,8 @@ cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) return virtio_scsi_cmd_data(op, cdbcmd, blocksize); case DTYPE_LSI_SCSI: return lsi_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_ESP_SCSI: + return esp_scsi_cmd_data(op, cdbcmd, blocksize); default: op->count = 0; return DISK_RET_EPARAM; diff --git a/src/disk.h b/src/disk.h index 2b2511f..3d07372 100644 --- a/src/disk.h +++ b/src/disk.h @@ -234,6 +234,7 @@ struct drive_s { #define DTYPE_USB 0x0a #define DTYPE_UAS 0x0b #define DTYPE_LSI_SCSI 0x0c +#define DTYPE_ESP_SCSI 0x0d
#define MAXDESCSIZE 80
diff --git a/src/esp-scsi.c b/src/esp-scsi.c new file mode 100644 index 0000000..daafa1e --- /dev/null +++ b/src/esp-scsi.c @@ -0,0 +1,231 @@ +// AMD PCscsi boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Paolo Bonzini pbonzini@redhat.com +// +// based on lsi-scsi.c which is written by: +// Gerd Hoffman kraxel@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 +#include "pci_regs.h" // PCI_VENDOR_ID +#include "boot.h" // bootprio_find_scsi_device +#include "blockcmd.h" // scsi_init_drive +#include "disk.h" + +#define ESP_TCLO 0x00 +#define ESP_TCMID 0x04 +#define ESP_FIFO 0x08 +#define ESP_CMD 0x0c +#define ESP_WBUSID 0x10 +#define ESP_TCHI 0x38 + +#define ESP_RSTAT 0x10 +#define ESP_RINTR 0x14 +#define ESP_RFLAGS 0x1c + +#define ESP_DMA_CMD 0x40 +#define ESP_DMA_STC 0x44 +#define ESP_DMA_SPA 0x48 +#define ESP_DMA_WBC 0x4c +#define ESP_DMA_WAC 0x50 +#define ESP_DMA_STAT 0x54 +#define ESP_DMA_SMDLA 0x58 +#define ESP_DMA_WMAC 0x58c + +#define ESP_CMD_DMA 0x80 +#define ESP_CMD_RESET 0x02 +#define ESP_CMD_TI 0x10 +#define ESP_CMD_ICCS 0x11 +#define ESP_CMD_SELATN 0x42 + +#define ESP_STAT_DI 0x01 +#define ESP_STAT_CD 0x02 +#define ESP_STAT_MSG 0x04 +#define ESP_STAT_TC 0x10 + +#define ESP_INTR_DC 0x20 + +struct esp_lun_s { + struct drive_s drive; + struct pci_device *pci; + u32 iobase; + u8 target; + u8 lun; +}; + +static void +esp_scsi_dma(u32 iobase, u32 buf, u32 len, int read) +{ + outb(len & 0xff, iobase + ESP_TCLO); + outb((len >> 8) & 0xff, iobase + ESP_TCMID); + outb((len >> 16) & 0xff, iobase + ESP_TCHI); + outl(buf, iobase + ESP_DMA_SPA); + outl(len, iobase + ESP_DMA_STC); + outb(read ? 0x83 : 0x03, iobase + ESP_DMA_CMD); +} + +static int +esp_scsi_cmd(struct esp_lun_s *llun, struct disk_op_s *op, + u8 *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + u32 iobase = GET_GLOBAL(llun->iobase); + int i, state; + u8 status; + + outb(target, iobase + ESP_WBUSID); + + /* + * We need to pass the LUN at the beginning of the command, and the FIFO + * is only 16 bytes, so we cannot support 16-byte CDBs. The alternative + * would be to use DMA for the 17-byte command too, which is quite + * overkill. + */ + outb(lun, iobase + ESP_FIFO); + cdbcmd[1] &= 0x1f; + cdbcmd[1] |= lun << 5; + for (i = 0; i < 12; i++) + outb(cdbcmd[i], iobase + ESP_FIFO); + outb(ESP_CMD_SELATN, iobase + ESP_CMD); + + for (state = 0;;) { + u8 stat = inb(iobase + ESP_RSTAT); + + /* Detect disconnected device. */ + if (state == 0 && (inb(iobase + ESP_RINTR) & ESP_INTR_DC)) { + return DISK_RET_ENOTREADY; + } + + /* HBA reads command, clears CD, sets TC -> do DMA if needed. */ + if (state == 0 && (stat & ESP_STAT_TC)) { + state++; + if (op->count && blocksize) { + /* Data phase. */ + u32 count = (u32)op->count * blocksize; + esp_scsi_dma(iobase, (u32)op->buf_fl, count, + cdb_is_read(cdbcmd, blocksize)); + outb(ESP_CMD_TI | ESP_CMD_DMA, iobase + ESP_CMD); + continue; + } + } + + /* At end of DMA TC is set again -> complete command. */ + if (state == 1 && (stat & ESP_STAT_TC)) { + state++; + outb(ESP_CMD_ICCS, iobase + ESP_CMD); + continue; + } + + /* Finally read data from the message in phase. */ + if (state == 2 && (stat & ESP_STAT_MSG)) { + state++; + status = inb(iobase + ESP_FIFO); + inb(iobase + ESP_FIFO); + break; + } + usleep(5); + } + + if (status == 0) { + return DISK_RET_SUCCESS; + } + + return DISK_RET_EBADTRACK; +} + +int +esp_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_ESP_SCSI) + return DISK_RET_EBADTRACK; + + struct esp_lun_s *llun = + container_of(op->drive_g, struct esp_lun_s, drive); + + return esp_scsi_cmd(llun, op, cdbcmd, + GET_GLOBAL(llun->target), GET_GLOBAL(llun->lun), + blocksize); +} + +static int +esp_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct esp_lun_s *llun = malloc_fseg(sizeof(*llun)); + if (!llun) { + warn_noalloc(); + return -1; + } + memset(llun, 0, sizeof(*llun)); + llun->drive.type = DTYPE_ESP_SCSI; + llun->drive.cntl_id = pci->bdf; + llun->pci = pci; + llun->target = target; + llun->lun = lun; + llun->iobase = iobase; + + char *name = znprintf(16, "esp %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_init_drive(&llun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(llun); + return -1; +} + +static void +esp_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target) +{ + esp_scsi_add_lun(pci, iobase, target, 0); +} + +static void +init_esp_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + + dprintf(1, "found esp at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + // reset + outb(ESP_CMD_RESET, iobase + ESP_CMD); + + int i; + for (i = 0; i <= 7; i++) + esp_scsi_scan_target(pci, iobase, i); + + return; +} + +void +esp_scsi_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_ESP_SCSI) + return; + + dprintf(3, "init esp\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_AMD + || pci->device != PCI_DEVICE_ID_AMD_SCSI) + continue; + init_esp_scsi(pci); + } +} diff --git a/src/post.c b/src/post.c index 0f31b4c..924b311 100644 --- a/src/post.c +++ b/src/post.c @@ -28,6 +28,7 @@ #include "virtio-blk.h" // virtio_blk_setup #include "virtio-scsi.h" // virtio_scsi_setup #include "lsi-scsi.h" // lsi_scsi_setup +#include "esp-scsi.h" // esp_scsi_setup
/**************************************************************** @@ -196,6 +197,7 @@ init_hw(void) virtio_blk_setup(); virtio_scsi_setup(); lsi_scsi_setup(); + esp_scsi_setup(); }
// Begin the boot process by invoking an int0x19 in 16bit mode.
On Thu, Aug 02, 2012 at 06:26:55PM +0200, Paolo Bonzini wrote:
This is a driver for an AMD PCscsi (am53c974) SCSI card. It can be used together with DOS or old operating systems such as Windows NT 3.1, Windows 3.1 or Windows 98.
Thanks.
What's the use case for this - is it a virtual device implemented in qemu/kvm or is this for real hardware? If it's the former, the Kconfig entry should be clear on this. Otherwise, the patch looks fine to me.
-Kevin
Il 03/08/2012 00:46, Kevin O'Connor ha scritto:
This is a driver for an AMD PCscsi (am53c974) SCSI card. It can be used together with DOS or old operating systems such as Windows NT 3.1, Windows 3.1 or Windows 98.
Thanks.
What's the use case for this - is it a virtual device implemented in qemu/kvm or is this for real hardware? If it's the former, the Kconfig entry should be clear on this. Otherwise, the patch looks fine to me.
It is mostly useful as a virtual device implemented in qemu/kvm. It is quite old, but pretty simple so it should work even with the real one.
Paolo
On Thu, Aug 02, 2012 at 06:26:55PM +0200, Paolo Bonzini wrote:
This is a driver for an AMD PCscsi (am53c974) SCSI card. It can be used together with DOS or old operating systems such as Windows NT 3.1, Windows 3.1 or Windows 98.
Cc: Hervé Poussineau hpoussin@reactos.org Signed-off-by: Paolo Bonzini pbonzini@redhat.com
Makefile | 2 +- src/Kconfig | 6 ++ src/block.c | 1 + src/blockcmd.c | 3 + src/disk.h | 1 + src/esp-scsi.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/post.c | 2 + 7 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 src/esp-scsi.c
[...]
--- a/src/post.c +++ b/src/post.c @@ -28,6 +28,7 @@ #include "virtio-blk.h" // virtio_blk_setup #include "virtio-scsi.h" // virtio_scsi_setup #include "lsi-scsi.h" // lsi_scsi_setup +#include "esp-scsi.h" // esp_scsi_setup
Your patch is missing "esp-scsi.h".
If you respin, please add a "!COREBOOT" clause to the Kconfig and update the description to say it has only been tested on emulators. If someone has the hardware and can confirm it works on real machines then we can take the clause out - but I'm leery of enabling a hardware driver without having tested it first.
Thanks. -Kevin