Sunday, February 10, 2013

What is 'Compatibility'?

So why another blog just a week after the last one? Given the aperiodic nature of my earlier blogs, this frequency seems a bit out of character. Well, I finished reading Fowler's Passionate Programmer and he exhorts the reader to practice, practice, practice. Not just code, but writing. And one venue for writing is a web log. No referees, reviewers, publisher, etc between my typing and publication. So here you go, for better or worse.

Now back to the topic of this blog.

There is often talk of 'compatibility' in the computer industry. From the perspective of the platform firmware, we define compatibility as 'the software you choose to run.' In this taxonomy, compatibility is not some absolute term or Platonic truth living outside the world but instead very much a contextual term.

So this is all well and good, but how does this relate to the platform firmware specifically? In the case of the boot firmware, we think of compatibility in terms of the PC/AT BIOS and its interfaces. These interfaces include the 16-bit int-callable interfaces published by the system board BIOS and BIOS extensions for use by booted software. The 'booted software' can include the MBR first stage loader and subsequent system software. The BIOS industry referred to these int-callable interfaces, such as int10h for video, int13h for disk, int16h for keyboard, along with data structures like the BIOS Data Area (BDA) and Extended BIOS Data Area (EBDA) as the "BIOS Runtime." This BIOS Runtime is distinct from the code that executes after a platform restart and the germination of the BIOS runtime interfaces. This phase of execution has been historically lumped under the term Power-On Self Test (POST).

Back in the early 1999 when the Extensible Firmware Interface (EFI) was first being deployed, this phase of execution, or the Boot Services (BS) and subsequent Run Time (RT), were really akin to the BIOS Runtime. This parallel is borne of the fact that the EFI specification, nor its successor the UEFI specification, dictates 'how' these environments appear. The pre-EFI/UEFI phase is opaque to and distinct from the UEFI BS and RT, just as POST is distinct from the BIOS Runtime and its int-calls.

In the case of EFI, the Intel Platform Innovation Framework (aka "Framework") and its successor, the UEFI Platform Initialization (PI) specifications, defined a stylized POST phase. This includes the SEC, PEI, and DXE phases of execution. But just as EFI is distinct from its preceding phase, the PI phase does not necessarily have to create a full UEFI environment. And it is this PI versus UEFI where compatibility comes into play. You see, as EFI and UEFI worked to gain acceptance, the bulk of platforms in the market since 1982 supported PC/AT BIOS and 16-bit BIOS option ROMs and OS loaders.

The CSM provides a way to encapsulate a BIOS runtime inside a Framework or PI-style firmware. The CSM specification abstracts the information collected during a PI-style POST, namely SEC, PEI, and DXE, and passes that information into the CSM wrapped version of a BIOS 16-bit runtime. We often refer to this binary in our edk2-based trees as a CSM16 binary.

Recall that UEFI provides services for blocks such as the EFI Block I/O protocol, and the BIOS supports similar capabilities via the int13h API. To leverage an underlying 3rd party storage option ROM's 16-bit disk services, a UEFI driver can be generically written to thunk or call down into the 16-bit code. The thunk is provided as a general utility via a API member of the CSM protocol. There is also a reverse thunk, but it is rarely if ever used. You see, a CSM16 cannot practically call back into native UEFI 32-bit or 64-bit code because when control is passed to a 16-bit MBR loader, the int15h E820h memory map that the legacy OS receives has the UEFI boot service pages mapped as available. And since there is no equivalent of ExitBootServices() for a legacy OS boot, UEFI services in the form of boot services drivers cannot reliably be invoked via a reverse thunk after the int19h IPL.

EFI/UEFI calling down into PC/AT BIOS CSM16, on the other hand, can be done throughout the boot services phase since the CSM16 services are available until ExitBootServices, so a BiosBlockIo or BiosVideo driver can reliably thunk to the int13h and int10h CSM16, respectively. The only way to have 32-bit or 64-bit native codes that are shareable between CSM16 and a DXE-based UEFI BS is to put the codes into something like a DXE SMM driver. The downside of the latter is that 3rd party UEFI drivers cannot be loaded into SMRAM. This only works for system board hardware, such as USB ports in the chipset.

And now even within EFI/UEFI there are compatibility concerns. EFI1.02 to EFI1.10 introduced OpenProtocol, and implementations of the EFI1.02 HandleProtocol had to use the later reference-counted API to implement the earlier non-reference counted API. Then UEFI2.0 in 2006 deprecated UGA and DeviceIo. From UEFI2.0 to 2.1, then 2.2, 2.3, 2.3.1 have introduced new services. Framework HII to UEFI HII. A style of programming to maintain compatibility often includes reading the specification version in the system table in order to take the appropriate code path. The same holds true for PI. PI became with the donated Intel Framework specifications, and from Framework 0.9x to PI 1.0, 1.1, 1.2, 1.2.1, 1.2.1a, etc there have been similar concerns with SEC, PEI, DXE, SMM.