mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
hw/arm/virt: Don't incorrectly claim architectural timer to be edge-triggered
The architectural timers in ARM CPUs all have level triggered interrupts (unless you're using KVM on a host kernel before 4.4, which misimplemented them as edge-triggered). We were incorrectly describing them in the device tree as edge triggered. This can cause problems for guest kernels in 4.8 before rc6: * pre-4.8 kernels ignore the values in the DT * 4.8 before rc6 write the DT values to the GIC config registers * newer than rc6 ignore the DT and insist that the timer interrupts are level triggered regardless Fix the DT so we're describing reality. For backwards-compatibility purposes, only do this for the virt-2.9 machine onward. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Andrew Jones <drjones@redhat.com>
This commit is contained in:
parent
c8ef2bda05
commit
156bc9a5ea
1 changed files with 30 additions and 4 deletions
|
@ -71,6 +71,7 @@ typedef struct {
|
||||||
bool disallow_affinity_adjustment;
|
bool disallow_affinity_adjustment;
|
||||||
bool no_its;
|
bool no_its;
|
||||||
bool no_pmu;
|
bool no_pmu;
|
||||||
|
bool claim_edge_triggered_timers;
|
||||||
} VirtMachineClass;
|
} VirtMachineClass;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -309,12 +310,31 @@ static void fdt_add_psci_node(const VirtMachineState *vms)
|
||||||
|
|
||||||
static void fdt_add_timer_nodes(const VirtMachineState *vms, int gictype)
|
static void fdt_add_timer_nodes(const VirtMachineState *vms, int gictype)
|
||||||
{
|
{
|
||||||
/* Note that on A15 h/w these interrupts are level-triggered,
|
/* On real hardware these interrupts are level-triggered.
|
||||||
* but for the GIC implementation provided by both QEMU and KVM
|
* On KVM they were edge-triggered before host kernel version 4.4,
|
||||||
* they are edge-triggered.
|
* and level-triggered afterwards.
|
||||||
|
* On emulated QEMU they are level-triggered.
|
||||||
|
*
|
||||||
|
* Getting the DTB info about them wrong is awkward for some
|
||||||
|
* guest kernels:
|
||||||
|
* pre-4.8 ignore the DT and leave the interrupt configured
|
||||||
|
* with whatever the GIC reset value (or the bootloader) left it at
|
||||||
|
* 4.8 before rc6 honour the incorrect data by programming it back
|
||||||
|
* into the GIC, causing problems
|
||||||
|
* 4.8rc6 and later ignore the DT and always write "level triggered"
|
||||||
|
* into the GIC
|
||||||
|
*
|
||||||
|
* For backwards-compatibility, virt-2.8 and earlier will continue
|
||||||
|
* to say these are edge-triggered, but later machines will report
|
||||||
|
* the correct information.
|
||||||
*/
|
*/
|
||||||
ARMCPU *armcpu;
|
ARMCPU *armcpu;
|
||||||
uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI;
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
||||||
|
uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI;
|
||||||
|
|
||||||
|
if (vmc->claim_edge_triggered_timers) {
|
||||||
|
irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI;
|
||||||
|
}
|
||||||
|
|
||||||
if (gictype == 2) {
|
if (gictype == 2) {
|
||||||
irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
|
irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
|
||||||
|
@ -1556,8 +1576,14 @@ static void virt_2_8_instance_init(Object *obj)
|
||||||
|
|
||||||
static void virt_machine_2_8_options(MachineClass *mc)
|
static void virt_machine_2_8_options(MachineClass *mc)
|
||||||
{
|
{
|
||||||
|
VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
|
||||||
|
|
||||||
virt_machine_2_9_options(mc);
|
virt_machine_2_9_options(mc);
|
||||||
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_8);
|
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_8);
|
||||||
|
/* For 2.8 and earlier we falsely claimed in the DT that
|
||||||
|
* our timers were edge-triggered, not level-triggered.
|
||||||
|
*/
|
||||||
|
vmc->claim_edge_triggered_timers = true;
|
||||||
}
|
}
|
||||||
DEFINE_VIRT_MACHINE(2, 8)
|
DEFINE_VIRT_MACHINE(2, 8)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue