diff mbox

RFC: ioapic polarity vs. qemu os-x guest

Message ID 20140211182330.GC29329@ERROL.INI.CMU.EDU (mailing list archive)
State New, archived
Headers show

Commit Message

Gabriel L. Somlo Feb. 11, 2014, 6:23 p.m. UTC
Hi,

I'm trying to get OS X to work as a QEMU guest, and one of the few
remaining "mysteries" I need to solve is that the OS X guest hangs
during boot, waiting for its boot disk to be available, unless the
following KVM patch is applied:


--


After digging around the KVM source for a bit, and printk-ing things
from Windows 7, Fedora 20, and OS X (10.9), I figured out the following:


1. Edge-triggered interrupts are invariably unaffected by the xor line
being removed by the patch. On all three guest types, edge-triggered
interrupts have polarity set to 0, so the xor is essentially a no-op,
and we can forget about it altogether.


2. Windows and Linux always configure all level-triggered interrupts
with polarity 0 (active-high, consistent with QEMU's ACPI/DSDT, in
particular q35-acpi-dsdt.dsl, which is what I'm using with -M q35).
As such, on Windows and Linux, the xor line in question is still a
no-op.


3. OS X (all versions I tried, at least since 10.5/Leopard) always
configures all level-triggered interrupts with polarity 1 (active-low),
regardless of what the QEMU DSDT says. As such, the xor line acts as
a negation of "irq_level", which at first glance sounds reasonable.

However: when KVM negates "irq_level" due to "polarity == 1", the OS X
guest hangs during boot.

OS X works fine when "polarity == 1" is ignored (with the xor line
commented out).

This may be another instance (similar to how OS X didn't use to check
with CPUID regarding monitor/mwait instruction availability) where
apple devs know that any of their supported hardware advertises
active-low in the DSDT, so no need to check, just hardcode that
assumption... :)


4. With s/ActiveHigh/ActiveLow/ in QEMU's q35-acpi-dsdt.dsl, Linux
actually switches to "polarity == 1" (active-low), and works fine
*with the xor line removed* !!!. With the xor line left intact (i.e.
without the above patch), the active-low fedora guest worked extremely
poorly, and printed out multiple error messages during boot:

	irq XX: nobody cared (try booting with the "irqpoll" option)
	...
	Disabling IRQ #XX

for XX in [16, 18, 19, ...].


So, right now, I'm wondering about the following:


1. Regarding KVM and the polarity xor line in the patch above: Does
anyone have experience with any *other* guests which insist on setting
level-triggered interrupt polarity to 1/active-low ? Is that xor line
actually doing anything useful in practice, for any other guest, on
either QEMU or any other platform ?


2. Is there anything in QEMU (besides the ACPI DSDT .dsl files) which
has a hardcoded assumption re. "polarity == 0", or active-high, for
level-triggered interrupts? I tried to dig through hw/i386/kvm/ioapic.c
and a bunch of other files, but couldn't isolate anything that I could
"flip" to fix things in userspace.


Any ideas or suggestions about the appropriate way to move forward would
be much appreciated !!!


Thanks much,
--Gabriel
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Michael S. Tsirkin Feb. 11, 2014, 7:54 p.m. UTC | #1
On Tue, Feb 11, 2014 at 01:23:31PM -0500, Gabriel L. Somlo wrote:
> Hi,
> 
> I'm trying to get OS X to work as a QEMU guest, and one of the few
> remaining "mysteries" I need to solve is that the OS X guest hangs
> during boot, waiting for its boot disk to be available, unless the
> following KVM patch is applied:
> 
> 
> diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
> index ce9ed99..1539d37 100644
> --- a/virt/kvm/ioapic.c
> +++ b/virt/kvm/ioapic.c
> @@ -328,7 +328,6 @@ int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int irq_source_id,
>  	irq_level = __kvm_irq_line_state(&ioapic->irq_states[irq],
>  					 irq_source_id, level);
>  	entry = ioapic->redirtbl[irq];
> -	irq_level ^= entry.fields.polarity;
>  	if (!irq_level) {
>  		ioapic->irr &= ~mask;
>  		ret = 1;
> --
> 
> 
> After digging around the KVM source for a bit, and printk-ing things
> from Windows 7, Fedora 20, and OS X (10.9), I figured out the following:
> 
> 
> 1. Edge-triggered interrupts are invariably unaffected by the xor line
> being removed by the patch. On all three guest types, edge-triggered
> interrupts have polarity set to 0, so the xor is essentially a no-op,
> and we can forget about it altogether.
> 
> 
> 2. Windows and Linux always configure all level-triggered interrupts
> with polarity 0 (active-high, consistent with QEMU's ACPI/DSDT, in
> particular q35-acpi-dsdt.dsl, which is what I'm using with -M q35).
> As such, on Windows and Linux, the xor line in question is still a
> no-op.
> 
> 
> 3. OS X (all versions I tried, at least since 10.5/Leopard) always
> configures all level-triggered interrupts with polarity 1 (active-low),
> regardless of what the QEMU DSDT says. As such, the xor line acts as
> a negation of "irq_level", which at first glance sounds reasonable.
> 
> However: when KVM negates "irq_level" due to "polarity == 1", the OS X
> guest hangs during boot.
> 
> OS X works fine when "polarity == 1" is ignored (with the xor line
> commented out).
> 
> This may be another instance (similar to how OS X didn't use to check
> with CPUID regarding monitor/mwait instruction availability) where
> apple devs know that any of their supported hardware advertises
> active-low in the DSDT, so no need to check, just hardcode that
> assumption... :)
> 
> 
> 4. With s/ActiveHigh/ActiveLow/ in QEMU's q35-acpi-dsdt.dsl, Linux
> actually switches to "polarity == 1" (active-low), and works fine
> *with the xor line removed* !!!. With the xor line left intact (i.e.
> without the above patch), the active-low fedora guest worked extremely
> poorly, and printed out multiple error messages during boot:
> 
> 	irq XX: nobody cared (try booting with the "irqpoll" option)
> 	...
> 	Disabling IRQ #XX
> 
> for XX in [16, 18, 19, ...].
> 
> 
> So, right now, I'm wondering about the following:
> 
> 
> 1. Regarding KVM and the polarity xor line in the patch above: Does
> anyone have experience with any *other* guests which insist on setting
> level-triggered interrupt polarity to 1/active-low ? Is that xor line
> actually doing anything useful in practice, for any other guest, on
> either QEMU or any other platform ?
> 
> 
> 2. Is there anything in QEMU (besides the ACPI DSDT .dsl files) which
> has a hardcoded assumption re. "polarity == 0", or active-high, for
> level-triggered interrupts? I tried to dig through hw/i386/kvm/ioapic.c
> and a bunch of other files, but couldn't isolate anything that I could
> "flip" to fix things in userspace.
> 
> 
> Any ideas or suggestions about the appropriate way to move forward would
> be much appreciated !!!
> 
> 
> Thanks much,
> --Gabriel

I think changing ACPI is the right thing to
do really. But we'll need to fix some things
first of course.

I think it's PC Q35 that has this assumption.
hw/i386/pc_q35.c

        gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
                                 GSI_NUM_PINS);

kvm_pc_gsi_handler simply forwards interrupts to kvm.

and

hw/isa/lpc_ich9.c
static void ich9_lpc_update_pic(ICH9LPCState *lpc, int pic_irq)
{
    int i, pic_level;

    /* The pic level is the logical OR of all the PCI irqs mapped to it */
    /* The pic level is the logical OR of all the PCI irqs mapped to it
 * */
    pic_level = 0;
    for (i = 0; i < ICH9_LPC_NB_PIRQS; i++) {
        int tmp_irq;
        int tmp_dis;
        ich9_lpc_pic_irq(lpc, i, &tmp_irq, &tmp_dis);
        if (!tmp_dis && pic_irq == tmp_irq) {
            pic_level |= pci_bus_get_irq_level(lpc->d.bus, i);
        }
    }

so somewhere we need to flip it, I am guessing in ich9
along the lines of:

-    pic_level = 0;
-            pic_level |= pci_bus_get_irq_level(lpc->d.bus, i);
+    pic_level = 1;
+            pic_level &= !pci_bus_get_irq_level(lpc->d.bus, i);
Gabriel L. Somlo Feb. 11, 2014, 9:35 p.m. UTC | #2
On Tue, Feb 11, 2014 at 09:54:44PM +0200, Michael S. Tsirkin wrote:
> On Tue, Feb 11, 2014 at 01:23:31PM -0500, Gabriel L. Somlo wrote:
> > I'm trying to get OS X to work as a QEMU guest, and one of the few
> > remaining "mysteries" I need to solve is that the OS X guest hangs
> > during boot, waiting for its boot disk to be available, unless the
> > following KVM patch is applied:
> > [...]
> > 2. Is there anything in QEMU (besides the ACPI DSDT .dsl files) which
> > has a hardcoded assumption re. "polarity == 0", or active-high, for
> > level-triggered interrupts? I tried to dig through hw/i386/kvm/ioapic.c
> > and a bunch of other files, but couldn't isolate anything that I could
> > "flip" to fix things in userspace.
> > 
> > 
> > Any ideas or suggestions about the appropriate way to move forward would
> > be much appreciated !!!
> > 
> > 
> > Thanks much,
> > --Gabriel
> 
> I think changing ACPI is the right thing to
> do really. But we'll need to fix some things
> first of course.
> 
> I think it's PC Q35 that has this assumption.
> hw/i386/pc_q35.c
> 
>         gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
>                                  GSI_NUM_PINS);
> 
> kvm_pc_gsi_handler simply forwards interrupts to kvm.
> 
> and
> 
> hw/isa/lpc_ich9.c
> static void ich9_lpc_update_pic(ICH9LPCState *lpc, int pic_irq)
> {
>     int i, pic_level;
> 
>     /* The pic level is the logical OR of all the PCI irqs mapped to it */
>     /* The pic level is the logical OR of all the PCI irqs mapped to it
>  * */
>     pic_level = 0;
>     for (i = 0; i < ICH9_LPC_NB_PIRQS; i++) {
>         int tmp_irq;
>         int tmp_dis;
>         ich9_lpc_pic_irq(lpc, i, &tmp_irq, &tmp_dis);
>         if (!tmp_dis && pic_irq == tmp_irq) {
>             pic_level |= pci_bus_get_irq_level(lpc->d.bus, i);
>         }
>     }
> 
> so somewhere we need to flip it, I am guessing in ich9
> along the lines of:
> 
> -    pic_level = 0;
> -            pic_level |= pci_bus_get_irq_level(lpc->d.bus, i);
> +    pic_level = 1;
> +            pic_level &= !pci_bus_get_irq_level(lpc->d.bus, i);

I think now we're on to something!

I managed to boot OS X on q35 with absolutely no kernel patches, but
Linux still hated it ("irqXX: nobody cared"). At least now I know what
I'm looking for, so I'll try to come up with a way to flip
level-triggered polarity to ActiveLow across all of i386, in a way
that works for Linux and Windows guests as well.

Thanks again for getting me unstuck!
--Gabriel
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
index ce9ed99..1539d37 100644
--- a/virt/kvm/ioapic.c
+++ b/virt/kvm/ioapic.c
@@ -328,7 +328,6 @@  int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int irq_source_id,
 	irq_level = __kvm_irq_line_state(&ioapic->irq_states[irq],
 					 irq_source_id, level);
 	entry = ioapic->redirtbl[irq];
-	irq_level ^= entry.fields.polarity;
 	if (!irq_level) {
 		ioapic->irr &= ~mask;
 		ret = 1;