BenLunt wrote:foliagecanine wrote:By the way Ben, I don't think your GDevDesc xHCI program works on QEMU:
Code: Select all
qemu-system-i386 USB/freedos_hd.img -device qemu-xhci,id=xhci -device usb-mouse,bus=xhci.0
Code: Select all
GD_xHCI -- xHCI: Get Device Descriptor. v1.10.20
Forever Young Software (C)opyright 1984-2016
PCI: Found a USB controller entry: Bus = 0, device = 4, function = 0
Found xHCI controller at 0xFEBB0000
C:\>
I will have to have a look and see. I am sure it did at the time I tested it, but things change.
Thanks,
Ben
Hi,
I remember now why I didn't mess with xHCI and QEMU. QEMU doesn't allow non-dword aligned mem-mapped reads (or writes) to the capabilities/operational registers or to the extended capabilities area.
For example, reading the controller Version Register in the Capabilities Registers, requires a dword read from offset 0x00000000 and then a shift by 16 and then a (redundant) AND 0xFFFF. Real hardware allows you to read a WORD from offset 2 to get the version.
Real Hardware:
Code: Select all
#define xHC_CAPS_CapLength 0x00
#define xHC_CAPS_IVersion 0x02
size_t bar = base_address_of_capabilities_registers;
version = * (bit16u *) (bar + xHC_CAPS_IVersion);
QEMU:
Code: Select all
#define xHC_CAPS_CapLength 0x00
#define xHC_CAPS_IVersion 0x02
size_t bar = base_address_of_capabilities_registers;
version = * (bit32u *) (bar + xHC_CAPS_CapLength);
version = (version >> 16) & 0xFFFF;
Second, when reading from the Extended Capabilities area (which is in the mem-mapped area of the xHCI, not the PCI config space), you need to read bytes and words to parse the port protocols. QEMU returns zero's for all non-dword aligned reads. The demo code you speak of does not pair up the ports correctly because of this.
My OS code has a work-around for this by reading in all of the Extended Capabilities registers as dwords into a memory buffer, then parsing the buffer using byte and word accesses. The demo code you speak of does not.
This is really something every newbie here needs to know about QEMU.
For example, do the following:
Code: Select all
#pragma optimize( "", off ) // see note below
struct FOO {
bit8u offset;
bit8u resv;
bit16u version;
};
struct FOO *bar = (struct FOO *) BASE_ADDRESS_RETURNED_BY_XHCI_BAR;
printf(" %02X %04X\n", bar->offset, bar->version);
Note that it prints the correct offset value (0x40 in QEMU), but the version is 0000.
However, now do the following:
Code: Select all
printf(" %04X\n", (* (bit32u *) &bar->offset) >> 16);
The version will now be printed correctly.
PLEASE NOTE: For this example to work, you have to have optimizations off. If not, the above code will be optimized to:
Code: Select all
// this line of code:
printf(" %04X\n", (* (bit32u *) &bar->offset) >> 16);
// will be optimized to:
printf(" %04X\n", (* (bit16u *) &bar->offset + 2);
// and since it is reading a non-dword aligned value, it will still print zero.
Therefore, to get around this, you must always read a dword-aligned dword from the XHCI registers when using QEMU. ALWAYS.
Ben
P.S. Don't get me wrong, I think QEMU is an excellent emulator and use it often. However, if you have been using emulators and have been programming hardware as long as I have, you tend to find errors in these things. I have commented this error to the QEMU maintainers, but since Linux works as is, they didn't have any desire to make my corrections. Oh well, nothing is perfect, especially me.