Message ID | 20240302214453.2071388-1-svens@stackframe.org |
---|---|
State | New |
Headers | show |
Series | hw/scsi/lsi53c895a: stop script on phase mismatch | expand |
On 3/2/24 22:44, Sven Schnelle wrote: > Netbsd isn't happy with qemu lsi53c895a emulation: > > cd0(esiop0:0:2:0): command with tag id 0 reset > esiop0: autoconfiguration error: phase mismatch without command > esiop0: autoconfiguration error: unhandled scsi interrupt, sist=0x80 sstat1=0x0 DSA=0x23a64b1 DSP=0x50 > > This is because lsi_bad_phase() triggers a phase mismatch, which > stops SCRIPT processing. However, after returning to > lsi_command_complete(), SCRIPT is restarted with lsi_resume_script(). > Fix this by adding a return value to lsi_bad_phase(), and only resume > script processing when lsi_bad_phase() didn't trigger a host interrupt. > > Signed-off-by: Sven Schnelle <svens@stackframe.org> Tested-by: Helge Deller <deller@gmx.de> Helge > --- > hw/scsi/lsi53c895a.c | 16 ++++++++++++---- > 1 file changed, 12 insertions(+), 4 deletions(-) > > diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c > index 4ff9470381..59b88aff3f 100644 > --- a/hw/scsi/lsi53c895a.c > +++ b/hw/scsi/lsi53c895a.c > @@ -573,8 +573,9 @@ static inline void lsi_set_phase(LSIState *s, int phase) > s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase; > } > > -static void lsi_bad_phase(LSIState *s, int out, int new_phase) > +static int lsi_bad_phase(LSIState *s, int out, int new_phase) > { > + int ret = 0; > /* Trigger a phase mismatch. */ > if (s->ccntl0 & LSI_CCNTL0_ENPMJ) { > if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) { > @@ -587,8 +588,10 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase) > trace_lsi_bad_phase_interrupt(); > lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); > lsi_stop_script(s); > + ret = 1; > } > lsi_set_phase(s, new_phase); > + return ret; > } > > > @@ -792,7 +795,7 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) > static void lsi_command_complete(SCSIRequest *req, size_t resid) > { > LSIState *s = LSI53C895A(req->bus->qbus.parent); > - int out; > + int out, stop = 0; > > out = (s->sstat1 & PHASE_MASK) == PHASE_DO; > trace_lsi_command_complete(req->status); > @@ -800,7 +803,10 @@ static void lsi_command_complete(SCSIRequest *req, size_t resid) > s->command_complete = 2; > if (s->waiting && s->dbc != 0) { > /* Raise phase mismatch for short transfers. */ > - lsi_bad_phase(s, out, PHASE_ST); > + stop = lsi_bad_phase(s, out, PHASE_ST); > + if (stop) { > + s->waiting = 0; > + } > } else { > lsi_set_phase(s, PHASE_ST); > } > @@ -810,7 +816,9 @@ static void lsi_command_complete(SCSIRequest *req, size_t resid) > lsi_request_free(s, s->current); > scsi_req_unref(req); > } > - lsi_resume_script(s); > + if (!stop) { > + lsi_resume_script(s); > + } > } > > /* Callback to indicate that the SCSI layer has completed a transfer. */
Queued, thanks. Paolo
diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 4ff9470381..59b88aff3f 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -573,8 +573,9 @@ static inline void lsi_set_phase(LSIState *s, int phase) s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase; } -static void lsi_bad_phase(LSIState *s, int out, int new_phase) +static int lsi_bad_phase(LSIState *s, int out, int new_phase) { + int ret = 0; /* Trigger a phase mismatch. */ if (s->ccntl0 & LSI_CCNTL0_ENPMJ) { if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) { @@ -587,8 +588,10 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase) trace_lsi_bad_phase_interrupt(); lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); lsi_stop_script(s); + ret = 1; } lsi_set_phase(s, new_phase); + return ret; } @@ -792,7 +795,7 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) static void lsi_command_complete(SCSIRequest *req, size_t resid) { LSIState *s = LSI53C895A(req->bus->qbus.parent); - int out; + int out, stop = 0; out = (s->sstat1 & PHASE_MASK) == PHASE_DO; trace_lsi_command_complete(req->status); @@ -800,7 +803,10 @@ static void lsi_command_complete(SCSIRequest *req, size_t resid) s->command_complete = 2; if (s->waiting && s->dbc != 0) { /* Raise phase mismatch for short transfers. */ - lsi_bad_phase(s, out, PHASE_ST); + stop = lsi_bad_phase(s, out, PHASE_ST); + if (stop) { + s->waiting = 0; + } } else { lsi_set_phase(s, PHASE_ST); } @@ -810,7 +816,9 @@ static void lsi_command_complete(SCSIRequest *req, size_t resid) lsi_request_free(s, s->current); scsi_req_unref(req); } - lsi_resume_script(s); + if (!stop) { + lsi_resume_script(s); + } } /* Callback to indicate that the SCSI layer has completed a transfer. */
Netbsd isn't happy with qemu lsi53c895a emulation: cd0(esiop0:0:2:0): command with tag id 0 reset esiop0: autoconfiguration error: phase mismatch without command esiop0: autoconfiguration error: unhandled scsi interrupt, sist=0x80 sstat1=0x0 DSA=0x23a64b1 DSP=0x50 This is because lsi_bad_phase() triggers a phase mismatch, which stops SCRIPT processing. However, after returning to lsi_command_complete(), SCRIPT is restarted with lsi_resume_script(). Fix this by adding a return value to lsi_bad_phase(), and only resume script processing when lsi_bad_phase() didn't trigger a host interrupt. Signed-off-by: Sven Schnelle <svens@stackframe.org> --- hw/scsi/lsi53c895a.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)