Remove unnecessary trailing newlines
[qemu] / hw / lsi53c895a.c
index 24dff0e..81d5672 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * QEMU LSI53C895A SCSI Host Bus Adapter emulation
  *
  * Copyright (c) 2006 CodeSourcery.
@@ -10,7 +10,9 @@
 /* ??? Need to check if the {read,write}[wl] routines work properly on
    big-endian targets.  */
 
-#include "vl.h"
+#include "hw.h"
+#include "pci.h"
+#include "scsi-disk.h"
 
 //#define DEBUG_LSI
 //#define DEBUG_LSI_REG
 #define DPRINTF(fmt, args...) \
 do { printf("lsi_scsi: " fmt , ##args); } while (0)
 #define BADF(fmt, args...) \
-do { fprintf(stderr, "lsi_scsi: " fmt , ##args); exit(1);} while (0)
+do { fprintf(stderr, "lsi_scsi: error: " fmt , ##args); exit(1);} while (0)
 #else
 #define DPRINTF(fmt, args...) do {} while(0)
 #define BADF(fmt, args...) \
-do { fprintf(stderr, "lsi_scsi: " fmt , ##args); } while (0)
+do { fprintf(stderr, "lsi_scsi: error: " fmt , ##args);} while (0)
 #endif
 
 #define LSI_SCNTL0_TRG    0x01
@@ -141,6 +143,14 @@ do { fprintf(stderr, "lsi_scsi: " fmt , ##args); } while (0)
 #define LSI_CCNTL0_PMJCTL 0x40
 #define LSI_CCNTL0_ENPMJ  0x80
 
+#define LSI_CCNTL1_EN64DBMV  0x01
+#define LSI_CCNTL1_EN64TIBMV 0x02
+#define LSI_CCNTL1_64TIMOD   0x04
+#define LSI_CCNTL1_DDAC      0x08
+#define LSI_CCNTL1_ZMOD      0x80
+
+#define LSI_CCNTL1_40BIT (LSI_CCNTL1_EN64TIBMV|LSI_CCNTL1_64TIMOD)
+
 #define PHASE_DO          0
 #define PHASE_DI          1
 #define PHASE_CMD         2
@@ -149,24 +159,47 @@ do { fprintf(stderr, "lsi_scsi: " fmt , ##args); } while (0)
 #define PHASE_MI          7
 #define PHASE_MASK        7
 
-/* The HBA is ID 7, so for simplicitly limit to 7 devices.  */
-#define LSI_MAX_DEVS      7
+/* Maximum length of MSG IN data.  */
+#define LSI_MAX_MSGIN_LEN 8
+
+/* Flag set if this is a tagged command.  */
+#define LSI_TAG_VALID     (1 << 16)
+
+typedef struct {
+    uint32_t tag;
+    uint32_t pending;
+    int out;
+} lsi_queue;
 
 typedef struct {
     PCIDevice pci_dev;
     int mmio_io_addr;
     int ram_io_addr;
     uint32_t script_ram_base;
-    uint32_t data_len;
 
     int carry; /* ??? Should this be an a visible register somewhere?  */
     int sense;
-    uint8_t msg;
-    /* Nonzero if a Wait Reselect instruction has been issued.  */
+    /* Action to take at the end of a MSG IN phase.
+       0 = COMMAND, 1 = disconect, 2 = DATA OUT, 3 = DATA IN.  */
+    int msg_action;
+    int msg_len;
+    uint8_t msg[LSI_MAX_MSGIN_LEN];
+    /* 0 if SCRIPTS are running or stopped.
+     * 1 if a Wait Reselect instruction has been issued.
+     * 2 if processing DMA from lsi_execute_script.
+     * 3 if a DMA operation is in progress.  */
     int waiting;
     SCSIDevice *scsi_dev[LSI_MAX_DEVS];
     SCSIDevice *current_dev;
     int current_lun;
+    /* The tag is a combination of the device ID and the SCSI tag.  */
+    uint32_t current_tag;
+    uint32_t current_dma_len;
+    int command_complete;
+    uint8_t *dma_buf;
+    lsi_queue *queue;
+    int queue_len;
+    int active_commands;
 
     uint32_t dsa;
     uint32_t temp;
@@ -184,6 +217,7 @@ typedef struct {
     uint8_t mbox0;
     uint8_t mbox1;
     uint8_t dfifo;
+    uint8_t ctest2;
     uint8_t ctest3;
     uint8_t ctest4;
     uint8_t ctest5;
@@ -203,10 +237,12 @@ typedef struct {
     uint8_t sxfer;
     uint8_t socl;
     uint8_t sdid;
+    uint8_t ssid;
     uint8_t sfbr;
     uint8_t stest1;
     uint8_t stest2;
     uint8_t stest3;
+    uint8_t sidl;
     uint8_t stime0;
     uint8_t respid0;
     uint8_t respid1;
@@ -215,7 +251,7 @@ typedef struct {
     uint32_t sfs;
     uint32_t drs;
     uint32_t sbms;
-    uint32_t dmbs;
+    uint32_t dbms;
     uint32_t dnad64;
     uint32_t pmjad1;
     uint32_t pmjad2;
@@ -224,7 +260,7 @@ typedef struct {
     uint32_t ia;
     uint32_t sbc;
     uint32_t csbc;
-    uint32_t scratch[13]; /* SCRATCHA-SCRATCHR */
+    uint32_t scratch[18]; /* SCRATCHA-SCRATCHR */
 
     /* Script ram is stored as 32-bit words in host byteorder.  */
     uint32_t script_ram[2048];
@@ -253,6 +289,7 @@ static void lsi_soft_reset(LSIState *s)
     s->mbox0 = 0;
     s->mbox1 = 0;
     s->dfifo = 0;
+    s->ctest2 = 0;
     s->ctest3 = 0;
     s->ctest4 = 0;
     s->ctest5 = 0;
@@ -274,6 +311,7 @@ static void lsi_soft_reset(LSIState *s)
     s->stest1 = 0;
     s->stest2 = 0;
     s->stest3 = 0;
+    s->sidl = 0;
     s->stime0 = 0;
     s->respid0 = 0x80;
     s->respid1 = 0;
@@ -282,7 +320,7 @@ static void lsi_soft_reset(LSIState *s)
     s->sfs = 0;
     s->drs = 0;
     s->sbms = 0;
-    s->dmbs = 0;
+    s->dbms = 0;
     s->dnad64 = 0;
     s->pmjad1 = 0;
     s->pmjad2 = 0;
@@ -293,8 +331,30 @@ static void lsi_soft_reset(LSIState *s)
     s->csbc = 0;
 }
 
+static int lsi_dma_40bit(LSIState *s)
+{
+    if ((s->ccntl1 & LSI_CCNTL1_40BIT) == LSI_CCNTL1_40BIT)
+        return 1;
+    return 0;
+}
+
+static int lsi_dma_ti64bit(LSIState *s)
+{
+    if ((s->ccntl1 & LSI_CCNTL1_EN64TIBMV) == LSI_CCNTL1_EN64TIBMV)
+        return 1;
+    return 0;
+}
+
+static int lsi_dma_64bit(LSIState *s)
+{
+    if ((s->ccntl1 & LSI_CCNTL1_EN64DBMV) == LSI_CCNTL1_EN64DBMV)
+        return 1;
+    return 0;
+}
+
 static uint8_t lsi_reg_readb(LSIState *s, int offset);
 static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val);
+static void lsi_execute_script(LSIState *s);
 
 static inline uint32_t read_dword(LSIState *s, uint32_t addr)
 {
@@ -345,7 +405,7 @@ static void lsi_update_irq(LSIState *s)
                 level, s->dstat, s->sist1, s->sist0);
         last_level = level;
     }
-    pci_set_irq(&s->pci_dev, 0, level);
+    qemu_set_irq(s->pci_dev.irq[0], level);
 }
 
 /* Stop SCRIPTS execution and raise a SCSI interrupt.  */
@@ -402,44 +462,207 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase)
     lsi_set_phase(s, new_phase);
 }
 
+
+/* Resume SCRIPTS execution after a DMA operation.  */
+static void lsi_resume_script(LSIState *s)
+{
+    if (s->waiting != 2) {
+        s->waiting = 0;
+        lsi_execute_script(s);
+    } else {
+        s->waiting = 0;
+    }
+}
+
+/* Initiate a SCSI layer data transfer.  */
 static void lsi_do_dma(LSIState *s, int out)
 {
-    uint8_t buf[TARGET_PAGE_SIZE];
-    uint32_t addr;
     uint32_t count;
-    int n;
+    target_phys_addr_t addr;
 
-    count = s->dbc;
-    addr = s->dnad;
-    DPRINTF("DMA %s addr=0x%08x len=%d avail=%d\n", out ? "out" : "in",
-            addr, count, s->data_len);
-    /* ??? Too long transfers are truncated. Don't know if this is the
-       correct behavior.  */
-    if (count > s->data_len) {
-        /* If the DMA length is greater then the device data length then
-           a phase mismatch will occur.  */
-        count = s->data_len;
-        s->dbc = count;
-        lsi_bad_phase(s, out, PHASE_ST);
+    if (!s->current_dma_len) {
+        /* Wait until data is available.  */
+        DPRINTF("DMA no data available\n");
+        return;
     }
 
+    count = s->dbc;
+    if (count > s->current_dma_len)
+        count = s->current_dma_len;
+
+    addr = s->dnad;
+    /* both 40 and Table Indirect 64-bit DMAs store upper bits in dnad64 */
+    if (lsi_dma_40bit(s) || lsi_dma_ti64bit(s))
+        addr |= ((uint64_t)s->dnad64 << 32);
+    else if (s->dbms)
+        addr |= ((uint64_t)s->dbms << 32);
+    else if (s->sbms)
+        addr |= ((uint64_t)s->sbms << 32);
+
+    DPRINTF("DMA addr=0x" TARGET_FMT_plx " len=%d\n", addr, count);
     s->csbc += count;
+    s->dnad += count;
+    s->dbc -= count;
+
+    if (s->dma_buf == NULL) {
+        s->dma_buf = s->current_dev->get_buf(s->current_dev,
+                                             s->current_tag);
+    }
 
     /* ??? Set SFBR to first data byte.  */
-    while (count) {
-        n = (count > TARGET_PAGE_SIZE) ? TARGET_PAGE_SIZE : count;
+    if (out) {
+        cpu_physical_memory_read(addr, s->dma_buf, count);
+    } else {
+        cpu_physical_memory_write(addr, s->dma_buf, count);
+    }
+    s->current_dma_len -= count;
+    if (s->current_dma_len == 0) {
+        s->dma_buf = NULL;
         if (out) {
-            cpu_physical_memory_read(addr, buf, n);
-            scsi_write_data(s->current_dev, buf, n);
+            /* Write the data.  */
+            s->current_dev->write_data(s->current_dev, s->current_tag);
         } else {
-            scsi_read_data(s->current_dev, buf, n);
-            cpu_physical_memory_write(addr, buf, n);
+            /* Request any remaining data.  */
+            s->current_dev->read_data(s->current_dev, s->current_tag);
+        }
+    } else {
+        s->dma_buf += count;
+        lsi_resume_script(s);
+    }
+}
+
+
+/* Add a command to the queue.  */
+static void lsi_queue_command(LSIState *s)
+{
+    lsi_queue *p;
+
+    DPRINTF("Queueing tag=0x%x\n", s->current_tag);
+    if (s->queue_len == s->active_commands) {
+        s->queue_len++;
+        s->queue = qemu_realloc(s->queue, s->queue_len * sizeof(lsi_queue));
+    }
+    p = &s->queue[s->active_commands++];
+    p->tag = s->current_tag;
+    p->pending = 0;
+    p->out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
+}
+
+/* Queue a byte for a MSG IN phase.  */
+static void lsi_add_msg_byte(LSIState *s, uint8_t data)
+{
+    if (s->msg_len >= LSI_MAX_MSGIN_LEN) {
+        BADF("MSG IN data too long\n");
+    } else {
+        DPRINTF("MSG IN 0x%02x\n", data);
+        s->msg[s->msg_len++] = data;
+    }
+}
+
+/* Perform reselection to continue a command.  */
+static void lsi_reselect(LSIState *s, uint32_t tag)
+{
+    lsi_queue *p;
+    int n;
+    int id;
+
+    p = NULL;
+    for (n = 0; n < s->active_commands; n++) {
+        p = &s->queue[n];
+        if (p->tag == tag)
+            break;
+    }
+    if (n == s->active_commands) {
+        BADF("Reselected non-existant command tag=0x%x\n", tag);
+        return;
+    }
+    id = (tag >> 8) & 0xf;
+    s->ssid = id | 0x80;
+    DPRINTF("Reselected target %d\n", id);
+    s->current_dev = s->scsi_dev[id];
+    s->current_tag = tag;
+    s->scntl1 |= LSI_SCNTL1_CON;
+    lsi_set_phase(s, PHASE_MI);
+    s->msg_action = p->out ? 2 : 3;
+    s->current_dma_len = p->pending;
+    s->dma_buf = NULL;
+    lsi_add_msg_byte(s, 0x80);
+    if (s->current_tag & LSI_TAG_VALID) {
+        lsi_add_msg_byte(s, 0x20);
+        lsi_add_msg_byte(s, tag & 0xff);
+    }
+
+    s->active_commands--;
+    if (n != s->active_commands) {
+        s->queue[n] = s->queue[s->active_commands];
+    }
+}
+
+/* Record that data is available for a queued command.  Returns zero if
+   the device was reselected, nonzero if the IO is deferred.  */
+static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
+{
+    lsi_queue *p;
+    int i;
+    for (i = 0; i < s->active_commands; i++) {
+        p = &s->queue[i];
+        if (p->tag == tag) {
+            if (p->pending) {
+                BADF("Multiple IO pending for tag %d\n", tag);
+            }
+            p->pending = arg;
+            if (s->waiting == 1) {
+                /* Reselect device.  */
+                lsi_reselect(s, tag);
+                return 0;
+            } else {
+               DPRINTF("Queueing IO tag=0x%x\n", tag);
+                p->pending = arg;
+                return 1;
+            }
         }
-        addr += n;
-        count -= n;
     }
+    BADF("IO with unknown tag %d\n", tag);
+    return 1;
 }
 
+/* Callback to indicate that the SCSI layer has completed a transfer.  */
+static void lsi_command_complete(void *opaque, int reason, uint32_t tag,
+                                 uint32_t arg)
+{
+    LSIState *s = (LSIState *)opaque;
+    int out;
+
+    out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
+    if (reason == SCSI_REASON_DONE) {
+        DPRINTF("Command complete sense=%d\n", (int)arg);
+        s->sense = arg;
+        s->command_complete = 2;
+        if (s->waiting && s->dbc != 0) {
+            /* Raise phase mismatch for short transfers.  */
+            lsi_bad_phase(s, out, PHASE_ST);
+        } else {
+            lsi_set_phase(s, PHASE_ST);
+        }
+        lsi_resume_script(s);
+        return;
+    }
+
+    if (s->waiting == 1 || tag != s->current_tag) {
+        if (lsi_queue_tag(s, tag, arg))
+            return;
+    }
+    DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg);
+    s->current_dma_len = arg;
+    s->command_complete = 1;
+    if (!s->waiting)
+        return;
+    if (s->waiting == 1 || s->dbc == 0) {
+        lsi_resume_script(s);
+    } else {
+        lsi_do_dma(s, out);
+    }
+}
 
 static void lsi_do_command(LSIState *s)
 {
@@ -451,36 +674,46 @@ static void lsi_do_command(LSIState *s)
         s->dbc = 16;
     cpu_physical_memory_read(s->dnad, buf, s->dbc);
     s->sfbr = buf[0];
-    n = scsi_send_command(s->current_dev, 0, buf, s->current_lun);
+    s->command_complete = 0;
+    n = s->current_dev->send_command(s->current_dev, s->current_tag, buf,
+                                     s->current_lun);
     if (n > 0) {
-        s->data_len = n;
         lsi_set_phase(s, PHASE_DI);
+        s->current_dev->read_data(s->current_dev, s->current_tag);
     } else if (n < 0) {
-        s->data_len = -n;
         lsi_set_phase(s, PHASE_DO);
+        s->current_dev->write_data(s->current_dev, s->current_tag);
     }
-}
-
-static void lsi_command_complete(void *opaque, uint32_t tag, int sense)
-{
-    LSIState *s = (LSIState *)opaque;
 
-    DPRINTF("Command complete sense=%d\n", sense);
-    s->sense = sense;
-    lsi_set_phase(s, PHASE_ST);
+    if (!s->command_complete) {
+        if (n) {
+            /* Command did not complete immediately so disconnect.  */
+            lsi_add_msg_byte(s, 2); /* SAVE DATA POINTER */
+            lsi_add_msg_byte(s, 4); /* DISCONNECT */
+            /* wait data */
+            lsi_set_phase(s, PHASE_MI);
+            s->msg_action = 1;
+            lsi_queue_command(s);
+        } else {
+            /* wait command complete */
+            lsi_set_phase(s, PHASE_DI);
+        }
+    }
 }
 
 static void lsi_do_status(LSIState *s)
 {
+    uint8_t sense;
     DPRINTF("Get status len=%d sense=%d\n", s->dbc, s->sense);
     if (s->dbc != 1)
         BADF("Bad Status move\n");
     s->dbc = 1;
-    s->msg = s->sense;
-    cpu_physical_memory_write(s->dnad, &s->msg, 1);
-    s->sfbr = s->msg;
+    sense = s->sense;
+    s->sfbr = sense;
+    cpu_physical_memory_write(s->dnad, &sense, 1);
     lsi_set_phase(s, PHASE_MI);
-    s->msg = 0; /* COMMAND COMPLETE */
+    s->msg_action = 1;
+    lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */
 }
 
 static void lsi_disconnect(LSIState *s)
@@ -491,55 +724,114 @@ static void lsi_disconnect(LSIState *s)
 
 static void lsi_do_msgin(LSIState *s)
 {
-    DPRINTF("Message in len=%d\n", s->dbc);
-    s->dbc = 1;
-    s->sfbr = s->msg;
-    cpu_physical_memory_write(s->dnad, &s->msg, 1);
-    if (s->msg == 0) {
-        lsi_disconnect(s);
+    int len;
+    DPRINTF("Message in len=%d/%d\n", s->dbc, s->msg_len);
+    s->sfbr = s->msg[0];
+    len = s->msg_len;
+    if (len > s->dbc)
+        len = s->dbc;
+    cpu_physical_memory_write(s->dnad, s->msg, len);
+    /* Linux drivers rely on the last byte being in the SIDL.  */
+    s->sidl = s->msg[len - 1];
+    s->msg_len -= len;
+    if (s->msg_len) {
+        memmove(s->msg, s->msg + len, s->msg_len);
     } else {
         /* ??? Check if ATN (not yet implemented) is asserted and maybe
            switch to PHASE_MO.  */
-        lsi_set_phase(s, PHASE_CMD);
+        switch (s->msg_action) {
+        case 0:
+            lsi_set_phase(s, PHASE_CMD);
+            break;
+        case 1:
+            lsi_disconnect(s);
+            break;
+        case 2:
+            lsi_set_phase(s, PHASE_DO);
+            break;
+        case 3:
+            lsi_set_phase(s, PHASE_DI);
+            break;
+        default:
+            abort();
+        }
     }
 }
 
+/* Read the next byte during a MSGOUT phase.  */
+static uint8_t lsi_get_msgbyte(LSIState *s)
+{
+    uint8_t data;
+    cpu_physical_memory_read(s->dnad, &data, 1);
+    s->dnad++;
+    s->dbc--;
+    return data;
+}
+
 static void lsi_do_msgout(LSIState *s)
 {
     uint8_t msg;
+    int len;
 
     DPRINTF("MSG out len=%d\n", s->dbc);
-    if (s->dbc != 1) {
-        /* Multibyte messages not implemented.  */
-        s->msg = 7; /* MESSAGE REJECT */
-        //s->dbc = 1;
-        //lsi_bad_phase(s, 1, PHASE_MI);
-        lsi_set_phase(s, PHASE_MI);
-        return;
-    }
-    cpu_physical_memory_read(s->dnad, &msg, 1);
-    s->sfbr = msg;
-    s->dnad++;
-
-    switch (msg) {
-    case 0x00:
-        DPRINTF("Got Disconnect\n");
-        lsi_disconnect(s);
-        return;
-    case 0x08:
-        DPRINTF("Got No Operation\n");
-        lsi_set_phase(s, PHASE_CMD);
-        return;
-    }
-    if ((msg & 0x80) == 0) {
-        DPRINTF("Unimplemented message 0x%d\n", msg);
-        s->msg = 7; /* MESSAGE REJECT */
-        lsi_bad_phase(s, 1, PHASE_MI);
-        return;
+    while (s->dbc) {
+        msg = lsi_get_msgbyte(s);
+        s->sfbr = msg;
+
+        switch (msg) {
+        case 0x00:
+            DPRINTF("MSG: Disconnect\n");
+            lsi_disconnect(s);
+            break;
+        case 0x08:
+            DPRINTF("MSG: No Operation\n");
+            lsi_set_phase(s, PHASE_CMD);
+            break;
+        case 0x01:
+            len = lsi_get_msgbyte(s);
+            msg = lsi_get_msgbyte(s);
+            DPRINTF("Extended message 0x%x (len %d)\n", msg, len);
+            switch (msg) {
+            case 1:
+                DPRINTF("SDTR (ignored)\n");
+                s->dbc -= 2;
+                break;
+            case 3:
+                DPRINTF("WDTR (ignored)\n");
+                s->dbc -= 1;
+                break;
+            default:
+                goto bad;
+            }
+            break;
+        case 0x20: /* SIMPLE queue */
+            s->current_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
+            DPRINTF("SIMPLE queue tag=0x%x\n", s->current_tag & 0xff);
+            break;
+        case 0x21: /* HEAD of queue */
+            BADF("HEAD queue not implemented\n");
+            s->current_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
+            break;
+        case 0x22: /* ORDERED queue */
+            BADF("ORDERED queue not implemented\n");
+            s->current_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
+            break;
+        default:
+            if ((msg & 0x80) == 0) {
+                goto bad;
+            }
+            s->current_lun = msg & 7;
+            DPRINTF("Select LUN %d\n", s->current_lun);
+            lsi_set_phase(s, PHASE_CMD);
+            break;
+        }
     }
-    s->current_lun = msg & 7;
-    DPRINTF("Select LUN %d\n", s->current_lun);
-    lsi_set_phase(s, PHASE_CMD);
+    return;
+bad:
+    BADF("Unimplemented message 0x%02x\n", msg);
+    lsi_set_phase(s, PHASE_MI);
+    lsi_add_msg_byte(s, 7); /* MESSAGE REJECT */
+    s->msg_action = 0;
 }
 
 /* Sign extend a 24-bit value.  */
@@ -564,16 +856,42 @@ static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
     }
 }
 
+static void lsi_wait_reselect(LSIState *s)
+{
+    int i;
+    DPRINTF("Wait Reselect\n");
+    if (s->current_dma_len)
+        BADF("Reselect with pending DMA\n");
+    for (i = 0; i < s->active_commands; i++) {
+        if (s->queue[i].pending) {
+            lsi_reselect(s, s->queue[i].tag);
+            break;
+        }
+    }
+    if (s->current_dma_len == 0) {
+        s->waiting = 1;
+    }
+}
+
 static void lsi_execute_script(LSIState *s)
 {
     uint32_t insn;
-    uint32_t addr;
+    uint32_t addr, addr_high;
     int opcode;
+    int insn_processed = 0;
 
     s->istat1 |= LSI_ISTAT1_SRUN;
 again:
+    insn_processed++;
     insn = read_dword(s, s->dsp);
+    if (!insn) {
+        /* If we receive an empty opcode increment the DSP by 4 bytes
+           instead of 8 and execute the next opcode at that location */
+        s->dsp += 4;
+        goto again;
+    }
     addr = read_dword(s, s->dsp + 4);
+    addr_high = 0;
     DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr);
     s->dsps = addr;
     s->dcmd = insn >> 24;
@@ -587,6 +905,8 @@ again:
         }
         s->dbc = insn & 0xffffff;
         s->rbc = s->dbc;
+        /* ??? Set ESA.  */
+        s->ia = s->dsp - 8;
         if (insn & (1 << 29)) {
             /* Indirect addressing.  */
             addr = read_dword(s, addr);
@@ -594,10 +914,57 @@ again:
             uint32_t buf[2];
             int32_t offset;
             /* Table indirect addressing.  */
+
+            /* 32-bit Table indirect */
             offset = sxt24(addr);
             cpu_physical_memory_read(s->dsa + offset, (uint8_t *)buf, 8);
-            s->dbc = cpu_to_le32(buf[0]);
+            /* byte count is stored in bits 0:23 only */
+            s->dbc = cpu_to_le32(buf[0]) & 0xffffff;
+            s->rbc = s->dbc;
             addr = cpu_to_le32(buf[1]);
+
+            /* 40-bit DMA, upper addr bits [39:32] stored in first DWORD of
+             * table, bits [31:24] */
+            if (lsi_dma_40bit(s))
+                addr_high = cpu_to_le32(buf[0]) >> 24;
+            else if (lsi_dma_ti64bit(s)) {
+                int selector = (cpu_to_le32(buf[0]) >> 24) & 0x1f;
+                switch (selector) {
+                case 0 ... 0x0f:
+                    /* offset index into scratch registers since
+                     * TI64 mode can use registers C to R */
+                    addr_high = s->scratch[2 + selector];
+                    break;
+                case 0x10:
+                    addr_high = s->mmrs;
+                    break;
+                case 0x11:
+                    addr_high = s->mmws;
+                    break;
+                case 0x12:
+                    addr_high = s->sfs;
+                    break;
+                case 0x13:
+                    addr_high = s->drs;
+                    break;
+                case 0x14:
+                    addr_high = s->sbms;
+                    break;
+                case 0x15:
+                    addr_high = s->dbms;
+                    break;
+                default:
+                    BADF("Illegal selector specified (0x%x > 0x15)"
+                         " for 64-bit DMA block move", selector);
+                    break;
+                }
+            }
+        } else if (lsi_dma_64bit(s)) {
+            /* fetch a 3rd dword if 64-bit direct move is enabled and
+               only if we're not doing table indirect or indirect addressing */
+            s->dbms = read_dword(s, s->dsp);
+            s->dsp += 4;
+            s->ia = s->dsp - 12;
         }
         if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) {
             DPRINTF("Wrong phase got %d expected %d\n",
@@ -606,12 +973,19 @@ again:
             break;
         }
         s->dnad = addr;
+        s->dnad64 = addr_high;
         switch (s->sstat1 & 0x7) {
         case PHASE_DO:
+            s->waiting = 2;
             lsi_do_dma(s, 1);
+            if (s->waiting)
+                s->waiting = 3;
             break;
         case PHASE_DI:
+            s->waiting = 2;
             lsi_do_dma(s, 0);
+            if (s->waiting)
+                s->waiting = 3;
             break;
         case PHASE_CMD:
             lsi_do_command(s);
@@ -634,8 +1008,6 @@ again:
         s->sbc = s->dbc;
         s->rbc -= s->dbc;
         s->ua = addr + s->dbc;
-        /* ??? Set ESA.  */
-        s->ia = s->dsp - 8;
         break;
 
     case 1: /* IO or Read/Write instruction.  */
@@ -655,9 +1027,13 @@ again:
             s->dnad = addr;
             switch (opcode) {
             case 0: /* Select */
+                s->sdid = id;
+                if (s->current_dma_len && (s->ssid & 0xf) == id) {
+                    DPRINTF("Already reselected by target %d\n", id);
+                    break;
+                }
                 s->sstat0 |= LSI_SSTAT0_WOA;
                 s->scntl1 &= ~LSI_SCNTL1_IARB;
-                s->sdid = id;
                 if (id >= LSI_MAX_DEVS || !s->scsi_dev[id]) {
                     DPRINTF("Selected absent target %d\n", id);
                     lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
@@ -670,6 +1046,7 @@ again:
                    it only applies in low-level mode (unimplemented).
                 lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */
                 s->current_dev = s->scsi_dev[id];
+                s->current_tag = id << 8;
                 s->scntl1 |= LSI_SCNTL1_CON;
                 if (insn & (1 << 3)) {
                     s->socl |= LSI_SOCL_ATN;
@@ -681,8 +1058,7 @@ again:
                 s->scntl1 &= ~LSI_SCNTL1_CON;
                 break;
             case 2: /* Wait Reselect */
-                DPRINTF("Wait Reselect\n");
-                s->waiting = 1;
+                lsi_wait_reselect(s);
                 break;
             case 3: /* Set */
                 DPRINTF("Set%s%s%s%s\n",
@@ -731,9 +1107,9 @@ again:
             data8 = (insn >> 8) & 0xff;
             opcode = (insn >> 27) & 7;
             operator = (insn >> 24) & 7;
-            DPRINTF("%s reg 0x%x %s data8 %d%s\n",
+            DPRINTF("%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s\n",
                     opcode_names[opcode - 5], reg,
-                    operator_names[operator], data8,
+                    operator_names[operator], data8, s->sfbr,
                     (insn & (1 << 23)) ? " SFBR" : "");
             op0 = op1 = 0;
             switch (opcode) {
@@ -770,7 +1146,7 @@ again:
                 op0 |= op1;
                 break;
             case 3: /* XOR */
-                op0 |= op1;
+                op0 ^= op1;
                 break;
             case 4: /* AND */
                 op0 &= op1;
@@ -778,6 +1154,7 @@ again:
             case 5: /* SHR */
                 op1 = op0 & 1;
                 op0 = (op0 >> 1) | (s->carry << 7);
+                s->carry = op1;
                 break;
             case 6: /* ADD */
                 op0 += op1;
@@ -899,8 +1276,9 @@ again:
             n = (insn & 7);
             reg = (insn >> 16) & 0xff;
             if (insn & (1 << 24)) {
-                DPRINTF("Load reg 0x%x size %d addr 0x%08x\n", reg, n, addr);
                 cpu_physical_memory_read(addr, data, n);
+                DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n,
+                        addr, *(int *)data);
                 for (i = 0; i < n; i++) {
                     lsi_reg_writeb(s, reg + i, data[i]);
                 }
@@ -913,8 +1291,17 @@ again:
             }
         }
     }
-    /* ??? Need to avoid infinite loops.  */
-    if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) {
+    if (insn_processed > 10000 && !s->waiting) {
+        /* Some windows drivers make the device spin waiting for a memory
+           location to change.  If we have been executed a lot of code then
+           assume this is the case and force an unexpected device disconnect.
+           This is apparently sufficient to beat the drivers into submission.
+         */
+        if (!(s->sien0 & LSI_SIST0_UDC))
+            fprintf(stderr, "inf. loop with UDC masked\n");
+        lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0);
+        lsi_disconnect(s);
+    } else if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) {
         if (s->dcntl & LSI_DCNTL_SSM) {
             lsi_script_dma_interrupt(s, LSI_DSTAT_SSI);
         } else {
@@ -953,6 +1340,10 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
         return s->sdid;
     case 0x07: /* GPREG0 */
         return 0x7f;
+    case 0x08: /* Revision ID */
+        return 0x00;
+    case 0xa: /* SSID */
+        return s->ssid;
     case 0xb: /* SBCL */
         /* ??? This is not correct. However it's (hopefully) only
            used for diagnostics, so should be ok.  */
@@ -981,7 +1372,7 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
     case 0x19: /* CTEST1 */
         return 0;
     case 0x1a: /* CTEST2 */
-        tmp = LSI_CTEST2_DACK | LSI_CTEST2_CM;
+        tmp = s->ctest2 | LSI_CTEST2_DACK | LSI_CTEST2_CM;
         if (s->istat0 & LSI_ISTAT0_SIGP) {
             s->istat0 &= ~LSI_ISTAT0_SIGP;
             tmp |= LSI_CTEST2_SIGP;
@@ -996,6 +1387,8 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
         return s->ctest4;
     case 0x22: /* CTEST5 */
         return s->ctest5;
+    case 0x23: /* CTEST6 */
+         return 0;
     case 0x24: /* DBC[0:7] */
         return s->dbc & 0xff;
     case 0x25: /* DBC[8:15] */
@@ -1027,6 +1420,8 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
         s->sist1 = 0;
         lsi_update_irq(s);
         return tmp;
+    case 0x46: /* MACNTL */
+        return 0x0f;
     case 0x47: /* GPCNTL0 */
         return 0x0f;
     case 0x48: /* STIME0 */
@@ -1041,20 +1436,29 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
         return s->stest2;
     case 0x4f: /* STEST3 */
         return s->stest3;
+    case 0x50: /* SIDL */
+        /* This is needed by the linux drivers.  We currently only update it
+           during the MSG IN phase.  */
+        return s->sidl;
     case 0x52: /* STEST4 */
         return 0xe0;
     case 0x56: /* CCNTL0 */
         return s->ccntl0;
     case 0x57: /* CCNTL1 */
         return s->ccntl1;
-    case 0x58: case 0x59: /* SBDL */
+    case 0x58: /* SBDL */
+        /* Some drivers peek at the data bus during the MSG IN phase.  */
+        if ((s->sstat1 & PHASE_MASK) == PHASE_MI)
+            return s->msg[0];
+        return 0;
+    case 0x59: /* SBDL high */
         return 0;
     CASE_GET_REG32(mmrs, 0xa0)
     CASE_GET_REG32(mmws, 0xa4)
     CASE_GET_REG32(sfs, 0xa8)
     CASE_GET_REG32(drs, 0xac)
     CASE_GET_REG32(sbms, 0xb0)
-    CASE_GET_REG32(dmbs, 0xb4)
+    CASE_GET_REG32(dbms, 0xb4)
     CASE_GET_REG32(dnad64, 0xb8)
     CASE_GET_REG32(pmjad1, 0xc0)
     CASE_GET_REG32(pmjad2, 0xc4)
@@ -1108,7 +1512,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
         break;
     case 0x02: /* SCNTL2 */
         val &= ~(LSI_SCNTL2_WSR | LSI_SCNTL2_WSS);
-        s->scntl3 = val;
+        s->scntl2 = val;
         break;
     case 0x03: /* SCNTL3 */
         s->scntl3 = val;
@@ -1119,8 +1523,21 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
     case 0x05: /* SXFER */
         s->sxfer = val;
         break;
+    case 0x06: /* SDID */
+        if ((val & 0xf) != (s->ssid & 0xf))
+            BADF("Destination ID does not match SSID\n");
+        s->sdid = val & 0xf;
+        break;
     case 0x07: /* GPREG0 */
         break;
+    case 0x08: /* SFBR */
+        /* The CPU is not allowed to write to this register.  However the
+           SCRIPTS register move instructions are.  */
+        s->sfbr = val;
+        break;
+    case 0x0a: case 0x0b: 
+        /* Openserver writes to these readonly registers on startup */
+       return;    
     case 0x0c: case 0x0d: case 0x0e: case 0x0f:
         /* Linux writes to these readonly registers on startup.  */
         return;
@@ -1134,7 +1551,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
             s->istat0 &= ~LSI_ISTAT0_INTF;
             lsi_update_irq(s);
         }
-        if (s->waiting && val & LSI_ISTAT0_SIGP) {
+        if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) {
             DPRINTF("Woken by SIGP\n");
             s->waiting = 0;
             s->dsp = s->dnad;
@@ -1143,10 +1560,16 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
         if (val & LSI_ISTAT0_SRST) {
             lsi_soft_reset(s);
         }
+        break;
     case 0x16: /* MBOX0 */
         s->mbox0 = val;
+        break;
     case 0x17: /* MBOX1 */
         s->mbox1 = val;
+        break;
+    case 0x1a: /* CTEST2 */
+       s->ctest2 = val & LSI_CTEST2_PCICIE;
+       break;
     case 0x1b: /* CTEST3 */
         s->ctest3 = val & 0x0f;
         break;
@@ -1163,19 +1586,19 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
         }
         s->ctest5 = val;
         break;
-    case 0x2c: /* DSPS[0:7] */
+    case 0x2c: /* DSP[0:7] */
         s->dsp &= 0xffffff00;
         s->dsp |= val;
         break;
-    case 0x2d: /* DSPS[8:15] */
+    case 0x2d: /* DSP[8:15] */
         s->dsp &= 0xffff00ff;
         s->dsp |= val << 8;
         break;
-    case 0x2e: /* DSPS[16:23] */
+    case 0x2e: /* DSP[16:23] */
         s->dsp &= 0xff00ffff;
         s->dsp |= val << 16;
         break;
-    case 0x2f: /* DSPS[14:31] */
+    case 0x2f: /* DSP[24:31] */
         s->dsp &= 0x00ffffff;
         s->dsp |= val << 24;
         if ((s->dmode & LSI_DMODE_MAN) == 0
@@ -1252,7 +1675,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
     CASE_SET_REG32(sfs, 0xa8)
     CASE_SET_REG32(drs, 0xac)
     CASE_SET_REG32(sbms, 0xb0)
-    CASE_SET_REG32(dmbs, 0xb4)
+    CASE_SET_REG32(dbms, 0xb4)
     CASE_SET_REG32(dnad64, 0xb8)
     CASE_SET_REG32(pmjad1, 0xc0)
     CASE_SET_REG32(pmjad2, 0xc4)
@@ -1475,10 +1898,10 @@ static void lsi_io_writel(void *opaque, uint32_t addr, uint32_t val)
     lsi_reg_writeb(s, addr, val & 0xff);
     lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff);
     lsi_reg_writeb(s, addr + 2, (val >> 16) & 0xff);
-    lsi_reg_writeb(s, addr + 2, (val >> 24) & 0xff);
+    lsi_reg_writeb(s, addr + 3, (val >> 24) & 0xff);
 }
 
-static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num, 
+static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num,
                            uint32_t addr, uint32_t size, int type)
 {
     LSIState *s = (LSIState *)pci_dev;
@@ -1493,7 +1916,7 @@ static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num,
     register_ioport_read(addr, 256, 4, lsi_io_readl, s);
 }
 
-static void lsi_ram_mapfunc(PCIDevice *pci_dev, int region_num, 
+static void lsi_ram_mapfunc(PCIDevice *pci_dev, int region_num,
                             uint32_t addr, uint32_t size, int type)
 {
     LSIState *s = (LSIState *)pci_dev;
@@ -1503,7 +1926,7 @@ static void lsi_ram_mapfunc(PCIDevice *pci_dev, int region_num,
     cpu_register_physical_memory(addr + 0, 0x2000, s->ram_io_addr);
 }
 
-static void lsi_mmio_mapfunc(PCIDevice *pci_dev, int region_num, 
+static void lsi_mmio_mapfunc(PCIDevice *pci_dev, int region_num,
                              uint32_t addr, uint32_t size, int type)
 {
     LSIState *s = (LSIState *)pci_dev;
@@ -1528,10 +1951,12 @@ void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id)
     }
     if (s->scsi_dev[id]) {
         DPRINTF("Destroying device %d\n", id);
-        scsi_disk_destroy(s->scsi_dev[id]);
+        s->scsi_dev[id]->destroy(s->scsi_dev[id]);
     }
     DPRINTF("Attaching block device %d\n", id);
-    s->scsi_dev[id] = scsi_disk_init(bd, lsi_command_complete, s);
+    s->scsi_dev[id] = scsi_generic_init(bd, 1, lsi_command_complete, s);
+    if (s->scsi_dev[id] == NULL)
+        s->scsi_dev[id] = scsi_disk_init(bd, 1, lsi_command_complete, s);
 }
 
 void *lsi_scsi_init(PCIBus *bus, int devfn)
@@ -1545,12 +1970,21 @@ void *lsi_scsi_init(PCIBus *bus, int devfn)
         return NULL;
     }
 
+    /* PCI Vendor ID (word) */
     s->pci_dev.config[0x00] = 0x00;
     s->pci_dev.config[0x01] = 0x10;
+    /* PCI device ID (word) */
     s->pci_dev.config[0x02] = 0x12;
     s->pci_dev.config[0x03] = 0x00;
+    /* PCI base class code */
     s->pci_dev.config[0x0b] = 0x01;
-    s->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */
+    /* PCI subsystem ID */
+    s->pci_dev.config[0x2e] = 0x00;
+    s->pci_dev.config[0x2f] = 0x10;
+    /* PCI latency timer = 255 */
+    s->pci_dev.config[0x0d] = 0xff;
+    /* Interrupt pin 1 */
+    s->pci_dev.config[0x3d] = 0x01;
 
     s->mmio_io_addr = cpu_register_io_memory(0, lsi_mmio_readfn,
                                              lsi_mmio_writefn, s);
@@ -1563,9 +1997,11 @@ void *lsi_scsi_init(PCIBus *bus, int devfn)
                            PCI_ADDRESS_SPACE_MEM, lsi_mmio_mapfunc);
     pci_register_io_region((struct PCIDevice *)s, 2, 0x2000,
                            PCI_ADDRESS_SPACE_MEM, lsi_ram_mapfunc);
+    s->queue = qemu_malloc(sizeof(lsi_queue));
+    s->queue_len = 1;
+    s->active_commands = 0;
 
     lsi_soft_reset(s);
 
     return s;
 }
-