OSDev.org

The Place to Start for Operating System Developers
It is currently Fri Aug 07, 2020 9:23 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 2 posts ] 
Author Message
 Post subject: QEMU and xHCI
PostPosted: Sun Jan 12, 2020 7:51 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 667
Location: USA
Hi guys,

I am finally starting to get back to this hobby of ours, and I decided to check my code with QEMU. I use Bochs mostly, simply because I have been using it for so long and have gotten quite intimate with its code several times :-)

I checked UHCI, OHCI, and EHCI and all my tests and code worked as expected. However, when I checked xHCI, it failed in a few places. After spending some time looking, I found three errors with QEMU. Therefore, I thought I would post here just to let you know that if you have been experiencing these same issues with your code, you can relax, it is a QEMU thing, not your code.

The first isn't really an error, more of a hard coded item. QEMU hard codes the Event Ring Segment Limit to one. i.e.: It does not have code to have more than one segment in an Event Ring. This isn't really an error, but more of a feature that I think should be added. I only found this out looking through other code.

The second is a bug. When your code has processed an Event TRB, it is to write to the xHC_INTERRUPTER_DEQUEUE register the address of this processed TRB, clearing the EHB bit and (optionally) writing the DESI value to the bottom three bits. This tells the controller that you have processed this Event. In my tests, QEMU was sending two interrupts, one for the initial Event TRB and then a spurious interrupt. Come to find out, QEMU checks to see that this value you write to the xHC_INTERRUPTER_DEQUEUE register matches where it thinks that TRB should be. However, QEMU has already incremented this value and the test is false, telling QEMU to send the interrupt again. I have reported the error at https://bugs.launchpad.net/qemu/+bug/1859359. Therefore, if you have been receiving two interrupts for each one Event, this is the problem.

The third item isn't really a bug, but is a certain problem that caused my code to not work. For those of you whom are familiar with USB, when you send a CONTROL transfer, you send a SETUP packet, send/receive zero or more DATA packets, then send a STATUS packet. Since the STATUS packet is used to indicate a successful transfer, it really isn't to be sent until the transfer is actually successful. Therefore, on an xHCI transfer ring, you really should only insert the SETUP TRB and zero or more DATA TRBs waiting for each one to successfully be transferred (setting aside short packets, etc). Once you have successfully received and/or sent all of the packets, you then send the STATUS TRB.

An example:
Code:
Insert SETUP
Insert DATA(s)
Ring Doorbell
Wait for Success/fail
IF Success THEN
    Insert STATUS
    Ring Doorbell
ELSE
    Clear STALL/ERROR
ENDIF

The issue here is that the current QEMU code checks to see that there is a STATUS TRB on the ring *before* it starts the CONTROL transfer. In my opinion, this is in error, though reading the specifications, this is acceptable, though if the controller does do this, it *must* send an EVENT TRB if found in error. QEMU is not doing this. I have reported this as well: https://bugs.launchpad.net/qemu/+bug/1859378

Once I ignored the spurious interrupt, which actually causes the xHC_INTERRUPTER_DEQUEUE to advance (in error by the way), and now do not check for a success/fail before sending the STATUS TRB, my code works just fine with QEMU.

I post this information here simply to show that if you are experiencing the same things, you now know why. I do not post trying to bash QEMU in any way. I use QEMU and have enjoyed using it for some time.

Anyway, just for your information. Hopefully someone can add these features/bug fixes to QEMU soon.

Thanks,
Ben
- http://www.fysnet.net/osdesign_book_series.htm


Top
 Profile  
 
 Post subject: Re: QEMU and xHCI
PostPosted: Mon Jan 13, 2020 7:33 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 667
Location: USA
BenLunt wrote:
The second is a bug. When your code has processed an Event TRB, it is to write to the xHC_INTERRUPTER_DEQUEUE register the address of this processed TRB, clearing the EHB bit and (optionally) writing the DESI value to the bottom three bits. This tells the controller that you have processed this Event. In my tests, QEMU was sending two interrupts, one for the initial Event TRB and then a spurious interrupt. Come to find out, QEMU checks to see that this value you write to the xHC_INTERRUPTER_DEQUEUE register matches where it thinks that TRB should be. However, QEMU has already incremented this value and the test is false, telling QEMU to send the interrupt again. I have reported the error at https://bugs.launchpad.net/qemu/+bug/1859359. Therefore, if you have been receiving two interrupts for each one Event, this is the problem.

After some research and communication, I have found the reason for this spurious interrupt. It actually isn't spurious afterall. QEMU is checking to see if the Event List is empty after every Event TRB is completed. Don't know why QEMU does this, but it does. On a mis-matched cycle bit, your code (the Consumer) is to update the xHC_INTERRUPTER_DEQUEUE register with the current TRB location *without* incrementing your internal Dequeue Pointer. This was my error, I was incrementing my internal Dequeue pointer. A simple code modification fixed this.

BenLunt wrote:
The third item isn't really a bug, but is a certain problem that caused my code to not work. For those of you whom are familiar with USB, when you send a CONTROL transfer, you send a SETUP packet, send/receive zero or more DATA packets, then send a STATUS packet. Since the STATUS packet is used to indicate a successful transfer, it really isn't to be sent until the transfer is actually successful. Therefore, on an xHCI transfer ring, you really should only insert the SETUP TRB and zero or more DATA TRBs waiting for each one to successfully be transferred (setting aside short packets, etc). Once you have successfully received and/or sent all of the packets, you then send the STATUS TRB.

I still think this is in error. However, I seriously doubt that anything will be done to QEMU to fix it.

Anyway, I learned something today. I continue to learn something each day and that is what makes this hobby of ours so enjoyable.

Thanks,
Ben
- http://www.fysnet.net/osdesign_book_series.htm


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 2 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Google [Bot] and 12 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group