[SOLVED]xHC interruption with MSI-X on QEMU doesn't work

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
tokusan
Member
Member
Posts: 29
Joined: Mon Feb 24, 2020 10:18 pm
Location: Japan

[SOLVED]xHC interruption with MSI-X on QEMU doesn't work

Post by tokusan »

Hello, I'm developing my xHCI drivers. I reset xHC by following the instructions of the Intel xHCI manual. An interrupt comes from xHCI on my laptop, but no interruption happens on QEMU. I suspect this is caused by my wrong configuration of the MSI-X table because the laptop has only MSI capability for xHCI, and QEMU has only MSI-X capability for it.

My full code: https://github.com/toku-sa-n/ramen/tree ... _from_qemu

This is the initialization of MSI-X. (kernel/src/device/pci/config/extended_capability/msi_x.rs)

Code: Select all

impl<'a> CapabilitySpec for MsiX<'a> {
    fn init_for_xhci(&self, config_type_spec: &TypeSpec) {
        let base_address = config_type_spec.base_address(self.bir());
        let mut table = self.table(base_address);

        let pending_base = config_type_spec.base_address(self.pending_bir());
        self.pending_bit_table(pending_base)[0] = 1;
        table[0].init_for_xhci();

        self.enable_interrupt();
    }
}

methods

Code: Select all

impl<'a> MsiX<'a> {
    fn enable_interrupt(&self) {
        let val = self.registers.get(self.base) | 0xf000_0000;
        self.registers.set(self.base, val);
    }
}
// kernel/src/device/pci/config/extended_capability/msi_x.rs
bitfield! {
    #[derive(Debug)]
    #[repr(transparent)]
    struct Element(u128);

    u32, from into MessageAddress, message_address,set_message_address: 31, 0;
    u32, from into MessageData, message_data, set_message_data: 95, 64;
    masked, set_mask: 96;
}
impl Element {
    fn init_for_xhci(&mut self) {
        self.message_address().init_for_xhci();
        self.message_data().init_for_xhci();
        self.set_mask(false);
    }
}

// kernel/src/device/pci/config/extended_capability/mod.rs
bitfield! {
    #[repr(transparent)]
    pub struct MessageAddress(u32);

    redirection_hint, set_redirection_hint: 3;
    u8, destination_id, set_destination_id: 19, 12;
    fixed_value, set_fixed_value: 31, 20;
}

impl MessageAddress {
    pub fn init_for_xhci(&mut self) {
        info!("LOCAL APIC ID: {}", Self::get_local_apic_id());
        self.set_destination_id(Self::get_local_apic_id());
        self.set_redirection_hint(false);
        self.set_fixed_value(0xfee);
    }

    fn get_local_apic_id() -> u8 {
        let accessor = single_object::Accessor::<u32>::new(LOCAL_APIC_ID_REGISTER_ADDR, 0);
        u8::try_from(*accessor >> 24).unwrap()
    }
}
bitfield! {
    #[repr(transparent)]
    pub struct MessageData(u32);

    vector, set_vector: 7, 0;
    delivery_mode, set_delivery_mode: 10, 8;
    level, set_level: 14;
    trigger_mode, set_trigger_mode: 15;
}

impl MessageData {
    pub fn init_for_xhci(&mut self) {
        self.set_level_trigger();
        self.set_vector(0x40);
        self.set_delivery_mode(0);
    }

    fn set_level_trigger(&mut self) {
        self.set_trigger_mode(true);
        self.set_level(true);
    }
}

Am I missing something?
Last edited by tokusan on Wed Oct 21, 2020 3:06 am, edited 1 time in total.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by BenLunt »

tokusan wrote:Hello, I'm developing my xHCI drivers. I reset xHC by following the instructions of the Intel xHCI manual. An interrupt comes from xHCI on my laptop, but no interruption happens on QEMU. I suspect this is caused by my wrong configuration of the MSI-X table because the laptop has only MSI capability for xHCI, and QEMU has only MSI-X capability for it.
I don't read RUST that well, so I can't decipher your code.

Have you set bit 15 in the PCIe Configuration-->Extended Capabilities-->MSIX Configuration WORD to enable MSI-X? With this being said, did you disable MSI with bit 0 in the MSI Configuration WORD?

Also, the comment that QEMU "only" supports MSI-X may be wrong, though I don't think you meant to mean it that way. QEMU supports PIN based interrupts as well.

Ben
- http://www.fysnet.net/the_universal_serial_bus.htm
User avatar
tokusan
Member
Member
Posts: 29
Joined: Mon Feb 24, 2020 10:18 pm
Location: Japan

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by tokusan »

BenLunt wrote:
tokusan wrote:Hello, I'm developing my xHCI drivers. I reset xHC by following the instructions of the Intel xHCI manual. An interrupt comes from xHCI on my laptop, but no interruption happens on QEMU. I suspect this is caused by my wrong configuration of the MSI-X table because the laptop has only MSI capability for xHCI, and QEMU has only MSI-X capability for it.
I don't read RUST that well, so I can't decipher your code.

Have you set bit 15 in the PCIe Configuration-->Extended Capabilities-->MSIX Configuration WORD to enable MSI-X? With this being said, did you disable MSI with bit 0 in the MSI Configuration WORD?

Also, the comment that QEMU "only" supports MSI-X may be wrong, though I don't think you meant to mean it that way. QEMU supports PIN based interrupts as well.

Ben
- http://www.fysnet.net/the_universal_serial_bus.htm
Thanks for your reply. Yes, I set the enable bit of MSI-X message control register. I can't disable MSI because there is no MSI capability. PCI capability pointer points MSI-X capability and the next pointer of MSI-X cap is 0.

And yes, I meant "only" to say QEMU doesn't have MSI capability. I forgot about PIN based interruption.
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by linuxyne »

From qemu's debugprints and traces:

Code: Select all

[email protected]:usb_xhci_runtime_write off 0x0028, val 0x00000100
[email protected]:usb_xhci_runtime_write off 0x0030, val 0x10ce9000
[email protected]:usb_xhci_runtime_write off 0x0034, val 0x00000000
xhci: invalid value for ERSTSZ: 256
xhci: asserted controller error
...
[email protected]:usb_xhci_oper_read off 0x0004, ret 0x00001000  <- USBSTS_HCE
User avatar
tokusan
Member
Member
Posts: 29
Joined: Mon Feb 24, 2020 10:18 pm
Location: Japan

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by tokusan »

linuxyne wrote:From qemu's debugprints and traces:

Code: Select all

[email protected]:usb_xhci_runtime_write off 0x0028, val 0x00000100
[email protected]:usb_xhci_runtime_write off 0x0030, val 0x10ce9000
[email protected]:usb_xhci_runtime_write off 0x0034, val 0x00000000
xhci: invalid value for ERSTSZ: 256
xhci: asserted controller error
...
[email protected]:usb_xhci_oper_read off 0x0004, ret 0x00001000  <- USBSTS_HCE
Thanks! I'll investigate this. BTW how did you print these debug info?
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by linuxyne »

tokusan wrote:BTW how did you print these debug info?
The lines prefixed with numerical info (most likely PID, etc.) can be enabled at the qemu monitor by the command

Code: Select all

trace-event usb_xhci* on
The other two lines (that begin with xhci:) requires rebuilding qemu with an additional -DDEBUG_XHCI entry into its cflags (or equivalently uncommenting the corresponding #define in the source).
User avatar
tokusan
Member
Member
Posts: 29
Joined: Mon Feb 24, 2020 10:18 pm
Location: Japan

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by tokusan »

Thanks. I fixed the value of ERSTSZ, and it seems event ring TRB is generated (QEMU log says that). Still no interruption happens...
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by linuxyne »

- qemu ignores MMIO writes to PBA.
- The vectors are all masked in the msix table.

Edit:

Code: Select all

    fn enable_interrupt(&self) {
        let val = self.registers.get(self.base) | 0xf000_0000;
        self.registers.set(self.base, val);
    }
It seems the above is trying to enable msi-x, but it also sets a mask:

Code: Select all

#define  PCI_MSIX_FLAGS_MASKALL 0x4000  /* Mask all vectors for this function */
#define  PCI_MSIX_FLAGS_ENABLE  0x8000  /* MSI-X enable */
You may want to OR with 0x8000_0000 alone.
User avatar
tokusan
Member
Member
Posts: 29
Joined: Mon Feb 24, 2020 10:18 pm
Location: Japan

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by tokusan »

linuxyne wrote:- qemu ignores MMIO writes to PBA.
- The vectors are all masked in the msix table.

Edit:

Code: Select all

    fn enable_interrupt(&self) {
        let val = self.registers.get(self.base) | 0xf000_0000;
        self.registers.set(self.base, val);
    }
It seems the above is trying to enable msi-x, but it also sets a mask:

Code: Select all

#define  PCI_MSIX_FLAGS_MASKALL 0x4000  /* Mask all vectors for this function */
#define  PCI_MSIX_FLAGS_ENABLE  0x8000  /* MSI-X enable */
You may want to OR with 0x8000_0000 alone.
Thanks. I fixed it. Although no changes occur, I noticed that MSI-X table is in fact not set correctly. I may use bitfield library wrongly. I'll continue investigating.
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by linuxyne »

The way you are trying to generate the interrupt depends on writing to the PBA, and since qemu ignores such writes, that method will not work.

The other fixes are necessary for proper functioning of the driver, but they alone will not be able to overcome the limitation that qemu ignores PBA writes.
User avatar
tokusan
Member
Member
Posts: 29
Joined: Mon Feb 24, 2020 10:18 pm
Location: Japan

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by tokusan »

linuxyne wrote:The way you are trying to generate the interrupt depends on writing to the PBA, and since qemu ignores such writes, that method will not work.

The other fixes are necessary for proper functioning of the driver, but they alone will not be able to overcome the limitation that qemu ignores PBA writes.
I confirmed that we can't use PBA on QEMU, but then I can't understand why because there are lines related to PBA in QEMU's source code (inside hw/pci/msix.c). Anyway, I'll write codes which doesn't depend on the interruption from xHCI. Thank you for spending time for me.
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by linuxyne »

tokusan wrote:I confirmed that we can't use PBA on QEMU, but then I can't understand why because there are lines related to PBA in QEMU's source code (inside hw/pci/msix.c).
Probably because the driver/OS isn't expected to write to PBA; it is managed by the device.
[Edit: PBA is indeed supported by qemu; it just doesn't allow the guest to write to that memory area.]

Here is the patch that added a dummy handler for such writes to PBA.

If you are willing to test, you can change the function msix_pba_mmio_write to call msix_set_pending. That will allow the driver/OS to actually change the bit. [Edit: I tested this change in qemu with your latest fix_no_interrupt_from_qemu branch. The message "INFO: Interrupt from 0x40" is printed.]
User avatar
tokusan
Member
Member
Posts: 29
Joined: Mon Feb 24, 2020 10:18 pm
Location: Japan

Re: xHC interruption with MSI-X on QEMU doesn't work

Post by tokusan »

linuxyne wrote:If you are willing to test, you can change the function msix_pba_mmio_write to call msix_set_pending. That will allow the driver/OS to actually change the bit. [Edit: I tested this change in qemu with your latest fix_no_interrupt_from_qemu branch. The message "INFO: Interrupt from 0x40" is printed.]
Could you show me the edited msix_pba_mmio_write? I tried several codes, but couldn't get an interruption.

(BTW I said that event TRB was generated, but it turned out that this was not true. I misinterpreted QEMU's output. But at least the problem of the interruptions is solved (the way I want to implement is seemingly not supported by QEMU), so I marked this thread as solved.)
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: [SOLVED]xHC interruption with MSI-X on QEMU doesn't work

Post by linuxyne »

tokusan wrote: Could you show me the edited msix_pba_mmio_write? I tried several codes, but couldn't get an interruption.
Below is a hack; it marks the vector# 0 as pending, regardless of the bit the driver/OS wants to
set in PBA. So, for testing, your code must always unmask vector# 0 (which it already does).

Code: Select all

static void msix_pba_mmio_write(void *opaque, hwaddr addr,
                                uint64_t val, unsigned size)
{
	PCIDevice *dev = opaque;
        msix_set_pending(dev, 0);
}
tokusan wrote: ...(the way I want to implement is seemingly not supported by QEMU),...
That's true, but I suppose that that way is only for testing. I do not think that commercial production OS and drivers actually write to PBA. They might read PBA to display interrupt status info. Moreover, if you add a usb-storage device to qemu's xhci bus, and run Linux on the guest, I can see that the xhci driver uses MSI-X, so the MSI-X functionality in qemu does work.
Post Reply