hw/scsi/ncr53c710.c | 94 +++++++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 24 deletions(-)
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<deller@gmx.de>
Signed-off-by: Soumyajyotii Ssarkar<soumyajyotisarkar23@gmail.com>
---
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
© 2016 - 2026 Red Hat, Inc.