Irregular Behavior in QEMU

I have recently returned to this hobby of mine, working with the USB and updating my notes for the next intended release of my book.

However, with some of my tests, I was seeing irregular behavior and had to investigate. With a little work, I found the mistake and remembered that QEMU functions a little different than I expected.

For this reason, I thought I would list, at least the two major behaviors, and possible later add to this list.

Now, don't get me wrong. I think QEMU is a wonderful emulator and use it often. I am simply listing what I think is irregular behavior with QEMU and its USB emulation, so that others whom might be having difficulties may make sure this behavior isn't holding them back.


Non-DWORD access to xHCI registers (observed) (observed)

The xHCI specification, version 1.0, section 5.4, states:

"Unless otherwise stated, all [Host Controller Operational] registers should be accessed as a 32-bit width on reads with an appropriate software mask, if needed. A software read/modify/write mechanism should be invoked for partial writes."

However, it does not say anything like this for the Host Controller Capability registers, therefore these registers might and could be accessed by BYTE, WORD, or DWORD reads (no writes allowed since they are read-only registers).

A long time ago, while I was just learning the USB, I ran into this very issue. I wrote some code that I thought was reading a mem-mapped register as a DWORD.

struct S_FOO {
  some member;
  DWORD  bar;
  another member;
};
volatile struct S_FOO *foo = (struct S_FOO *) BASE_ADDR;
if ((foo->bar & 0x000000FF) != 0x10) return 0;

However, the compiler I was using, optimized the compiled code to:

xxxxxx00  B8xxxxxxxx  mov eax,BASE_ADDR
xxxxxx05  8A10        mov dl,[eax]
xxxxxx07  80FA10      cmp dl,byte 10h

Notice that the actual read from the mem-mapped register is now a BYTE read, not a DWORD read.

Since the Host Controller Operational Registers require DWORD reads and writes--and you might as well do the same for the Host Controller Capability Registers--be sure to read as DWORDs, masking off any portion you need, and write as DWORDs using a said read/modify/write mechanism.

As for QEMU's irregular behavior, *both* register sets *must* be accessed as DWORDs. Real hardware may allow either set--more likely the Capability registers--to be accessed with smaller sizes, though QEMU does not.


SETUP/DATAx/STATUS Control Transfers (submitted)

Normally, when sending transfers via the Control Pipe, you send the SETUP and zero or more DATA packets across the wire. Once this is successful, you then send the STATUS packet.

For example, if the pipe stalls at the SETUP or any of the DATA packets, there is no need for the STATUS packet.

However, QEMU's xHCI emulation checks that there is a STATUS packet before it executes the SETUP packet and fails if there is no trailing STATUS packet.

This is actually allowed via the xHCI specification. However, the specification states in section 4.11.2.2, page 159, that if this check is indeed made, that if it finds that the STATUS packet is not present, the controller must generate a Transfer Event for the TRB with an TRB Error Completion Code.

The xHC is NOT required to check for the following Control transfer error conditions. If system software is properly designed these error conditions will never occur. However if the xHC does check for these conditions it shall generate a Transfer Event for the TRB that the error was detected on with the Completion Code set to TRB Error.
  •  If a Status Stage TD does not follow a Data Stage TD.
  •  If the Setup Stage TRB defines a Length not = 8.
  •  If the Status Stage TRB defines a Length > 0.

The first bullet in that list identifies that an Transfer Event with an TRB Error Completeion Code must be generated due to the fact that QEMU does indeed make the check.

Therefore, make sure that all Control Pipe Transfers use a single Transfer Descriptor (TD) that encompasses all SETUP, zero or more DATA, and the STATUS packets, all within one TD. If you place the SETUP and DATA packets in one TD, then the STATUS packet as the next TD, QEMU will fail the transfer.


Please see next months blog for more irregularities.

Again, I am not trying to say anything bad about QEMU or its USB implementation. I am simply listing a few items that may or may not influence your code, or find that your code doesn't work as expected due to these irregular behaviors.

I wrote the original UHCI, OHCI, and xHCI emulation for the Bochs emulator, and I am in no way stating that it is perfect. It has its irregular behaviors as well, though I try to fix them as I find them, or as they are pointed out to me.