diff mbox

OVMF, Q35 and USB keyboard/mouse

Message ID 20140915150754.GO1825@ERROL.INI.CMU.EDU
State New
Headers show

Commit Message

Gabriel L. Somlo Sept. 15, 2014, 3:07 p.m. UTC
On Mon, Sep 15, 2014 at 05:01:21PM +0200, Laszlo Ersek wrote:
> > diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c
> > index 289ca3b..bb230f1 100644
> > --- a/hw/usb/hcd-ehci-pci.c
> > +++ b/hw/usb/hcd-ehci-pci.c
> > @@ -208,8 +208,8 @@ struct ehci_companions {
> >  };
> >  
> >  static const struct ehci_companions ich9_1d[] = {
> > -    { .name = "ich9-usb-uhci1", .func = 0, .port = 0 },
> > -    { .name = "ich9-usb-uhci2", .func = 1, .port = 2 },
> > +    { .name = "ich9-usb-uhci3", .func = 0, .port = 0 },
> > +    { .name = "ich9-usb-uhci3", .func = 1, .port = 2 },
> >      { .name = "ich9-usb-uhci3", .func = 2, .port = 4 },
> >  };
> > 
> > 
> > they *all* get detected and work great on ovmf+osx. Slow kbd+mouse
> > get routed automatically to one of them, and work fine. The only
> > differences I can see between them (in hw/usb/hcd-uhci.c) is
> > the name string and "irq_pin" field. Not sure yet if that's likely
> > to point to an explanation...
> 
> 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:


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).

DSDT looks identical across the ovmf vs. chameleon divide. I'm going
to take some time to do a more thorough search of any and all logging
I can find on OS X, and also try to find wherever in ovmf the guest
device irq pin comes into play where uhci is concerned.

I am basically just poking at it with a stick right now, but maybe
something will start making sense soon :)

Thanks again,
--Gabriel

Comments

Laszlo Ersek Sept. 15, 2014, 6:02 p.m. UTC | #1
On 09/15/14 17:07, Gabriel L. Somlo wrote:
> On Mon, Sep 15, 2014 at 05:01:21PM +0200, Laszlo Ersek wrote:
>>> diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c
>>> index 289ca3b..bb230f1 100644
>>> --- a/hw/usb/hcd-ehci-pci.c
>>> +++ b/hw/usb/hcd-ehci-pci.c
>>> @@ -208,8 +208,8 @@ struct ehci_companions {
>>>  };
>>>  
>>>  static const struct ehci_companions ich9_1d[] = {
>>> -    { .name = "ich9-usb-uhci1", .func = 0, .port = 0 },
>>> -    { .name = "ich9-usb-uhci2", .func = 1, .port = 2 },
>>> +    { .name = "ich9-usb-uhci3", .func = 0, .port = 0 },
>>> +    { .name = "ich9-usb-uhci3", .func = 1, .port = 2 },
>>>      { .name = "ich9-usb-uhci3", .func = 2, .port = 4 },
>>>  };
>>>
>>>
>>> they *all* get detected and work great on ovmf+osx. Slow kbd+mouse
>>> get routed automatically to one of them, and work fine. The only
>>> differences I can see between them (in hw/usb/hcd-uhci.c) is
>>> the name string and "irq_pin" field. Not sure yet if that's likely
>>> to point to an explanation...
>>
>> 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).
> 
> 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:

InstallQemuLinkedTables: "etc/acpi/tables" offset 0x0000000000000000: Signature="FACS" Length=0x00000040
InstallQemuLinkedTables: "etc/acpi/tables" offset 0x0000000000000040: Signature="DSDT" Length=0x00001CE5
InstallQemuLinkedTables: "etc/acpi/tables" offset 0x0000000000001D25: Signature="FACP" Length=0x00000074
InstallQemuLinkedTables: "etc/acpi/tables" offset 0x0000000000001D99: Signature="SSDT" Length=0x00000687
InstallQemuLinkedTables: "etc/acpi/tables" offset 0x0000000000002420: Signature="APIC" Length=0x00000090
InstallQemuLinkedTables: "etc/acpi/tables" offset 0x00000000000024B0: Signature="HPET" Length=0x00000038
InstallQemuLinkedTables: "etc/acpi/tables" offset 0x00000000000024E8: Signature="QEMU" Length=0x0000003C
InstallQemuLinkedTables: "etc/acpi/tables" offset 0x0000000000002524: Signature="RSDT" Length=0x00000038
InstallAllQemuLinkedTables: installed 7 tables

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.

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.)

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.)

Laszlo

> I'm going
> to take some time to do a more thorough search of any and all logging
> I can find on OS X, and also try to find wherever in ovmf the guest
> device irq pin comes into play where uhci is concerned.
> 
> I am basically just poking at it with a stick right now, but maybe
> something will start making sense soon :)
> 
> Thanks again,
> --Gabriel
>
Gabriel L. Somlo Sept. 15, 2014, 7:23 p.m. UTC | #2
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
BALATON Zoltan Sept. 15, 2014, 7:56 p.m. UTC | #3
On Mon, 15 Sep 2014, Gabriel L. Somlo wrote:
> 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.

I think you can find these in the ioreg output. Try ioreg -lfw0 and look 
for IOInterruptSpecifiers or you can try different IORegistryPlanes with 
the -p option (I don't have OS X at hand to test). Alternatively there's a 
GUI tool to get the same info called IORegistryExplorer that used to be 
part of older XCode versions, not sure where can you get it now.

Regards,
BALATON Zoltan
Gerd Hoffmann Sept. 16, 2014, 8:15 a.m. UTC | #4
Hi,

> 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.

Can you test what happens with piix4 + usb2?  (I suspect you can't as
osx will simply not boot then due to the piix4 being much older than the
intel port of osx).

I think ovmf simply doesn't get the IRQ routing right (doesn't
initialize PCI_INTERRUPT_LINE register correctly).  It is *not* the same
on piix and q35.  See piix_pci_slot_get_irq vs. mch_pci_slot_get_irq in
seabios source code.

I think if you boot linux guests with "noacpi" it will have trouble with
usb interrupts too.

cheers,
  Gerd
Gabriel L. Somlo Sept. 21, 2014, 10:10 p.m. UTC | #5
On Mon, Sep 15, 2014 at 08:02:04PM +0200, Laszlo Ersek wrote:
> 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*.

Using F20-live on Q35 with SeaBIOS vs. OVMF, I get:

$> grep LNK dmsg*.log

dmsg_bios.log: ACPI: PCI Interrupt Link [LNKA] (IRQs 5 *10 11)
dmsg_bios.log: ACPI: PCI Interrupt Link [LNKB] (IRQs 5 *10 11)
dmsg_bios.log: ACPI: PCI Interrupt Link [LNKC] (IRQs 5 10 *11)
dmsg_bios.log: ACPI: PCI Interrupt Link [LNKD] (IRQs 5 10 *11)
dmsg_bios.log: ACPI: PCI Interrupt Link [LNKE] (IRQs 5 *10 11)
dmsg_bios.log: ACPI: PCI Interrupt Link [LNKF] (IRQs 5 *10 11)
dmsg_bios.log: ACPI: PCI Interrupt Link [LNKG] (IRQs 5 10 *11)
dmsg_bios.log: ACPI: PCI Interrupt Link [LNKH] (IRQs 5 10 *11)

dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKA] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKB] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKC] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKD] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKE] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKF] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKG] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKH] (IRQs 5 10 11) *0, disabled.

Neither logs any "Link enabled at IRQ X" messages (at least not for
LNKX; they both do for GSIX, though). Wonder why it says "disabled"
on OVMF... This is the only suspicious (to me) difference between
SeaBIOS and OVMF with q35 and fedora.

$> grep uhci_hcd dmsg*.log

dmsg_bios.log: uhci_hcd: USB Universal Host Controller Interface driver
dmsg_bios.log: uhci_hcd 0000:00:1d.0: setting latency timer to 64
dmsg_bios.log: uhci_hcd 0000:00:1d.0: UHCI Host Controller
dmsg_bios.log: uhci_hcd 0000:00:1d.0: new USB bus registered, assigned bus # 2
dmsg_bios.log: uhci_hcd 0000:00:1d.0: irq 16, io base 0x0000c080
dmsg_bios.log: usb usb2: Manufacturer: Linux 3.11.10-301.fc20.x86_64 uhci_hcd
dmsg_bios.log: uhci_hcd 0000:00:1d.1: setting latency timer to 64
dmsg_bios.log: uhci_hcd 0000:00:1d.1: UHCI Host Controller
dmsg_bios.log: uhci_hcd 0000:00:1d.1: new USB bus registered, assigned bus # 3
dmsg_bios.log: uhci_hcd 0000:00:1d.1: irq 17, io base 0x0000c0a0
dmsg_bios.log: usb usb3: Manufacturer: Linux 3.11.10-301.fc20.x86_64 uhci_hcd
dmsg_bios.log: uhci_hcd 0000:00:1d.2: setting latency timer to 64
dmsg_bios.log: uhci_hcd 0000:00:1d.2: UHCI Host Controller
dmsg_bios.log: uhci_hcd 0000:00:1d.2: new USB bus registered, assigned bus # 4
dmsg_bios.log: uhci_hcd 0000:00:1d.2: irq 18, io base 0x0000c0c0
dmsg_bios.log: usb usb4: Manufacturer: Linux 3.11.10-301.fc20.x86_64 uhci_hcd

dmsg_ovmf.log: uhci_hcd: USB Universal Host Controller Interface driver
dmsg_ovmf.log: uhci_hcd 0000:00:1d.0: setting latency timer to 64
dmsg_ovmf.log: uhci_hcd 0000:00:1d.0: UHCI Host Controller
dmsg_ovmf.log: uhci_hcd 0000:00:1d.0: new USB bus registered, assigned bus # 2
dmsg_ovmf.log: uhci_hcd 0000:00:1d.0: irq 16, io base 0x0000c0e0
dmsg_ovmf.log: usb usb2: Manufacturer: Linux 3.11.10-301.fc20.x86_64 uhci_hcd
dmsg_ovmf.log: uhci_hcd 0000:00:1d.1: setting latency timer to 64
dmsg_ovmf.log: uhci_hcd 0000:00:1d.1: UHCI Host Controller
dmsg_ovmf.log: uhci_hcd 0000:00:1d.1: new USB bus registered, assigned bus # 3
dmsg_ovmf.log: uhci_hcd 0000:00:1d.1: irq 17, io base 0x0000c0c0
dmsg_ovmf.log: usb usb3: Manufacturer: Linux 3.11.10-301.fc20.x86_64 uhci_hcd
dmsg_ovmf.log: uhci_hcd 0000:00:1d.2: setting latency timer to 64
dmsg_ovmf.log: uhci_hcd 0000:00:1d.2: UHCI Host Controller
dmsg_ovmf.log: uhci_hcd 0000:00:1d.2: new USB bus registered, assigned bus # 4
dmsg_ovmf.log: uhci_hcd 0000:00:1d.2: irq 18, io base 0x0000c0a0
dmsg_ovmf.log: usb usb4: Manufacturer: Linux 3.11.10-301.fc20.x86_64 uhci_hcd

The IO base addresses are the only difference, not sure if this matters:

 pci    io_base_addr
device   bios  ovmf
---------------------
 1d.0    c080  c0e0
 1d.1    c0a0  c0c0
 1d.2    c0c0  c0a0

Finally, the relevant bits from /proc/interrupts:

with SeaBIOS:

 16:   0    0    0    0   IO-APIC-fasteoi   uhci_hcd:usb2, i801_smbus
 17:   0    0    0    0   IO-APIC-fasteoi   uhci_hcd:usb3
 18:   0    0    0    0   IO-APIC-fasteoi   uhci_hcd:usb4
 19:  55    0  341    0   IO-APIC-fasteoi   ehci_hcd:usb1

and with OVMF:

 16:   0    0    0    0   IO-APIC-fasteoi   uhci_hcd:usb2, i801_smbus
 17:   0    0    0    0   IO-APIC-fasteoi   uhci_hcd:usb3
 18:   0    0    0    0   IO-APIC-fasteoi   uhci_hcd:usb4
 19:  55    0  131    0   IO-APIC-fasteoi   ehci_hcd:usb1

So, basically, no difference. 

I'm off studying "pci_slot_get_irq" functions in SeaBIOS per Gerd's
suggestion, hopefully I can locate the equivalent functionality in
OVMF and spot some problem with it I can fix.

That might take me a while... :)

Thanks,
--Gabriel
Laszlo Ersek Sept. 21, 2014, 10:43 p.m. UTC | #6
On 09/22/14 00:10, Gabriel L. Somlo wrote:

> dmsg_bios.log: ACPI: PCI Interrupt Link [LNKA] (IRQs 5 *10 11)
> dmsg_bios.log: ACPI: PCI Interrupt Link [LNKB] (IRQs 5 *10 11)
> dmsg_bios.log: ACPI: PCI Interrupt Link [LNKC] (IRQs 5 10 *11)
> dmsg_bios.log: ACPI: PCI Interrupt Link [LNKD] (IRQs 5 10 *11)
> dmsg_bios.log: ACPI: PCI Interrupt Link [LNKE] (IRQs 5 *10 11)
> dmsg_bios.log: ACPI: PCI Interrupt Link [LNKF] (IRQs 5 *10 11)
> dmsg_bios.log: ACPI: PCI Interrupt Link [LNKG] (IRQs 5 10 *11)
> dmsg_bios.log: ACPI: PCI Interrupt Link [LNKH] (IRQs 5 10 *11)
> 
> dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKA] (IRQs 5 10 11) *0, disabled.
> dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKB] (IRQs 5 10 11) *0, disabled.
> dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKC] (IRQs 5 10 11) *0, disabled.
> dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKD] (IRQs 5 10 11) *0, disabled.
> dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKE] (IRQs 5 10 11) *0, disabled.
> dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKF] (IRQs 5 10 11) *0, disabled.
> dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKG] (IRQs 5 10 11) *0, disabled.
> dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKH] (IRQs 5 10 11) *0, disabled.

I mentioned earlier that grepping OvmfPkg for PCI_LIB_ADDRESS would be
necessary. See PciInitialization() in
"OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c".

  //
  // Bus 0, Device 1, Function 0 - PCI to ISA Bridge
  //
  PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x3c), 0x00);
  PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // LNKA routing target
  PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // LNKB routing target
  PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // LNKC routing target
  PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // LNKD routing target

This is not appropriate for q35. See "ICH9 PCI to ISA irq remapping" in
qemu's "q35-acpi-dsdt.dsl" file, vs. "PIIX PCI to ISA irq remapping" in
"acpi-dsdt.dsl".

The above was added in 05c89c7f.

Thanks
Laszlo
Paolo Bonzini Sept. 22, 2014, 4:44 p.m. UTC | #7
Il 22/09/2014 00:43, Laszlo Ersek ha scritto:
>   // Bus 0, Device 1, Function 0 - PCI to ISA Bridge
>   //
>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x3c), 0x00);
>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // LNKA routing target
>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // LNKB routing target
>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // LNKC routing target
>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // LNKD routing target
> 
> This is not appropriate for q35. See "ICH9 PCI to ISA irq remapping" in
> qemu's "q35-acpi-dsdt.dsl" file, vs. "PIIX PCI to ISA irq remapping" in
> "acpi-dsdt.dsl".

If I understand the DSDT right, the field is still at 0x60, but the
bridge is now at 1f.0.  Right?  Also, LNKE-LNKH must be set at 0x68.

However, these are only used if OS X runs in PIC mode rather than APIC
mode (see \_PIC).  Why should OS X be running in PIC mode?  So fixing
the above is right to do, but may not be enough.

Paolo
Gabriel L. Somlo Sept. 22, 2014, 4:59 p.m. UTC | #8
On Mon, Sep 22, 2014 at 06:44:26PM +0200, Paolo Bonzini wrote:
> Il 22/09/2014 00:43, Laszlo Ersek ha scritto:
> > I mentioned earlier that grepping OvmfPkg for PCI_LIB_ADDRESS would be
> > necessary. See PciInitialization() in
> > "OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c".

I remember that, but back then I hadn't RTFMed enough for it
to mean something useful to me. I get it now though, so thanks
for the repetition :)

> >   //
> >   // Bus 0, Device 1, Function 0 - PCI to ISA Bridge
> >   //
> >   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x3c), 0x00);
> >   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // LNKA routing target
> >   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // LNKB routing target
> >   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // LNKC routing target
> >   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // LNKD routing target
> > 
> > This is not appropriate for q35. See "ICH9 PCI to ISA irq remapping" in
> > qemu's "q35-acpi-dsdt.dsl" file, vs. "PIIX PCI to ISA irq remapping" in
> > "acpi-dsdt.dsl".
> 
> If I understand the DSDT right, the field is still at 0x60, but the
> bridge is now at 1f.0.  Right?  Also, LNKE-LNKH must be set at 0x68.
> 
> However, these are only used if OS X runs in PIC mode rather than APIC
> mode (see \_PIC).  Why should OS X be running in PIC mode?  So fixing
> the above is right to do, but may not be enough.

I'll take a pass at making these dependent on the host bridge DID (same
as with the pmbase/pmtimer) instead of hard coded, then we'll see what
else breaks :)

Thanks again for
Laszlo Ersek Sept. 22, 2014, 8:40 p.m. UTC | #9
On 09/22/14 18:44, Paolo Bonzini wrote:
> Il 22/09/2014 00:43, Laszlo Ersek ha scritto:
>>   // Bus 0, Device 1, Function 0 - PCI to ISA Bridge
>>   //
>>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x3c), 0x00);
>>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // LNKA routing target
>>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // LNKB routing target
>>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // LNKC routing target
>>   PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // LNKD routing target
>>
>> This is not appropriate for q35. See "ICH9 PCI to ISA irq remapping" in
>> qemu's "q35-acpi-dsdt.dsl" file, vs. "PIIX PCI to ISA irq remapping" in
>> "acpi-dsdt.dsl".
> 
> If I understand the DSDT right, the field is still at 0x60, but the
> bridge is now at 1f.0.  Right?  Also, LNKE-LNKH must be set at 0x68.
> 
> However, these are only used if OS X runs in PIC mode rather than APIC
> mode (see \_PIC).  Why should OS X be running in PIC mode?  So fixing
> the above is right to do, but may not be enough.

I agree completely. I mentioned this because of

dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKA] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKB] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKC] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKD] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKE] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKF] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKG] (IRQs 5 10 11) *0, disabled.
dmsg_ovmf.log: ACPI: PCI Interrupt Link [LNKH] (IRQs 5 10 11) *0, disabled.

Thanks,
Laszlo
diff mbox

Patch

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 */