[PATCH] hw/scsi/lsi53c895a: stop script on phase mismatch

Sven Schnelle posted 1 patch 8 months, 4 weeks ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20240302214453.2071388-1-svens@stackframe.org
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Fam Zheng <fam@euphon.net>
hw/scsi/lsi53c895a.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
[PATCH] hw/scsi/lsi53c895a: stop script on phase mismatch
Posted by Sven Schnelle 8 months, 4 weeks ago
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(-)

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.  */
-- 
2.43.2
Re: [PATCH] hw/scsi/lsi53c895a: stop script on phase mismatch
Posted by Paolo Bonzini 8 months, 3 weeks ago
Queued, thanks.

Paolo
Re: [PATCH] hw/scsi/lsi53c895a: stop script on phase mismatch
Posted by Helge Deller 8 months, 4 weeks ago
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.  */