pl011: do not put into fifo before enabled the interruption
PATCH 1/1 pl011: do not put into fifo before enabled the interruption
If the user pressed some keys in the console during the guest booting, the console will be hanged after entering the shell. Because in the above case the pl011_can_receive will return 0 that the pl011_receive will not be called. That means no interruption will be injected in to the kernel and the pl011 state could not be driven further.
This patch fixed that issue by checking the interruption is enabled or not before putting into the fifo.
--- hw/char/pl011.c | 2 ++ 1 file changed, 2 insertions(+)
hw/char/pl011.c +2 -0
@@ -229,6 +229,8 @@ static int pl011_can_receive(void *opaque)PL011State *s = (PL011State *)opaque;int r; +if (!s->int_enabled) +return 0 ;if (s->lcr &0x10 ) { r = s->read_count <16 ; }else {
Signature · 1 line
2.11.0
Quoted 20 lines · Wei Xu
If the user pressed some keys in the console during the guest booting, the console will be hanged after entering the shell. Because in the above case the
pl011_can_receivewill return 0 that thepl011_receivewill not be called. That means no interruption will be injected in to the kernel and the pl011 state could not be driven further.This patch fixed that issue by checking the interruption is enabled or not before putting into the fifo.
Signed-off-by Wei Xu <[email protected]>--- hw/char/pl011.c | 2 ++ 1 file changed, 2 insertions(+)hw/char/pl011.c +0
-0
hw/char/pl011.c static int pl011_can_receive(void *opaque)
Quoted 9 lines
hw/char/pl011.c +2
-1@@ -229,6 +229,8 @@ static int pl011_can_receive(void *opaque)PL011State *s = (PL011State *)opaque;int r; +if (!s->int_enabled) +return 0 ;if (s->lcr &0x10 ) { r = s->read_count <16 ; }else { --
This doesn't look right. You should be able to use the PL011 in a strictly polling mode, without ever enabling interrupts. Returning false in can_receive if interrupts are disabled would break that.
If the user presses keys before interrupts are enabled, what ought to happen is:
* we put the key in the FIFO, and update the int_level flags
* when the FIFO is full, can_receive starts returning 0 and QEMU stops passing us new characters
* when the guest driver for the pl011 initializes the device and enables interrupts then either:
(a) it does something that clears the FIFO, which will mean can_receive starts allowing new chars again, or
(b) it leaves the FIFO as it is, and we should thus immediately raise an interrupt for the characters still in the FIFO; when the guest handles this interrupt and gets the characters, can_receive will permit new ones
What is happening in your situation that means this is not working as expected ?
thanks
-- PMM
Hi Peter,
Quoted 36 lines · Peter Maydell
Quoted 20 lines · Wei Xu
If the user pressed some keys in the console during the guest booting, the console will be hanged after entering the shell. Because in the above case the
pl011_can_receivewill return 0 that thepl011_receivewill not be called. That means no interruption will be injected in to the kernel and the pl011 state could not be driven further.This patch fixed that issue by checking the interruption is enabled or not before putting into the fifo.
Signed-off-by Wei Xu <[email protected]>--- hw/char/pl011.c | 2 ++ 1 file changed, 2 insertions(+)hw/char/pl011.c +0
-0hw/char/pl011.c static int pl011_can_receive(void *opaque)
Quoted 9 lines
hw/char/pl011.c +2
-1@@ -229,6 +229,8 @@ static int pl011_can_receive(void *opaque)PL011State *s = (PL011State *)opaque;int r; +if (!s->int_enabled) +return 0 ;if (s->lcr &0x10 ) { r = s->read_count <16 ; }else { --This doesn't look right. You should be able to use the PL011 in a strictly polling mode, without ever enabling interrupts. Returning false in
can_receiveif interrupts are disabled would break that.
Quoted 14 lines
If the user presses keys before interrupts are enabled, what ought to happen is:
* we put the key in the FIFO, and update theint_levelflags
* when the FIFO is full,can_receivestarts returning 0 and QEMU stops passing us new characters
* when the guest driver for the pl011 initializes the device and enables interrupts then either:
(a) it does something that clears the FIFO, which will meancan_receivestarts allowing new chars again, or
(b) it leaves the FIFO as it is, and we should thus immediately raise an interrupt for the characters still in the FIFO; when the guest handles this interrupt and gets the characters,can_receivewill permit new ones
Yes, now it is handled like b.
Quoted 2 lines
What is happening in your situation that means this is not working as expected ?
But in the kernel side, the pll011 is triggered as a level interruption. During the booting, if any key is pressed ,the call stack is as below:
QEMU side:pl011_update
-->qemu_set_irq(level as 0)
---->kvm_arm_gic_set_irq
Kernel side:kvm_vm_ioctl_irq_line
-->kvm_vgic_inject_irq
---->vgic_validate_injection (if level did not change, return) ---->vgic_queue_irq_unlock
Without above changes, in the vgic_validate_injection, because the interruption level is always 0, this irq will not be queued into vgic. And the guest will not read the pl011 fifo.
Best Regards,
Wei
Quoted 6 lines
thanks
-- PMM.
Quoted 34 lines · Wei Xu
Quoted 5 lines · Peter Maydell
If the user presses keys before interrupts are enabled, what ought to happen is:
* we put the key in the FIFO, and update theint_levelflags
* when the FIFO is full,can_receivestarts returning 0 and QEMU stops passing us new charactersQuoted 9 lines
* when the guest driver for the pl011 initializes the device and enables interrupts then either:
(a) it does something that clears the FIFO, which will meancan_receivestarts allowing new chars again, or
(b) it leaves the FIFO as it is, and we should thus immediately raise an interrupt for the characters still in the FIFO; when the guest handles this interrupt and gets the characters,can_receivewill permit new onesYes, now it is handled like b.
Quoted 2 lines
What is happening in your situation that means this is not working as expected ?
But in the kernel side, the pll011 is triggered as a level interruption. During the booting, if any key is pressed ,the call stack is as below:
QEMU side:pl011_update
-->qemu_set_irq(level as 0)
---->kvm_arm_gic_set_irqKernel side:
kvm_vm_ioctl_irq_line
-->kvm_vgic_inject_irq
---->vgic_validate_injection (if level did not change, return) ---->vgic_queue_irq_unlock
Quoted 3 lines
Without above changes, in the
vgic_validate_injection, because the interruption level is always 0, this irq will not be queued into vgic. And the guest will not read the pl011 fifo.
The pl011 code should call qemu_set_irq(..., 1) when the guest enables interrupts on the device by writing to the int_enabled (UARTIMSC) register. That will be a 0-to-1 level change and the KVM VGIC should report the interrupt to the guest.
thanks
-- PMM
Hi Peter,
Quoted 35 lines · Peter Maydell
Quoted 34 lines · Wei Xu
Quoted 5 lines · Peter Maydell
If the user presses keys before interrupts are enabled, what ought to happen is:
* we put the key in the FIFO, and update theint_levelflags
* when the FIFO is full,can_receivestarts returning 0 and QEMU stops passing us new charactersQuoted 9 lines
* when the guest driver for the pl011 initializes the device and enables interrupts then either:
(a) it does something that clears the FIFO, which will meancan_receivestarts allowing new chars again, or
(b) it leaves the FIFO as it is, and we should thus immediately raise an interrupt for the characters still in the FIFO; when the guest handles this interrupt and gets the characters,can_receivewill permit new onesYes, now it is handled like b.
Quoted 2 lines
What is happening in your situation that means this is not working as expected ?
But in the kernel side, the pll011 is triggered as a level interruption. During the booting, if any key is pressed ,the call stack is as below:
QEMU side:pl011_update
-->qemu_set_irq(level as 0)
---->kvm_arm_gic_set_irqKernel side:
kvm_vm_ioctl_irq_line
-->kvm_vgic_inject_irq
---->vgic_validate_injection (if level did not change, return) ---->vgic_queue_irq_unlock
Quoted 9 lines
Quoted 3 lines
Without above changes, in the
vgic_validate_injection, because the interruption level is always 0, this irq will not be queued into vgic. And the guest will not read the pl011 fifo.The pl011 code should call
qemu_set_irq(..., 1)when the guest enables interrupts on the device by writing to theint_enabled(UARTIMSC) register. That will be a 0-to-1 level change and the KVM VGIC should report the interrupt to the guest.
Yes.
And in the pl011_update, the irq level is set by s->int_level & s->int_enabled. When writing to the int_enabled, not sure why the int_level is set to 0x20(PL011_INT_TX) but int_enabled is 0x50.
It still call qemu_set_irq(..., 0).
I added "s->int_level |= PL011_INT_RX" before calling pl011_update when writing to the int_enabled and tested it also works. How do you think about like that?
Thanks!
Best Regards,
Wei
Quoted 5 lines
thanks
-- PMM.
Quoted 12 lines · Wei Xu
Quoted 5 lines · Peter Maydell
The pl011 code should call
qemu_set_irq(..., 1)when the guest enables interrupts on the device by writing to theint_enabled(UARTIMSC) register. That will be a 0-to-1 level change and the KVM VGIC should report the interrupt to the guest.Yes.
And in thepl011_update, the irq level is set bys->int_level&s->int_enabled. When writing to theint_enabled, not sure why theint_levelis set to 0x20(PL011_INT_TX) butint_enabledis0x50.
Quoted 4 lines
It still call
qemu_set_irq(..., 0).I added "
s->int_level|=PL011_INT_RX" before callingpl011_updatewhen writing to theint_enabledand tested it also works.
No, that's not right either. int_level should already have the RX bit set, because pl011_put_fifo() sets that bit when it gets a character from QEMU and puts it into the FIFO.
Does something else clear the int_level between the character going into the FIFO from QEMU and the guest enabling interrupts?
thanks
-- PMM
Quoted 18 lines · Peter Maydell
Quoted 12 lines · Wei Xu
Quoted 5 lines · Peter Maydell
The pl011 code should call
qemu_set_irq(..., 1)when the guest enables interrupts on the device by writing to theint_enabled(UARTIMSC) register. That will be a 0-to-1 level change and the KVM VGIC should report the interrupt to the guest.Yes.
And in thepl011_update, the irq level is set bys->int_level&s->int_enabled. When writing to theint_enabled, not sure why theint_levelis set to 0x20(PL011_INT_TX) butint_enabledis0x50.Quoted 4 lines
It still call
qemu_set_irq(..., 0).I added "
s->int_level|=PL011_INT_RX" before callingpl011_updatewhen writing to theint_enabledand tested it also works.
Quoted 7 lines
No, that's not right either.
int_levelshould already have the RX bit set, becausepl011_put_fifo()sets that bit when it gets a character from QEMU and puts it into the FIFO.Does something else clear the
int_levelbetween the character going into the FIFO from QEMU and the guest enabling interrupts?
As part of the boot process Linux restarts the UART a few times. When Linux drives the PL011 with the SBSA driver then the FIFO doesn't get reset prior to being used again, as the SBSA doesn't specify a way to do that. I'm not sure if this issue is due to the SBSA attempting to be overly simple, or something the Linux driver can deal with. See this thread for a discussion I started once.
https://www.spinics.net/lists/linux-serial/msg23163.html
Wei,
I assume you're using UEFI/ACPI when booting, as I don't recall this problem occurring with the Linux PL011 driver which would be used when booting with DT.
Thanks,
drew
Quoted 23 lines · Andrew Jones
Quoted 22 lines · Peter Maydell
Quoted 12 lines · Wei Xu
Quoted 5 lines · Peter Maydell
The pl011 code should call
qemu_set_irq(..., 1)when the guest enables interrupts on the device by writing to theint_enabled(UARTIMSC) register. That will be a 0-to-1 level change and the KVM VGIC should report the interrupt to the guest.Yes.
And in thepl011_update, the irq level is set bys->int_level&s->int_enabled. When writing to theint_enabled, not sure why theint_levelis set to 0x20(PL011_INT_TX) butint_enabledis0x50.Quoted 4 lines
It still call
qemu_set_irq(..., 0).I added "
s->int_level|=PL011_INT_RX" before callingpl011_updatewhen writing to theint_enabledand tested it also works.No, that's not right either.
int_levelshould already have the RX bit set, becausepl011_put_fifo()sets that bit when it gets a character from QEMU and puts it into the FIFO.
Quoted 8 lines
Quoted 3 lines
Does something else clear the
int_levelbetween the character going into the FIFO from QEMU and the guest enabling interrupts?As part of the boot process Linux restarts the UART a few times. When Linux drives the PL011 with the SBSA driver then the FIFO doesn't get reset prior to being used again, as the SBSA doesn't specify a way to do that.
Right, but the FIFO not being cleared shouldn't be a problem -- if the FIFO is still full then the int_level should still indicate that so that when the Linux driver enables interrupts it takes an interrupt (and the handler should then drain the FIFO by reading characters from it).
It seems likely that there's a bug in QEMU's pl011 model (this doesn't happen with real hardware PL011s, I assume) -- but we need to find out what the divergence from the hardware is, rather than making changes which happen to fix the symptoms without having first nailed down what the underlying cause is.
thanks
-- PMM
Hi Andrew,
Quoted 27 lines · Andrew Jones
Quoted 18 lines · Peter Maydell
Quoted 12 lines · Wei Xu
Quoted 5 lines · Peter Maydell
The pl011 code should call
qemu_set_irq(..., 1)when the guest enables interrupts on the device by writing to theint_enabled(UARTIMSC) register. That will be a 0-to-1 level change and the KVM VGIC should report the interrupt to the guest.Yes.
And in thepl011_update, the irq level is set bys->int_level&s->int_enabled. When writing to theint_enabled, not sure why theint_levelis set to 0x20(PL011_INT_TX) butint_enabledis0x50.Quoted 4 lines
It still call
qemu_set_irq(..., 0).I added "
s->int_level|=PL011_INT_RX" before callingpl011_updatewhen writing to theint_enabledand tested it also works.Quoted 7 lines
No, that's not right either.
int_levelshould already have the RX bit set, becausepl011_put_fifo()sets that bit when it gets a character from QEMU and puts it into the FIFO.Does something else clear the
int_levelbetween the character going into the FIFO from QEMU and the guest enabling interrupts?
Quoted 8 lines
As part of the boot process Linux restarts the UART a few times. When Linux drives the PL011 with the SBSA driver then the FIFO doesn't get reset prior to being used again, as the SBSA doesn't specify a way to do that. I'm not sure if this issue is due to the SBSA attempting to be overly simple, or something the Linux driver can deal with. See this thread for a discussion I started once.
I am not sure it is the same problem or not.
I will check that.
Thanks!
Quoted 7 lines
Wei,
I assume you're using UEFI/ACPI when booting, as I don't recall this problem occurring with the Linux PL011 driver which would be used when booting with DT.
I am using an ARM64 board, the guest is booted *without* UEFI but the host is booted with UEFI/ACPI.
The command I am using is as below:
"qemu-system-aarch64 -enable-kvm -m 1024 -cpu host -M virt \ -nographic --kernel Image --initrd roofs.cpio.gz"
Thanks!
Best Regards,
Wei
Quoted 5 lines
Thanks,
drew.
Quoted 51 lines · Wei Xu
Hi Andrew,
Quoted 27 lines · Andrew Jones
Quoted 18 lines · Peter Maydell
Quoted 12 lines · Wei Xu
Quoted 5 lines · Peter Maydell
The pl011 code should call
qemu_set_irq(..., 1)when the guest enables interrupts on the device by writing to theint_enabled(UARTIMSC) register. That will be a 0-to-1 level change and the KVM VGIC should report the interrupt to the guest.Yes.
And in thepl011_update, the irq level is set bys->int_level&s->int_enabled. When writing to theint_enabled, not sure why theint_levelis set to 0x20(PL011_INT_TX) butint_enabledis0x50.Quoted 4 lines
It still call
qemu_set_irq(..., 0).I added "
s->int_level|=PL011_INT_RX" before callingpl011_updatewhen writing to theint_enabledand tested it also works.Quoted 7 lines
No, that's not right either.
int_levelshould already have the RX bit set, becausepl011_put_fifo()sets that bit when it gets a character from QEMU and puts it into the FIFO.Does something else clear the
int_levelbetween the character going into the FIFO from QEMU and the guest enabling interrupts?Quoted 8 lines
As part of the boot process Linux restarts the UART a few times. When Linux drives the PL011 with the SBSA driver then the FIFO doesn't get reset prior to being used again, as the SBSA doesn't specify a way to do that. I'm not sure if this issue is due to the SBSA attempting to be overly simple, or something the Linux driver can deal with. See this thread for a discussion I started once.
I am not sure it is the same problem or not.
I will check that.
Thanks!Quoted 7 lines
Wei,
I assume you're using UEFI/ACPI when booting, as I don't recall this problem occurring with the Linux PL011 driver which would be used when booting with DT.
Quoted 5 lines
I am using an ARM64 board, the guest is booted *without* UEFI but the host is booted with UEFI/ACPI.
The command I am using is as below:"qemu-system-aarch64 -enable-kvm -m 1024 -cpu host -M virt \ -nographic --kernel Image --initrd roofs.cpio.gz"
Hmm. This means you're booting the guest with DT, which means the Linux driver is the PL011 driver, not the SBSA driver. I didn't recall that driver having the issue, but maybe something changed.
Thanks,
drew
Quoted 13 lines
Thanks!
Best Regards,
WeiQuoted 5 lines
Thanks,
drew.
Hi Peter,
Quoted 18 lines · Peter Maydell
Quoted 12 lines · Wei Xu
Quoted 5 lines · Peter Maydell
The pl011 code should call
qemu_set_irq(..., 1)when the guest enables interrupts on the device by writing to theint_enabled(UARTIMSC) register. That will be a 0-to-1 level change and the KVM VGIC should report the interrupt to the guest.Yes.
And in thepl011_update, the irq level is set bys->int_level&s->int_enabled. When writing to theint_enabled, not sure why theint_levelis set to 0x20(PL011_INT_TX) butint_enabledis0x50.Quoted 4 lines
It still call
qemu_set_irq(..., 0).I added "
s->int_level|=PL011_INT_RX" before callingpl011_updatewhen writing to theint_enabledand tested it also works.
Quoted 7 lines
No, that's not right either.
int_levelshould already have the RX bit set, becausepl011_put_fifo()sets that bit when it gets a character from QEMU and puts it into the FIFO.Does something else clear the
int_levelbetween the character going into the FIFO from QEMU and the guest enabling interrupts?
Yes. When the guest enabling the interrupts, the pl011 driver in the kernel will clear the RX interrupts[1].
And pasted the code below to make it easy to read.
static void pl011_enable_interrupts(struct uart_amba_port *uap)
{
spin_lock_irq(&uap->port.lock);
/* Clear out any spuriously appearing RX interrupts */
pl011_write(UART011_RTIS | UART011_RXIS, uap, REG_ICR);
uap->im = UART011_RTIM;
if (!pl011_dma_rx_running(uap))
uap->im |= UART011_RXIM;
pl011_write(uap->im, uap, REG_IMSC);
spin_unlock_irq(&uap->port.lock);
}I tried kept the RXIS in the kernel side to test and found the issue is still there. A little confused now :(
[1]: https://elixir.free-electrons.com/linux/latest/source/drivers/tty/serial/amba-pl011.c#L1732
Best Regards,
Wei
Quoted 6 lines
thanks
-- PMM.
Quoted 13 lines · Wei Xu
Quoted 7 lines · Peter Maydell
No, that's not right either.
int_levelshould already have the RX bit set, becausepl011_put_fifo()sets that bit when it gets a character from QEMU and puts it into the FIFO.Does something else clear the
int_levelbetween the character going into the FIFO from QEMU and the guest enabling interrupts?Yes. When the guest enabling the interrupts, the pl011 driver in the kernel will clear the RX interrupts[1].
And pasted the code below to make it easy to read.
Quoted 13 lines
static void pl011_enable_interrupts(struct uart_amba_port *uap)
{
spin_lock_irq(&uap->port.lock);
/* Clear out any spuriously appearing RX interrupts */
pl011_write(UART011_RTIS | UART011_RXIS, uap, REG_ICR);
uap->im = UART011_RTIM;
if (!pl011_dma_rx_running(uap))
uap->im |= UART011_RXIM;
pl011_write(uap->im, uap, REG_IMSC);
spin_unlock_irq(&uap->port.lock);
}This seems at first glance like a kernel driver bug to me -- if it is explicitly killing off the pending interrupt without handling it then it's naturally going to get stuck if characters come in in the window before it does that.
(It wouldn't surprise me if there's something in the driver init sequence that has the effect of clearing the FIFO on real hardware but not QEMU, which would mean that on real h/w the window where this can happen would be small, but it's still there.)
thanks
-- PMM
This series seems to have some coding style problems. See output below for more information:
Hi,
This series seems to have some coding style problems. See output below for more information:
Type: series
Message-id: [email protected]
Subject: [Qemu-devel] [Qemu-arm] [PATCH] pl011: do not put into fifo before enabled the interruption
=== TEST SCRIPT BEGIN ===
#!/bin/bash
BASE=base
n=1
total=$(git log --oneline $BASE.. | wc -l)
failed=0
git config --local diff.renamelimit 0
git config --local diff.renames True
commits="$(git log --format=%H --reverse $BASE..)" for c in $commits; do
echo "Checking PATCH $n/$total: $(git log -n 1 --format=%s $c)..."
if ! git show $c --format=email | ./scripts/checkpatch.pl --mailback -; then
failed=1
echo
fi
n=$((n+1))done
exit $failed
=== TEST SCRIPT END ===
Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
* [new tag] patchew/[email protected] -> patchew/[email protected]
Switched to a new branch 'test'aac404070b pl011: do not put into fifo before enabled the interruption
=== OUTPUT BEGIN ===
Checking PATCH 1/1: pl011: do not put into fifo before enabled the interruption...
ERROR: braces {} are necessary for all arms of this statement
#27: FILE: hw/char/pl011.c:232:
+ if (!s->int_enabled)
[...]
ERROR: code indent should never use tabs
#28: FILE: hw/char/pl011.c:233:
+^Ireturn 0;$
total: 2 errors, 0 warnings, 8 lines checked
Your patch has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS.
=== OUTPUT END ===
Test command exited with code: 1
---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to [email protected]