[PATCH for-6.2 2/2] escc: update the R_SPEC register SPEC_ALLSENT bit when writing to W_TXCTRL1

Mark Cave-Ayland posted 2 patches 4 years, 2 months ago
Maintainers: "Marc-André Lureau" <marcandre.lureau@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>
[PATCH for-6.2 2/2] escc: update the R_SPEC register SPEC_ALLSENT bit when writing to W_TXCTRL1
Posted by Mark Cave-Ayland 4 years, 2 months ago
The ESCC datasheet states that SPEC_ALLSENT is always set in sync mode and set
in async mode once all characters have cleared the transmitter. Since writes to
SERIAL_DATA use a synchronous chardev API, the guest can never see the state when
transmission is in progress so it is possible to set SPEC_ALLSENT in the
R_SPEC register unconditionally.

This fixes a hang when using the Sun PROM as it attempts to enumerate the
onboard serial devices, and a similar hang in OpenBSD SPARC32 where in both cases
the boot process will not proceed until SPEC_ALLSENT has been set after writing
to W_TXCTRL1.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
---
 hw/char/escc.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/hw/char/escc.c b/hw/char/escc.c
index a7d9050c83..8755d8d34f 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -586,6 +586,20 @@ static void escc_mem_write(void *opaque, hwaddr addr,
             s->wregs[s->reg] = val;
             break;
         case W_TXCTRL1:
+            s->wregs[s->reg] = val;
+            /*
+             * The ESCC datasheet states that SPEC_ALLSENT is always set in
+             * sync mode, and set in async mode when all characters have
+             * cleared the transmitter. Since writes to SERIAL_DATA use the
+             * blocking qemu_chr_fe_write_all() function to write each
+             * character, the guest can never see the state when async data
+             * is in the process of being transmitted so we can set this bit
+             * unconditionally regardless of the state of the W_TXCTRL1 mode
+             * bits.
+             */
+            s->rregs[R_SPEC] |= SPEC_ALLSENT;
+            escc_update_parameters(s);
+            break;
         case W_TXCTRL2:
             s->wregs[s->reg] = val;
             escc_update_parameters(s);
-- 
2.20.1


Re: [PATCH for-6.2 2/2] escc: update the R_SPEC register SPEC_ALLSENT bit when writing to W_TXCTRL1
Posted by Philippe Mathieu-Daudé 4 years, 2 months ago
On 11/18/21 19:18, Mark Cave-Ayland wrote:
> The ESCC datasheet states that SPEC_ALLSENT is always set in sync mode and set
> in async mode once all characters have cleared the transmitter. Since writes to
> SERIAL_DATA use a synchronous chardev API, the guest can never see the state when
> transmission is in progress so it is possible to set SPEC_ALLSENT in the
> R_SPEC register unconditionally.
> 
> This fixes a hang when using the Sun PROM as it attempts to enumerate the
> onboard serial devices, and a similar hang in OpenBSD SPARC32 where in both cases
> the boot process will not proceed until SPEC_ALLSENT has been set after writing
> to W_TXCTRL1.
> 
> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
> ---
>  hw/char/escc.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>