Sunday, June 7, 2015

GUIDs, Revisions, Interrupts

This blog will cover a few topics, including GUIDs, Revisions, Hardware Interrupts, and Portable Libraries.

GUID versus Revision
To being, I was recently asked about how one should evolve a protocol interface. There are two ways to extend an interface, including: 1) have a revision field that designates if the service set or data has been extended, and 2) define a new protocol GUID.

The first technique will be familiar with some of the original EFI1.02 style API's, such as the EFI_BLOCK_IO_PROTOCOL and the various service tables, such as the EFI System Table, PEI Service Table in the PI specification, etc. For this technique, the service table or protocol can be extended in a back compatible fashion by appending new services to the end of the table while at the same time increasing the revision number.

This leads to a programming technique wherein the caller locates the protocol or service table and has to check the revision to see if the revision is greater than or equal to a number that matches the industry standard. An example of this technique in action includes the EFI_PEI_RESET2_SYSTEM in the recently published PI1.4 specification http://www.uefi.org/sites/default/files/resources/PI_1_4.zip. If a calling PEIM wants to use this service but also maintain portability across PI1.0 through PI1.3 conformant systems, the caller would only invoke the new PI1.4 service if PEI Service table revision greater than or equal to 1.40.

This first technique is required for the service tables, but for protocols the preferred extension method is to define a new GUID. Although the original EFI Protocols featured the revision field, and protocols like EFI_BLOCK_IO_PROTOCOL have been extended via the revision field, all other protocols have been evolved via new GUIDs. This can include the EFI_SIMPLE_TEXT_INPUT to the EFI_SIMPLE_TEXT_INPUT_EX change, but more often the growth is seen via appending a '2' to the original protocol, such as in EFI_LOAD_FILE2_PROTOCOL, EFI_DRIVER_DIAGNOSTICS2_PROTOCOL, EFI_COMPONENT_NAME2_PROTOCOL, EFI_FORM_BROWSER2_PROTOCOL, etc. These can be found in the UEFI 2.5 specification http://www.uefi.org/sites/default/files/resources/UEFI%202_5.pdf.

The nice thing about the second technique is that the caller doesn't have to do the cumbersome revision check, PI variants can include the proper name in the dependency expression, and API bug fixes can span all of the services. In addition, the producer of the protocol can produce the original and '2' variant quite easily by sharing implementations of the common services in a single driver.

It turns out hardware interrupts are used in UEFI, including at least the hardware timer tick. But as table 22 of the UEFI 2.5 specification notes, the system may choose to implement "firmware interrupts" between TPL_NOTIFY and TPL_HIGH_LEVEL.

The table says "This level is internal to the firmware."

This means that the firmware must adhere to the TPL mapping in the specification in order to maintain interoperability, viz.,

And if you look at the DXE core implementation, a TPL level is really just a linked list. So the way that a hardware interrupt protocol driver could be implemented would be to use the CPU architectural protocol to register interrupt service handlers (ISRs) with the SOC, using something like a programmable interrupt controller's priorities to map TPL_DEVICE_1 to lower priority devices and TPL_DEVICE_MAX to higher priority. Think low-speed consoles like a UART to the former and a high-speed networking device to the latter.

The implementation of the ISR would be similar to the top half http://www.makelinux.net/ldd3/chp-10-sect-4 of a Linux driver, namely just enough code to quiesce the device that triggered the interrupt and then signalling an event to invoke a lower TPL handler, or something like a bottom half of a Linux driver. Another technique is to invocation of a Deferred Procedure Call (DPC). The motivation for the DPC-like logic is to do most of the long-lived processing at a lower TPL than the interrupt in order to allow for other activity to be interleaved and to have the richer service set of a lower TPL, as shown in the Table 23 "TPL Restrictions" of the UEFI 2.5 specification.

So we see that the plumbing is in place to have hardware device interrupts, but the reason that a UEFI specification conformant driver cannot depend upon hardware interrupts, beyond the implicit timer tick for the timed event services, is that the DEVICE TPL mapping above is not codified by the UEFI specification. Each vendor may provide different TPL mappings for the range between NOTIFY and HIGH_LEVEL. In addition, there is no API like IoConnectInterrupt which abstracts the use of TPL's and managing of the IO interrupt controller from a given UEFI implementation to a portable UEFI driver.

This doesn't stop a vendor who provides the full UEFI implementation, such as a EDK II-based DXE core and platform drivers, from providing this capability and a customer interrupt protocol, of course. You could also retrofit an existing polled UEFI driver to be modal, namely have the driver entry point looks for the platform interrupt protocol and register an ISR if it exists, and if it doesn't default to the existing polled behavior that is most broadly compatible.

And given that EFI has been shipping for the last 15 years with the present polled driver model, there is some question as to whether hardware device interrupts are necessary. For boot scenarios where the pre-OS is typically doing a single activity, such as accessing an I/O device, the polled model suffices. When there are performance concerns, the system designer can vary the system timer tick, sometimes going as low as 1ms for the timer period in order to service the device actions. Today, performance sensitive drivers like the Pxe basecode driver aggressively poll the underlying network API's in order to maintain line-rate.

The only cracks in the armor for this model appear when different I/O stacks interact. For example, if we are performing a network download into a memory buffer, only the networking stack is in play. But if the networking download interleaves packet transition with writes to a durable storage media, then the networking stack and file system storage stack compete for resources. This is an area where the polled model can observe system performance challenges.

As we enhance the scenarios with the recent UEFI HTTP API's and other capabilities, it will be fun to watch this space.

Libaries
Speaking of spaces to watch, another area that interests me is portable libraries. Specifically, the MdePkg of EDK II has different library classes, including 'base'. The nice thing about a library of type base is that I can use the code in PEI pre-memory, PEI post-memory, DXE, DXE SMM, UEFI boot service, UEFI Runtime, and SEC. You can see from the last sentence that firmware programming in the UEFI PI world is pretty challenging. There are seven regimes to write code, and business and technical reasons sometimes dictate moving code from one area to the other, such as re-using the UEFI FAT driver to create a PI recovery PEIM to load a recovery FV from disk, or moving SI code from DXE to PEI for purposes of creating an Intel (R) Firmware Support Package (FSP) binary http://firmware.intel.com/sites/default/files/resources/A_Tour_Beyond_BIOS_Using_the_Intel_Firmware_Support_Package_Version_1_1_with_the_EFI_Developer_Kit_II.pdf.

The business value is the fundamental logic in the C code, not the syntactic sugar around the code to make it a SMM driver or a service processor task in the RTOS/process in the OS.

TXT and UEFI Secure Boot & Measured Boot
We just left left off talking about adjacent technologies of UEFI PI and service processors. Another adjacent and quite complementary technology includes UEFI PI and Trusted Computing, including Intel(R) Trusted Execution Technology (TXT). I sometimes get asked about this so I thought that I'd spend a couple of moments on this topic I gave a quick overview of UEFI Secure Boot and Measured Boot using a Trusted Platform Module (TPM) on open hardware at http://firmware.intel.com/blog/security-technologies-and-minnowboard-max, but I omitted TXT since this open platform's Intel Baytrail CPU doesn't support those extensions. For hardware that does support TXT, such as Xeon class CPU's and client VPro, though, the relationship bears mentioning.

If you do the reference chasing on the latter link, though, you'll see that the UEFI Secure Boot and the Static Root of Trust for Measurement (SRTM) are a blended scenario, with the latter using the non-resettable, static PCR's 0..7 for the pre-OS, and PCR's 8..15 for the OS. This scenario can co-exist with TXT, such as in slide 10 https://01.org/sites/default/files/openstacksummit_vancouver_trusteddockercontainers.pdf where the "BIOS" here is the early PI code that loads SMM, and the latter BIOS with option ROM's falls under the purview of UEFI Secure Boot. T-Boot is a type of Measured Launch Environment (MLE) https://www.kernel.org/doc/Documentation/intel_txt.txt and using SENTER instruction will activate measurement into the resettable PCR's above PCR15. As such, it provides a Dynamic Root of Trust for Measurement (DRTM) alongside the SRTM. And for purposes of attestation, having more platform elements in the attestation vector provides a richer management experience.