qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] OVMF, Q35 and USB keyboard/mouse


From: Gabriel L. Somlo
Subject: Re: [Qemu-devel] OVMF, Q35 and USB keyboard/mouse
Date: Mon, 15 Sep 2014 15:23:34 -0400
User-agent: Mutt/1.5.23 (2014-03-12)

On Mon, Sep 15, 2014 at 08:02:04PM +0200, Laszlo Ersek wrote:
> >> It is actually extremely relevant, the irq_pin field. I'm not exactly
> >> sure how just yet, but it is. Maybe check the interrupt routing in OSX
> >> somehow? Do you have a dmesg-like log in OSX, with a PRT dump from the
> >> DSDT, and messages about interrupt routing setup? Do you have in OSX
> >> anything that corresponds to /proc/interrupts under Linux?
> > 
> > Actually, even more exciting:
> > 
> > diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
> > index 3b3ebcd..d61656e 100644
> > --- a/hw/usb/hcd-uhci.c
> > +++ b/hw/usb/hcd-uhci.c
> > @@ -1335,21 +1335,21 @@ static UHCIInfo uhci_info[] = {
> >          .vendor_id = PCI_VENDOR_ID_INTEL,
> >          .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1,
> >          .revision  = 0x03,
> > -        .irq_pin   = 0,
> > +        .irq_pin   = 1,
> >          .unplug    = false,
> >      },{
> >          .name      = "ich9-usb-uhci2", /* 00:1d.1 */
> >          .vendor_id = PCI_VENDOR_ID_INTEL,
> >          .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2,
> >          .revision  = 0x03,
> > -        .irq_pin   = 1,
> > +        .irq_pin   = 2,
> >          .unplug    = false,
> >      },{
> >          .name      = "ich9-usb-uhci3", /* 00:1d.2 */
> >          .vendor_id = PCI_VENDOR_ID_INTEL,
> >          .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3,
> >          .revision  = 0x03,
> > -        .irq_pin   = 2,
> > +        .irq_pin   = 3,
> >          .unplug    = false,
> >      },{
> >          .name      = "ich9-usb-uhci4", /* 00:1a.0 */
> > 
> > Turns out, anything with an irq_pin <= 1 won't show up when osx is
> > booted on q35 with ovmf (but osx + q35 works if booted via Chameleon).

OK, so I forgot to articulate that with the above patch, I'm seeing
*both* uhci2 and uhci3, but not uhci1. Basically, if uhciX has an
irq_pin less than 2, it won't show up in OSX if booted with ovmf.
We're no longer looking at PIIX, this is q35 with ovmf or without.

> > 
> > DSDT looks identical across the ovmf vs. chameleon divide.
> 
> Now I'm curious. What's this chameleon thing? (Yes, I did find the
> homepage. Apparently the lead developer is a fellow Hungarian. A small
> world.) I'm surprised how you can get the same DSDT under both OVMF and
> chameleon. Assuming you run a recent OVMF on a recent QEMU, the DSDT
> exposed to the guest will originate from QEMU. This is confirmed by your
> q35.log that you sent me in private (due to its size) previously:
> 
...
> 
> I've got no clue how you can end up with the exact same DSDT under
> chameleon, unless it has a client for QEMU's fw_cfg ACPI linker/loader.

Chameleon is a multistage bootloader which can be started by a PC-BIOS
based machine, and which can then load OSX's /mach_kernel file from
the root directory of the main HFS+ partition (as opposed to loading
and running /System/Library/CoreServices/boot.efi, which is how an EFI
compatible BIOS would do it natively.

With qemu, I'm "side-loading" Chameleon's stage-2 loader. I.e., I
won't bother loading all the stages via the bios from the hard drive.
Instead, I add "-kernel chameleon_stage2_loader" to the qemu command
line, which bypasses all earlier stages.

While Chameleon *can* override the DSDT of its underlying machine
by accessing a .plist config file dropped into the root of the OS X
file system (i.e., that's how it's done on a hackintosh), in my case
I don't need to do that, as QEMU already provides a perfectly adequate
DSDT, so Chameleon just leaves it alone and proceeds to boot the mach
kernel.

I downloaded a DSDT ripper for the Mac (DSDTEditor_Mac.zip) I found via
from some forum or another (insanelymac.com or osx86project.org or
tonymacx86.com, don't remember precisely anymore) and dumped the DSDT
from inside OSX after having booted it with Chameleon-on-top-of-SeaBIOS on
one hand, or OVMF on the other. They look identical. It's just that
when using ovmf, the uhci irq_pin less-than-two invisibility thing kicks
in for some weird reason I'm still looking for :)

> In fact I think that should be *precisely* the difference here. The PCI
> interrupt routing table (_PRT) in the DSDT describes a two-level
> mapping. (I've probably forgotten most of the details, sorry.) First, it
> maps each PCI (bus, dev, pin) triplet to a PNP0C0F ("PCI interrupt
> link") device. We usually call these LNKA, LNKB, LNC, LNKD, LNKS on
> i440fx; there are more on q35. Then, each LNKx specifies a set of
> possible legacy interrupts that the link can be programmed for /
> assigned to. At runtime, the OS programs each of the interrupt links to
> one of its allowed legacy interrupts, and then all the pins (across
> buses and functions) that are connected to that interrupt link will
> trigger that interrupt. The OS usually tries to come up with a mapping
> (from LNKx to IRQ) so that interrupt sharing is minimized.
> 
> Here's an example from my i440fx Fedora 20 VM.
> 
> (1) The dmesg says first
> 
>   ACPI: PCI Interrupt Link [LNKA] (IRQs 5 10 *11)
>   ACPI: PCI Interrupt Link [LNKB] (IRQs 5 10 *11)
>   ACPI: PCI Interrupt Link [LNKC] (IRQs 5 *10 11)
>   ACPI: PCI Interrupt Link [LNKD] (IRQs 5 *10 11)
>   ACPI: PCI Interrupt Link [LNKS] (IRQs *9)
> 
> This displays what IRQs the _PRT in the DSDT allows for each of the LNKx
> links, and the asterisks show (IIRC) what elements of those sets are
> selected (programmed) when Linux inherits the hardware.
> 
> (2) Later it logs
> 
>   ACPI: PCI Interrupt Link [LNKC] enabled at IRQ 10
>   ACPI: PCI Interrupt Link [LNKD] enabled at IRQ 11
>   ACPI: PCI Interrupt Link [LNKA] enabled at IRQ 11
>   ACPI: PCI Interrupt Link [LNKB] enabled at IRQ 10
> 
> Let's call this mapping LNK_IRQ().
> 
> (3) Then look for the uchi controllers:
> 
>   uhci_hcd 0000:00:07.0: irq 10, io base 0x0000c0c0
>   uhci_hcd 0000:00:07.1: irq 11, io base 0x0000c0a0
>   uhci_hcd 0000:00:07.2: irq 11, io base 0x0000c080
> 
> And /proc/interrupts is consistent with that:
> 
>              CPU0       CPU1       
>    10:          6         25   IO-APIC-fasteoi   ehci_hcd:usb1, uhci_hcd:usb2
>    11:          0          0   IO-APIC-fasteoi   uhci_hcd:usb3, 
> uhci_hcd:usb4, virtio2
> 
> These last two blocks are *results*.
> 
> Again, this is the result of composing two functions:
> 
>   device_interrupt = LNK_IRQ(PRT(bus, dev, pin))
> 
> PRT() comes from the DSDT, and maps (bus, dev, pin) to a link, while
> LNK_IRQ() comes from the OS (the actual link -> IRQ assignment), and is
> restricted to the possibilities offered in the DSDT.
> 
> The PRT that QEMU generates follows a rotating pattern (it is not
> restricted by physical circuits). As you go from one PCI device to the
> next, the same LNKA - LNKD links are distributed over the device's pins,
> but the sequence is shifted by one. The idea is that most PCI devices
> use only their first pin (INTA), and placing such devices "beside" each
> other should nicely iterate over all links, evenly.
> 
> Thus far I didn't speak about functions of the same PCI device. I didn't
> do that because I'm uneducated (even more than in the above :)). The
> basic idea is that different functions of the device will use different
> pins. Most devices are single-function, hence they usually stick with
> INTA. If you've got a multifunction device, then the functions will use
> separate pins.
> 
> For example, if I dump and decompile the DSDT in the guest, for bus 0
> device 7, I get, from the PRT:
> 
>   Package (0x04) { 0x0007FFFF,  Zero,  LNKC,  Zero },  <-- pin 0 / INTA
>   Package (0x04) { 0x0007FFFF,  One,  LNKD,  Zero },   <-- pin 1 / INTB
>   Package (0x04) { 0x0007FFFF,  0x02,  LNKA,  Zero },  <-- pin 2 / INTC
>   Package (0x04) { 0x0007FFFF,  0x03,  LNKB,  Zero },  <-- pin 3 / INTD
> 
> Let's put it all together, for a QEMU command line with
> 
>   -device 
> ich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x7
>   -device ich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pci.0,addr=0x7.0x1
>   -device ich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pci.0,addr=0x7.0x2
> 
> dev & func,  pin, set  interrupt link  IRQ programmed
> set on qemu  in qemu   for dev & pin,  by Linux for
> cmdline      source    set in DSDT     link
> -----------  --------  --------------  --------------
> 07.0                0            LNKC              10
> 07.1                1            LNKD              11
> 07.2                2            LNKA              11
> 
> The first two columns are input parameters (from the command line and
> from the source code). The third colum is the result of evaluating PRT()
> on the input params. The fourth column is the result of evaluating
> Linux's LNK_IRQ() function on the third column.
> 
> (Note that all of the above is for i440fx, not q35, but the method is
> similar.)

Thanks for this crash course, this is really useful stuff to know!

> Ultimately, I think that the difference between OVMF and chameleon is
> the following: when booting OSX with chameleon, QEMU's rotating _PRT is
> not exposed to OSX, because chameleon doesn't know how to download and
> interpret the necessary fw_cfg blobs. (What _PRT OSX decides to use
> then, I can't imagine.) But when you boot OSX with OVMF, then QEMU's
> _PRT is exposed to OSX, and OSX, seeing the PCI bus/device/func
> addresses of the UHCI controllers, *and* seeing their respective PINs,
> *and* seeing their respective LNKx links (from the DSDT), maps the
> function to some interrupt that kills the device.
> 
> This is consistent with your results (if you change the PINs in the
> source code, things work). It would be interesting to see what happens
> if you shuffle the PCI addresses of the UHCI controllers.
> 
> ... Hm. You did mention in the thread starter that chameleon runs on top
> of SeaBIOS. SeaBIOS does have an ACPI linker/loader client, which would
> explain why you see the same DSDT. The only thing that could differ
> between the two cases is the LNK_IRQ() assignment then (ie. how OSX
> chooses to map PCI interrupt links to IRQs), and I don't know why that
> would be different. A /proc/interrupts table would be useful, again.
> 
> (Sorry about all the crazy errors I must have said above about PCI, ACPI
> etc etc etc. Even if it turns out to be incorrect to some degree, if it
> helps others help you, then it wasn't in vain.)

Since the DSDT as seen by the guest is identical regardless of whether I
boot via ovmf or seabios+chameleon, I assume the results of all these
mappings should end up being the same. I'll look for as close an
equivalent of "cat /proc/interrupts" on os x as I can find, and try to
sanity-check this assumption.

Thanks,
--Gabriel



reply via email to

[Prev in Thread] Current Thread [Next in Thread]