aboutsummaryrefslogtreecommitdiffstats
path: root/include/hw/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'include/hw/scsi')
-rw-r--r--include/hw/scsi/emulation.h16
-rw-r--r--include/hw/scsi/esp.h163
-rw-r--r--include/hw/scsi/scsi.h229
3 files changed, 408 insertions, 0 deletions
diff --git a/include/hw/scsi/emulation.h b/include/hw/scsi/emulation.h
new file mode 100644
index 000000000..952170432
--- /dev/null
+++ b/include/hw/scsi/emulation.h
@@ -0,0 +1,16 @@
+#ifndef HW_SCSI_EMULATION_H
+#define HW_SCSI_EMULATION_H
+
+typedef struct SCSIBlockLimits {
+ bool wsnz;
+ uint16_t min_io_size;
+ uint32_t max_unmap_descr;
+ uint32_t opt_io_size;
+ uint32_t max_unmap_sectors;
+ uint32_t unmap_sectors;
+ uint32_t max_io_sectors;
+} SCSIBlockLimits;
+
+int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl);
+
+#endif
diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h
new file mode 100644
index 000000000..b1ec27612
--- /dev/null
+++ b/include/hw/scsi/esp.h
@@ -0,0 +1,163 @@
+#ifndef QEMU_HW_ESP_H
+#define QEMU_HW_ESP_H
+
+#include "hw/scsi/scsi.h"
+#include "hw/sysbus.h"
+#include "qemu/fifo8.h"
+#include "qom/object.h"
+
+/* esp.c */
+#define ESP_MAX_DEVS 7
+typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len);
+
+#define ESP_REGS 16
+#define ESP_FIFO_SZ 16
+#define ESP_CMDFIFO_SZ 32
+
+typedef struct ESPState ESPState;
+
+#define TYPE_ESP "esp"
+OBJECT_DECLARE_SIMPLE_TYPE(ESPState, ESP)
+
+struct ESPState {
+ DeviceState parent_obj;
+
+ uint8_t rregs[ESP_REGS];
+ uint8_t wregs[ESP_REGS];
+ qemu_irq irq;
+ qemu_irq irq_data;
+ uint8_t chip_id;
+ bool tchi_written;
+ int32_t ti_size;
+ uint32_t status;
+ uint32_t dma;
+ Fifo8 fifo;
+ SCSIBus bus;
+ SCSIDevice *current_dev;
+ SCSIRequest *current_req;
+ Fifo8 cmdfifo;
+ uint8_t cmdfifo_cdb_offset;
+ uint8_t lun;
+ uint32_t do_cmd;
+
+ bool data_in_ready;
+ uint8_t ti_cmd;
+ int dma_enabled;
+
+ uint32_t async_len;
+ uint8_t *async_buf;
+
+ ESPDMAMemoryReadWriteFunc dma_memory_read;
+ ESPDMAMemoryReadWriteFunc dma_memory_write;
+ void *dma_opaque;
+ void (*dma_cb)(ESPState *s);
+ void (*pdma_cb)(ESPState *s);
+
+ uint8_t mig_version_id;
+
+ /* Legacy fields for vmstate_esp version < 5 */
+ uint32_t mig_dma_left;
+ uint32_t mig_deferred_status;
+ bool mig_deferred_complete;
+ uint32_t mig_ti_rptr, mig_ti_wptr;
+ uint8_t mig_ti_buf[ESP_FIFO_SZ];
+ uint8_t mig_cmdbuf[ESP_CMDFIFO_SZ];
+ uint32_t mig_cmdlen;
+};
+
+#define TYPE_SYSBUS_ESP "sysbus-esp"
+OBJECT_DECLARE_SIMPLE_TYPE(SysBusESPState, SYSBUS_ESP)
+
+struct SysBusESPState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion iomem;
+ MemoryRegion pdma;
+ uint32_t it_shift;
+ ESPState esp;
+};
+
+#define ESP_TCLO 0x0
+#define ESP_TCMID 0x1
+#define ESP_FIFO 0x2
+#define ESP_CMD 0x3
+#define ESP_RSTAT 0x4
+#define ESP_WBUSID 0x4
+#define ESP_RINTR 0x5
+#define ESP_WSEL 0x5
+#define ESP_RSEQ 0x6
+#define ESP_WSYNTP 0x6
+#define ESP_RFLAGS 0x7
+#define ESP_WSYNO 0x7
+#define ESP_CFG1 0x8
+#define ESP_RRES1 0x9
+#define ESP_WCCF 0x9
+#define ESP_RRES2 0xa
+#define ESP_WTEST 0xa
+#define ESP_CFG2 0xb
+#define ESP_CFG3 0xc
+#define ESP_RES3 0xd
+#define ESP_TCHI 0xe
+#define ESP_RES4 0xf
+
+#define CMD_DMA 0x80
+#define CMD_CMD 0x7f
+
+#define CMD_NOP 0x00
+#define CMD_FLUSH 0x01
+#define CMD_RESET 0x02
+#define CMD_BUSRESET 0x03
+#define CMD_TI 0x10
+#define CMD_ICCS 0x11
+#define CMD_MSGACC 0x12
+#define CMD_PAD 0x18
+#define CMD_SATN 0x1a
+#define CMD_RSTATN 0x1b
+#define CMD_SEL 0x41
+#define CMD_SELATN 0x42
+#define CMD_SELATNS 0x43
+#define CMD_ENSEL 0x44
+#define CMD_DISSEL 0x45
+
+#define STAT_DO 0x00
+#define STAT_DI 0x01
+#define STAT_CD 0x02
+#define STAT_ST 0x03
+#define STAT_MO 0x06
+#define STAT_MI 0x07
+#define STAT_PIO_MASK 0x06
+
+#define STAT_TC 0x10
+#define STAT_PE 0x20
+#define STAT_GE 0x40
+#define STAT_INT 0x80
+
+#define BUSID_DID 0x07
+
+#define INTR_FC 0x08
+#define INTR_BS 0x10
+#define INTR_DC 0x20
+#define INTR_RST 0x80
+
+#define SEQ_0 0x0
+#define SEQ_MO 0x1
+#define SEQ_CD 0x4
+
+#define CFG1_RESREPT 0x40
+
+#define TCHI_FAS100A 0x4
+#define TCHI_AM53C974 0x12
+
+void esp_dma_enable(ESPState *s, int irq, int level);
+void esp_request_cancelled(SCSIRequest *req);
+void esp_command_complete(SCSIRequest *req, size_t resid);
+void esp_transfer_data(SCSIRequest *req, uint32_t len);
+void esp_hard_reset(ESPState *s);
+uint64_t esp_reg_read(ESPState *s, uint32_t saddr);
+void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val);
+extern const VMStateDescription vmstate_esp;
+int esp_pre_save(void *opaque);
+
+#endif
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
new file mode 100644
index 000000000..a567a5ed8
--- /dev/null
+++ b/include/hw/scsi/scsi.h
@@ -0,0 +1,229 @@
+#ifndef QEMU_HW_SCSI_H
+#define QEMU_HW_SCSI_H
+
+#include "block/aio.h"
+#include "hw/block/block.h"
+#include "hw/qdev-core.h"
+#include "scsi/utils.h"
+#include "qemu/notify.h"
+#include "qom/object.h"
+
+#define MAX_SCSI_DEVS 255
+
+typedef struct SCSIBus SCSIBus;
+typedef struct SCSIBusInfo SCSIBusInfo;
+typedef struct SCSIDevice SCSIDevice;
+typedef struct SCSIRequest SCSIRequest;
+typedef struct SCSIReqOps SCSIReqOps;
+
+#define SCSI_SENSE_BUF_SIZE_OLD 96
+#define SCSI_SENSE_BUF_SIZE 252
+#define DEFAULT_IO_TIMEOUT 30
+
+struct SCSIRequest {
+ SCSIBus *bus;
+ SCSIDevice *dev;
+ const SCSIReqOps *ops;
+ uint32_t refcount;
+ uint32_t tag;
+ uint32_t lun;
+ int16_t status;
+ int16_t host_status;
+ void *hba_private;
+ size_t resid;
+ SCSICommand cmd;
+ NotifierList cancel_notifiers;
+
+ /* Note:
+ * - fields before sense are initialized by scsi_req_alloc;
+ * - sense[] is uninitialized;
+ * - fields after sense are memset to 0 by scsi_req_alloc.
+ * */
+
+ uint8_t sense[SCSI_SENSE_BUF_SIZE];
+ uint32_t sense_len;
+ bool enqueued;
+ bool io_canceled;
+ bool retry;
+ bool dma_started;
+ BlockAIOCB *aiocb;
+ QEMUSGList *sg;
+ QTAILQ_ENTRY(SCSIRequest) next;
+};
+
+#define TYPE_SCSI_DEVICE "scsi-device"
+OBJECT_DECLARE_TYPE(SCSIDevice, SCSIDeviceClass, SCSI_DEVICE)
+
+struct SCSIDeviceClass {
+ DeviceClass parent_class;
+ void (*realize)(SCSIDevice *dev, Error **errp);
+ void (*unrealize)(SCSIDevice *dev);
+ int (*parse_cdb)(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf,
+ void *hba_private);
+ SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun,
+ uint8_t *buf, void *hba_private);
+ void (*unit_attention_reported)(SCSIDevice *s);
+};
+
+struct SCSIDevice
+{
+ DeviceState qdev;
+ VMChangeStateEntry *vmsentry;
+ QEMUBH *bh;
+ uint32_t id;
+ BlockConf conf;
+ SCSISense unit_attention;
+ bool sense_is_ua;
+ uint8_t sense[SCSI_SENSE_BUF_SIZE];
+ uint32_t sense_len;
+ QTAILQ_HEAD(, SCSIRequest) requests;
+ uint32_t channel;
+ uint32_t lun;
+ int blocksize;
+ int type;
+ uint64_t max_lba;
+ uint64_t wwn;
+ uint64_t port_wwn;
+ int scsi_version;
+ int default_scsi_version;
+ uint32_t io_timeout;
+ bool needs_vpd_bl_emulation;
+ bool hba_supports_iothread;
+};
+
+extern const VMStateDescription vmstate_scsi_device;
+
+#define VMSTATE_SCSI_DEVICE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(SCSIDevice), \
+ .vmsd = &vmstate_scsi_device, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, SCSIDevice), \
+}
+
+/* cdrom.c */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
+
+/* scsi-bus.c */
+struct SCSIReqOps {
+ size_t size;
+ void (*free_req)(SCSIRequest *req);
+ int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
+ void (*read_data)(SCSIRequest *req);
+ void (*write_data)(SCSIRequest *req);
+ uint8_t *(*get_buf)(SCSIRequest *req);
+
+ void (*save_request)(QEMUFile *f, SCSIRequest *req);
+ void (*load_request)(QEMUFile *f, SCSIRequest *req);
+};
+
+struct SCSIBusInfo {
+ int tcq;
+ int max_channel, max_target, max_lun;
+ int (*parse_cdb)(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf,
+ void *hba_private);
+ void (*transfer_data)(SCSIRequest *req, uint32_t arg);
+ void (*fail)(SCSIRequest *req);
+ void (*complete)(SCSIRequest *req, size_t resid);
+ void (*cancel)(SCSIRequest *req);
+ void (*change)(SCSIBus *bus, SCSIDevice *dev, SCSISense sense);
+ QEMUSGList *(*get_sg_list)(SCSIRequest *req);
+
+ void (*save_request)(QEMUFile *f, SCSIRequest *req);
+ void *(*load_request)(QEMUFile *f, SCSIRequest *req);
+ void (*free_request)(SCSIBus *bus, void *priv);
+};
+
+#define TYPE_SCSI_BUS "SCSI"
+OBJECT_DECLARE_SIMPLE_TYPE(SCSIBus, SCSI_BUS)
+
+struct SCSIBus {
+ BusState qbus;
+ int busnr;
+
+ SCSISense unit_attention;
+ const SCSIBusInfo *info;
+};
+
+/**
+ * scsi_bus_init_named: Initialize a SCSI bus with the specified name
+ * @bus: SCSIBus object to initialize
+ * @bus_size: size of @bus object
+ * @host: Device which owns the bus (generally the SCSI controller)
+ * @info: structure defining callbacks etc for the controller
+ * @bus_name: Name to use for this bus
+ *
+ * This in-place initializes @bus as a new SCSI bus with a name
+ * provided by the caller. It is the caller's responsibility to make
+ * sure that name does not clash with the name of any other bus in the
+ * system. Unless you need the new bus to have a specific name, you
+ * should use scsi_bus_new() instead.
+ */
+void scsi_bus_init_named(SCSIBus *bus, size_t bus_size, DeviceState *host,
+ const SCSIBusInfo *info, const char *bus_name);
+
+/**
+ * scsi_bus_init: Initialize a SCSI bus
+ *
+ * This in-place-initializes @bus as a new SCSI bus and gives it
+ * an automatically generated unique name.
+ */
+static inline void scsi_bus_init(SCSIBus *bus, size_t bus_size,
+ DeviceState *host, const SCSIBusInfo *info)
+{
+ scsi_bus_init_named(bus, bus_size, host, info, NULL);
+}
+
+static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
+{
+ return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus);
+}
+
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk,
+ int unit, bool removable, int bootindex,
+ bool share_rw,
+ BlockdevOnError rerror,
+ BlockdevOnError werror,
+ const char *serial, Error **errp);
+void scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
+void scsi_legacy_handle_cmdline(void);
+
+SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
+ uint32_t tag, uint32_t lun, void *hba_private);
+SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
+ uint8_t *buf, void *hba_private);
+int32_t scsi_req_enqueue(SCSIRequest *req);
+SCSIRequest *scsi_req_ref(SCSIRequest *req);
+void scsi_req_unref(SCSIRequest *req);
+
+int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf,
+ void *hba_private);
+int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf);
+void scsi_req_build_sense(SCSIRequest *req, SCSISense sense);
+void scsi_req_print(SCSIRequest *req);
+void scsi_req_continue(SCSIRequest *req);
+void scsi_req_data(SCSIRequest *req, int len);
+void scsi_req_complete(SCSIRequest *req, int status);
+void scsi_req_complete_failed(SCSIRequest *req, int host_status);
+uint8_t *scsi_req_get_buf(SCSIRequest *req);
+int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len);
+void scsi_req_cancel_complete(SCSIRequest *req);
+void scsi_req_cancel(SCSIRequest *req);
+void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier);
+void scsi_req_retry(SCSIRequest *req);
+void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense);
+void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense);
+void scsi_device_report_change(SCSIDevice *dev, SCSISense sense);
+void scsi_device_unit_attention_reported(SCSIDevice *dev);
+void scsi_generic_read_device_inquiry(SCSIDevice *dev);
+int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
+int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size,
+ uint8_t *buf, uint8_t buf_size, uint32_t timeout);
+SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
+SCSIDevice *scsi_device_get(SCSIBus *bus, int channel, int target, int lun);
+
+/* scsi-generic.c. */
+extern const SCSIReqOps scsi_generic_req_ops;
+
+#endif