Before when NetBSD was booting it hanged in the device initialization
due to memory corruption, check below for error:

[   1.0000000] lpt1 at gsc0 hpa 0xf0102000 path 2/0/6 irq 7
SeaBIOS: Start PDC32 proc PDC_SOFT_POWER(23) option 1 result=0xfcc600 ARG3=0x1  
ARG4=0xa63790 ARG5=0x1004280 ARG6=0x73cf48 ARG7=0x0
[   1.0000080] scsibus0: waiting 2 seconds for devices to settle...
(hanged here).

Fixed applied in this patch:
- Fixed use after free in ncr710_command_complete() done by clearning
  hba_private.
- Added null checks in ncr710_request_free() &
  ncr710_request_cancelled().
- Corrected scripts endianess handling
- Improved device selection and lun scanning
- Fix interrupt and register handling

Finally NetBSD now successfully boots with NCR710 SCSI Controller.

Reported-by: Helge Deller<[email protected]>
Signed-off-by: Soumyajyotii Ssarkar<[email protected]>
---
 hw/scsi/ncr53c710.c | 94 +++++++++++++++++++++++++++++++++------------
 1 file changed, 70 insertions(+), 24 deletions(-)

diff --git a/hw/scsi/ncr53c710.c b/hw/scsi/ncr53c710.c
index 47a6983491..808b4ebd53 100644
--- a/hw/scsi/ncr53c710.c
+++ b/hw/scsi/ncr53c710.c
@@ -593,7 +593,7 @@ static void ncr710_update_irq(NCR710State *s)
 {
     int level = 0;
 
-    if (s->dstat) {
+    if (s->dstat & ~NCR710_DSTAT_DFE) {
         if (s->dstat & s->dien) {
             level = 1;
         }
@@ -718,7 +718,9 @@ static void ncr710_do_dma(NCR710State *s, int out)
     if (s->current->dma_len == 0) {
         s->current->dma_buf = NULL;
         s->current->pending = 0;
+        s->waiting = NCR710_WAIT_DMA;
         scsi_req_continue(s->current->req);
+        return;
     } else {
         s->current->dma_buf += count;
         s->waiting = NCR710_WAIT_NONE;
@@ -737,6 +739,12 @@ static void ncr710_add_msg_byte(NCR710State *s, uint8_t 
data)
 
 static void ncr710_request_free(NCR710State *s, NCR710Request *p)
 {
+    if (!p) {
+        return;
+    }
+    if (p->req && p->req->hba_private == p) {
+        p->req->hba_private = NULL;
+    }
     if (p == s->current) {
         s->current = NULL;
     }
@@ -747,8 +755,11 @@ void ncr710_request_cancelled(SCSIRequest *req)
 {
     NCR710State *s = ncr710_from_scsi_bus(req->bus);
     NCR710Request *p = (NCR710Request *)req->hba_private;
-    req->hba_private = NULL;
-    ncr710_request_free(s, p);
+    if (p) {
+        req->hba_private = NULL;
+        p->req = NULL;
+        ncr710_request_free(s, p);
+    }
     scsi_req_unref(req);
 }
 
@@ -789,7 +800,14 @@ void ncr710_command_complete(SCSIRequest *req, size_t 
resid)
 
     ncr710_set_phase(s, PHASE_ST);
 
-    if (req->hba_private == s->current) {
+    if (p) {
+        if (p == s->current) {
+            req->hba_private = NULL;
+            p->req = NULL;
+        } else {
+            req->hba_private = NULL;
+            ncr710_request_free(s, p);
+        }
         scsi_req_unref(req);
     }
 
@@ -980,6 +998,7 @@ static void ncr710_do_status(NCR710State *s)
     ncr710_set_phase(s, PHASE_MI);
     s->msg_action = NCR710_MSG_ACTION_DISCONNECT;
     ncr710_add_msg_byte(s, 0); /* COMMAND COMPLETE */
+    s->command_complete = NCR710_CMD_COMPLETE;
 }
 
 static void ncr710_do_msgin(NCR710State *s)
@@ -1024,7 +1043,7 @@ static void ncr710_do_msgin(NCR710State *s)
         ncr710_set_phase(s, PHASE_CO);
         break;
     case NCR710_MSG_ACTION_DISCONNECT:
-        ncr710_disconnect(s);
+        s->sstat2 &= ~PHASE_MASK;
         break;
     case NCR710_MSG_ACTION_DATA_OUT:
         ncr710_set_phase(s, PHASE_DO);
@@ -1338,8 +1357,8 @@ again:
             offset = sextract32(addr, 0, 24);
             ncr710_dma_read(s, s->dsa + offset, buf, 8);
             /* byte count is stored in bits 0:23 only */
-            s->dbc = cpu_to_le32(buf[0]) & 0xffffff;
-            addr = cpu_to_le32(buf[1]);
+            s->dbc = be32_to_cpu(buf[0]) & 0xffffff;
+            addr = be32_to_cpu(buf[1]);
         }
         /* Check phase match for block move instructions */
         if ((s->sstat2 & PHASE_MASK) != ((insn >> 24) & 7)) {
@@ -1404,8 +1423,29 @@ again:
                         s->dsp = s->dnad;
                         break;
                     }
-                } else if (!scsi_device_find(&s->bus, 0, idbitstonum(id), 0)) {
+                }
+                bool device_exists = false;
+                if (insn & (1 << 24)) {
+                    for (int lun = 0; lun < 8; lun++) {
+                        SCSIDevice *dev = scsi_device_find(&s->bus, 0,
+                                                           idbitstonum(id),
+                                                           lun);
+                        if (dev) {
+                            device_exists = true;
+                            break;
+                        }
+                    }
+                } else {
+                    SCSIDevice *dev = scsi_device_find(&s->bus, 0,
+                                                       idbitstonum(id), 0);
+                    device_exists = dev != NULL;
+                }
+
+                if (!device_exists) {
                     ncr710_bad_selection(s, id);
+                    if (!(insn & (1 << 24)) && addr != 0) {
+                        s->dsp = addr;
+                    }
                     break;
                 } else {
                     /*
@@ -1425,13 +1465,10 @@ again:
                 }
                 break;
             case 1: /* Disconnect */
-
                 if (s->command_complete != NCR710_CMD_PENDING) {
                     s->scntl1 &= ~NCR710_SCNTL1_CON;
                     s->istat &= ~NCR710_ISTAT_CON;
-                    if (s->waiting == NCR710_WAIT_RESELECT) {
-                        s->waiting = NCR710_WAIT_NONE;
-                    }
+                    s->waiting = NCR710_WAIT_NONE;
                 } else {
                     if (s->current) {
                         s->current->resume_offset = s->dsp;
@@ -1770,7 +1807,6 @@ static uint8_t ncr710_reg_readb(NCR710State *s, int 
offset)
         }
         s->dstat = 0;  /* Clear all DMA interrupt status bits */
         s->dstat |= NCR710_DSTAT_DFE;
-        s->istat &= ~NCR710_ISTAT_DIP;
         ncr710_update_irq(s);
 
         if (s->waiting == NCR710_WAIT_RESELECT && s->current &&
@@ -1796,7 +1832,7 @@ static uint8_t ncr710_reg_readb(NCR710State *s, int 
offset)
         return ret;
     case NCR710_SSTAT0_REG: /* SSTAT0 */
         ret = s->sstat0;
-        if (s->sstat0 != 0 && !(s->sstat0 & NCR710_SSTAT0_STO)) {
+        if (s->sstat0 != 0) {
             s->sstat0 = 0;
             s->istat &= ~NCR710_ISTAT_SIP;
             ncr710_update_irq(s);
@@ -1809,14 +1845,7 @@ static uint8_t ncr710_reg_readb(NCR710State *s, int 
offset)
         ret = s->sstat0;
         break;
     case NCR710_SSTAT2_REG: /* SSTAT2 */
-        ret = s->dstat;
-
-        if (s->dstat & NCR710_DSTAT_SIR) {
-            /* SIR bit processing */
-        }
-        s->dstat = 0;
-        s->istat &= ~NCR710_ISTAT_DIP;
-        ncr710_update_irq(s);
+        ret = s->sstat2;
         break;
         CASE_GET_REG32(dsa, NCR710_DSA_REG)
         break;
@@ -1887,7 +1916,6 @@ static uint8_t ncr710_reg_readb(NCR710State *s, int 
offset)
         if (s->dsps == GOOD_STATUS_AFTER_STATUS &&
             (s->dstat & NCR710_DSTAT_SIR)) {
             s->dstat &= ~NCR710_DSTAT_SIR;
-            s->istat &= ~NCR710_ISTAT_DIP;
             ncr710_update_irq(s);
         }
         break;
@@ -2049,7 +2077,21 @@ static void ncr710_reg_writeb(NCR710State *s, int 
offset, uint8_t val)
         /* Linux writes to these readonly registers on startup */
         return;
 
-    CASE_SET_REG32(dsa, NCR710_DSA_REG)
+    case NCR710_DSA_REG:
+        s->dsa &= 0xffffff00;
+        s->dsa |= val;
+        break;
+    case NCR710_DSA_REG + 1:
+        s->dsa &= 0xffff00ff;
+        s->dsa |= val << 8;
+        break;
+    case NCR710_DSA_REG + 2:
+        s->dsa &= 0xff00ffff;
+        s->dsa |= val << 16;
+        break;
+    case NCR710_DSA_REG + 3:
+        s->dsa &= 0x00ffffff;
+        s->dsa |= val << 24;
         break;
 
     case NCR710_CTEST0_REG: /* CTEST0 */
@@ -2118,7 +2160,11 @@ static void ncr710_reg_writeb(NCR710State *s, int 
offset, uint8_t val)
         if (val & 0x04) {
             ncr710_scsi_fifo_init(&s->scsi_fifo);
             s->dstat |= NCR710_DSTAT_DFE;
+            s->ctest1 = 0xFF; /* FMT - FIFO empty, all status bits set */
+        } else if (s->ctest8 & 0x04) {
+            s->ctest1 = 0x00;
         }
+        s->ctest8 = val;
         break;
     case NCR710_LCRC_REG: /* LCRC */
         s->lcrc = val;
-- 
2.49.0


Reply via email to