In a previous article, I wrote about using Intel Direct Connect Interface (DCI) to debug the TianoCore MinPlatform source tree available on the AAEON UP Xtreme Whiskey Lake board. This is a fantastic platform for learning about the UEFI codebase. In this blog, I’ll describe the steps needed to get this target to boot to the UEFI shell, so that Satoshi’s Type 1 hypervisor and an OS kernel can also be debugged; but also with full UEFI source code!
In the blog JTAG Debug using DCI on the AAEON UP Xtreme Whiskey Lake board, the individual steps needed to get the board into a state where you can debug it were described:
- Build the TianoCore MinPlatform image with source and symbols
- Modify the image to enable DCI
- Modify the image to create a deadloop at the reset vector
- Flash the target
As mentioned near the bottom of that article, a deficiency of this build is that the AAEON Whiskey Lake board won’t boot all the way to the UEFI shell. It gets up past DXEMAIN, and you see the “Press F2” prompt on the monitor, but then the platform wedges in a deadloop.
After some investigation, we found that the issue here is that the MinPlatform build for this target is resetting the USB ports once it gets to the F2 prompt. And, since DCI DbC3 uses the USB port, we drop the debug connection, and SourcePoint goes into the weeds. At that point, it’s impossible to recover.
This is fine if your intent is only to debug UEFI. It’s obviously not good if you want to run a hypervisor on top of UEFI, or even better debug an operating system kernel, such as Windows or Linux.
Digging into the code, here are the step-by-step instructions for stopping the DCI USB port from being reset and keeping SourcePoint alive through the BDS transition:
Step One is to modify \UpX\edk2\MdeModulePkg\Bus\Pci\EhciDxe\Ehci.c
The before and after shots (left and right) are below:
You can see we added the following line to the top of the EhcReset procedure:
if ((Attributes & 0xF0) == 0) return EFI_SUCCESS; /// ASSET
This is what we wish to happen; to avoid resetting the controller, at least for our purposes here.
There is one trick; the compiler “knows” that we will exit right away, so we have to add some code below in the “switch” statement, so the rest of the function won’t be optimized away:
We tossed in an additional statement defining the “attrib” variable. It took Attributes, AND’ed it with x’F0’, and then OR’ed the result with EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG;
UINT16 attrib = ((Attributes & 0xF0 | EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG); /// ASSET
We then switch on our new variable attrib, as opposed to based upon the Attributes parameter. And if you look lower down in the case statements, this results in essentially a NOP:
case EFI_USB_HC_GLOBAL_WITH_DEBUG: // nothing happens here; just exits the case statement.
And then, we need to make similar changes to Uhci.c, Xhci.c, and XhciReg.c. Below are all of the changes I made:
This might not be the most elegant approach, but, hey, it works!
We then rebuild the image with these changes as described in JTAG Debug using DCI on the AAEON UP Xtreme Whiskey Lake board, and voila, we can boot to the UEFI shell!
After that, we can exercise SourcePoint’s hypervisor debug capabilities, as described for example in Debugging Type 1 Hypervisors on the UP Xtreme i11 using JTAG/DCI, and my Webinar recording: DCI debug of UEFI and hypervisor technologies on the AAEON UP Whiskey Lake and Tiger Lake boards. But this time, with full UEFI source and symbols! The Whiskey Lake board has the advantage over the Tiger Lake board, in that you can debug with source for both UEFI and Satoshi’s hypervisor. The interactions between the two are fascinating. In my opinion, this provides an unparalleled capability to understand low-level firmware AND hypervisor technologies. And sets the stage for debugging the OS kernel. I’ll write about that topic soon.