UEFI News and Commentary

Friday, September 27, 2013

UEFI 2.4 Review, Part 15: Capsule on Disk

This article is the fifteenth in a series reviewing the changes in the UEFI 2.4 specification. This time we take a look at a new standard way for capsules to be delivered to the pre-OS environment: on disk.

Going back in history, capsules have been used for various things. Operating systems have tucked away their BSOD information in a capsule, so they could recover it on next boot, even if the debug information couldn't be displayed. But by far the most common usage is to tuck away a firmware update for the pre-OS to deal with on the next reboot.

There's one small problem with that: where does the firmware put the capsule? Well, in some cases, the underlying memory controller would guarantee that the memory contents would persist across certain types of reset. This would allow the firmware to pick up the capsule on the next reset, and mark its memory location as reserved so it would not be stomped on until the appropriate application or driver could get ahold of it. Others hid it away in System Management RAM (SMRAM) or even in the belly of an embedded controller.

In other systems, where the memory holding the capsule could not guarantee that the memory would be valid, some other solution was needed. In UEFI 2.4, there is now the option to store a file on the system partition of the boot disk, in the /EFI/UpdateCapsule directory and set a bit in the OsIndications EFI variable.

On the next boot, the UEFI firmware will detect that bit set and will search for any files within the boot device's system partition and try to load and validate them, in alphabetical order. Once they have been processed, they are deleted (if possible) and the system is reset.

But wait? How do I know what happened when the capsule was processed? Well, there is now a handy-dandy CapsuleNNNN EFI variable that is created to describe both what was processed and the resulting status. Possible results include success (of course), invalid capsule, device error, access denied, unsupported capsule type, no valid driver in the capsule, or there wasn't enough memory to process the capsule. This mechanism shouldn't be confused with Microsoft's own special ACPI table, the ESRT table (little public information available, but here's a hint) which reports the results of firmware updates.

So with UEFI 2.4, the operating system (and related system software) can pass large quantities of data back to the pre-OS for processing without worrying about the size. Much more flexible, and better yet, its a standard.


P.S. A lot of credit for the capsule updates in UEFI 2.4 goes to my Insyde colleague, Jeff Bobzin. He produced a public demonstration of these technologies with Terry Kirch of Emulex at the most recent IDF.

Tuesday, September 24, 2013

Zork on UEFI

Leave it to Matthew Garrett to find some extra time on his hands, and then decide to use it to port Zork (or rather, the Frotz Z-machine interpreter) to UEFI. He wrote about his experiences on his inimitable blog and, during the process, highlighted some of the on-going issues with writing applications for UEFI.

Matthew highlights the fact that the standard C library included in EDK2 only supports UEFI Shell applications, not UEFI applications running directly on UEFI or UEFI drivers. This makes porting code more difficult than necessary, since I've struggled to share code between UEFI drivers and applications, even going to the extreme of writing my "driver" as a UEFI application.

Of course, he just updated Frotz to be a UEFI shell application, and then launched it. This turns out to be trickier than it sounds, since the UEFI shell doesn't set the current working directory where he expected it.

As anyone who follows this blog knows, I am a great fan of stretching UEFI into new areas, like text adventures and utility C libraries. More on that in the next few weeks.


Monday, September 23, 2013

UEFI Gains New Members, Touts Their New White Paper

Back on September 18th, UEFI Forum posted a press release (found here) talking about how the number of adopting companies had topped 250, including Huawei, the Linux Foundation and Novell. They also mentioned their new white paper "UEFI Secure Boot in Modern Computer Solutions" by Brian Richardson and my former colleague, Dick Wilkins which gives an excellent overview of the state of the industry on this sometimes controversial topic.

The Linux Foundation news plays well with the fact that the UEFI Plug Fest was co-located with LinuxCon in New Orleans this past week. Linux-as-business has generally come to terms with UEFI and its secure boot strategy. Linux-as-religion may never come to terms with it. Every major Linux distribution supports UEFI secure boot, including SUSU Labs, who spoke at the Intel Developer's Forum two weeks ago, describing their infrastructure for extending secure boot from the UEFI hand-off up through loading the kernel.

What does this really mean? UEFI continues to expand the range of platforms and operating systems on which it can create a powerful pre-OS boot environment. And the UEFI specification is making orderly progress in addressing the needs of the people who ship systems and those who produce code that runs on those systems.

UEFI 2.4 Review, Part 14: Interruptible Driver Diagnostics

This article is the fourteenth in a series looking over the changes introduced in the UEFI 2.4 specification. This time we take a look at the new support for interruptible driver diagnostics.

The Driver Diagnostics protocol, in its current form, has been around since the UEFI 2.0 specification. The earlier version, found in the EFI specification, was identical other than the way in which supported languages were listed in the SupportedLanguages protocol member. So this protocol has been around a while, and a number of drivers already support it.

But some types of diagnostics don't finish quickly. In fact, some may take quite a long time to complete. When a diagnostics application or the built-in setup application in the firmware tries to use these, it would like to provide some sort of status indicator to show that, in fact, the diagnostics still continue. In addition, some managing diagnostic applications can use timers to detect when a device's diagnostic function has hung to give control back to the user. But it is hard for these applications to detect the difference between a really long diag run and a hang.

The UEFI 2.4 specification helps with this. First, it allows a device's diagnostic function (RunDiagnostics()) to return the EFI_NOT_READY status code, indicating that diagnostics have begun and are on-going. A managing diagnostic application that receives this error code can continue to call the RunDiagnostics() function over and over again, until the function either returns another error or returns EFI_SUCCESS. But at least it knows that the diagnostics have not hung.

In addition, a new DiagnosticType (EfiDriverDiagnosticTypeCancel) allows the managing application to cancel an on-going diagnostic. So, if the user hits Ctrl-C to break, and the managing diagnostic application knows that there is work on-going, it can tell the driver to terminate the diagnostic activity cleanly.

These simple changes in the UEFI 2.4 specification make providing device driver diagnostic capability more robust and responsive.

Saturday, September 21, 2013

UEFI 2.4 Review, Part 13: Hash of Certificates Used for Secure Boot Revocation

This article is the thirteenth in a series describing the changes introduced in the UEFI 2.4 specification. This time we focus on improvements to the secure boot technology. Specifically, the updates make it easier for UEFI firmware to be notified about malware UEFI drivers and applications.

In the UEFI 2.2 specification, the idea of a white list and a black list was created. Drivers whose signatures were found in the white list would be allowed to load and those found in the black list were not loaded. The white list signatures were recorded in the EFI variable "db" and the black list signatures were recorded in the EFI variable "dbx".

In the UEFI 2.3 specification, the list of signature types was expanded to allow X.509 certificates, which allowed UEFI drivers and applications to be signed by trusted certificate authorities so that anyone who was signed by that certificate authority could be loaded by UEFI firmware. There were other enhancements to allow new, authenticated EFI variable contents to be appended to an existing EFI variable without having to resign the entire EFI variable. This made distributing updates from, say, an OS or driver vendor, easier once they were already trusted.

But one of the problems with adopting the X.509 certificate was size. Each certificate required 1-2KB. This might not seem a lot in the age of terabyte drives and gigabyte DRAMs, but for UEFI firmware it is a lot to set aside just for security. In particular, as malware becomes more and more real, the need to add more UEFI drivers or UEFI driver-signing certificates to the black list started creating concerns about how much space would need to be set aside and, in some cases, when the space was full, UEFI firmware would be in the unfortunate situation of having to reject new black list entries.

Now, in the UEFI 2.4 specification, three new types of signatures for the black list were added. Specifically, the various recommended forms of the To-Be-Signed hash value (160, 256 and 512-bits) which are created during the creation of a certificate could be added to the black list instead of the certificate itself. Since it is very hard to create another certificate which corresponds to the same hash value, it is nearly identical, security-wise to use the hash value instead of the certificate itself. The bonus of this approach is that hash values for certificates range in size from 20- to 64-bytes, a big improvement over the 1-2KB needed for the certificate itself.

As an added bonus, these new certificate types also have a timestamp field, which allows the UEFI firmware to determine whether the UEFI driver or application was signed before the revocation date of the certificate. The revocation date is the time at which the certificate was identified as having been compromised. By checking the time stamps of the certificates in the UEFI drivers or applications against the time stamp of the certificate's revocation. If a UEFI driver or application was signed before that date and time, then it can still be loaded by the UEFI firmware since the certificate had not yet been compromised. If the UEFI driver or application was signed after that date and time, it might be malware and should be rejected. This generates  less thrash for the IT department or OEM in having to update every single executable image ever signed by that certificate.

The timestamps used in these certificates are created and compared by procedures described in RFC3161. With this information, the time stamps can be compared with an integer compare.

Then the question arises: who gets to generate the time stamps? Well, it turns out that there are time stamp authorities, similar to certificate authorities, that issue signed time stamp certificates. When checking the time stamps, their authority must have its signature registered with the UEFI firmware, in a new authetnicated EFI variable called "dbt".

UEFI OS' must be able to get the updates to the UEFI firmware in the field so that, as more malware comes along, and more certificates are compromised in some fashion, the UEFI firmware will be up to date and able to handle these threats, well after the systems are shipped and used in production environments. UEFI 2.4 keeps the industry ready for the ever-evolving challenges of securing the boot, from reset through OS runtime.


Thursday, September 19, 2013

UEFI 2.4 Review, Part 12: Random Number Generator Protocol

This article is the twelfth part in a series describing the changes in the UEFI 2.4 specification. This article discusses the new Random Number Generator protocol, which provides random numbers for use in applications or as an entropy seed for other cryptographic purposes. In cryptography, random numbers are used for key generators, as the salt value in certain signature schemes and nonces.

In the pre-OS this can be very important, since the system has not been running for very long. Software random number generators (sometimes called pseudo-random number generators) give a good distribution of returned values of the total possible range of values, but need a reliably random starting or seed value. Unfortunately, in the pre-OS when UEFI is running, random numbers are needed before a sufficiently random seed value can be found. Hardware random number generators can provide a good distribution, compensating for biases in the measuring process.

The Random Number protocol in UEFI 2.4 allows you to select the source of the random number from a list of random-number algorithms. Some of these are standard. For example, NIST SP 800-90 provides a series guidelines for ensuring adequate range and randomness for software random number generators. And other NIST recommendations detail an algorithm based on ANSI X9.31. Or you can define your own by generating and returning your own GUID. In the case of hardware random number generators, there is a default GUID (EFI_RNG_ALGORITHM_RAW) which gets the raw value from the hardware or NULL, which uses the driver's default algorithm, hardware or software.

Random number generators provide the basis for any number of security-related algorithms, including the network traffic. By providing access to a high-quality random number generator in the pre-OS, UEFI paves the way for handling that network traffic securely.

UPDATE: Appendix A of the UEFI 2.4 specification also indicates that the last 48 bits of the GUID can be optionally filled in with a random number. Extracting any 48 bits from a generated random number provided by this protocol would work.

Monday, September 16, 2013

UEFI At IDF13, Part 3: Using UEFI for Secure Firmware Update of Expansion Cards

The third presentation at IDF related to UEFI was titled: "Using UEFI for Secure Firmware Update of Expansion Cards" presented by Brian Richardson, Jeff Bobzin and Terry Kirch.

With the UEFI 2.3 specification, the contributors recognized that there are lots of devices in the platform that might need their firmware image updated. Add-in cards, embedded controllers, BMCs, and sensor hubs have flash. If they have flash, then they probably want to update the contents of that flash. So the specification allowed each device driver to publish the Firmware Management protocol (chapter 32) which gave the driver a way to publish its ability to handle firmware updates and validate their version and contents. But there was still something lacking: how can an OS application update these flash images in a standard, secure way?

This presentation highlighted the security issues with firmware update of the motherboard or the add-in devices. If you leave the flash device unprotected while the OS is running, malware can attack it. So there must be security. And, if you want updated firmware images to be distributed in such a way that they can be queued for update, there has to be a standard, especially while the OS is running.

If this is true, then there needs to be some way for the OS application to pass back the new firmware image to the pre-OS environment, and then a way for these firmware images to be passed to the correct instance of the Firmware Management protocol.

The UEFI 2.4 specification introduced the Firmware Management capsule. Capsules are buckets of data associated with a type that the OS or OS application can pass back to the system firmware using the UpdateCapsule() runtime service. After a reset (when the flash protection is disabled and control is handed back to the firmware), the capsules are passed to the correct instance of the Firmware Management protocol.

Jeff, Terry and Brian went over this and then highlighted the best practices for using this new capsule type. They also identified three scenarios for capsule update which were all easy to use but which offered varying degrees of security, from good to best.
  • Good - In the first scenario, the capsule contains both a UEFI driver and a firmware image. The BIOS validates the UEFI driver and then launches it, expecting it will produce the Firmware Management protocol. The system firmware then passes that instance of the protocol the rest of the capsule payload. The driver's instance of the protocol validates the firmware image and updates the flash image. The add-in card vendor doesn't put the flash and validation logic on the add-in card itself, saving precious ROM space on the card. The system firmware validates the UEFI driver using the normal secure boot mechanism, assuming it is turned on. That is, the card has to depend on the system firmware. 
  • Better - In the second scenario, the capsule only contains the firmware update payload, but not a UEFI driver. Instead, the add-in card provides the instance of the Firmware Management protocol, which is installed by the UEFI driver that resides on the add-in card. That driver is also validated by the system firmware using the same secure boot criteria, and the UEFI driver on the add-in card performs the validation of the payload before flashing it. This requires more space on the flash device on the add-in card to support this validation and flash code, but it means that the security of the card's flash image is all in the hands of the card without any other help from the BIOS. 
  • Best - In the third scenario, we have the same situation as the second scenario except that the validation and flash update are done by hardware on the add-in card rather than by the UEFI driver. 
This session highlighted this new feature of the UEFI 2.4 specification: secure, standard update of other firmware in the system other than system BIOS. This other firmware could be a security hole if facilities weren't available to ensure the untampered delivery and launch of flash image verification and update. This presentation highlighted the best practices with UEFI.

Disclaimer: Jeff Bobzin is my co-worker and vice president at Insyde Software.

Friday, September 13, 2013

UEFI At IDF13, Part 2: UEFI Secure Boot in Linux

The second session about UEFI at IDF Fall 2013 in San Francisco concerned how UEFI secure boot applies to Linux. This was presented by Vincent Zimmer, my long-time co-conspirator in the UEFI and PI specifications, and Gary Lin and Philip Oswald from SUSE Labs.

From a UEFI perspective, they took the basic secure boot story and showed how this was extended into the kernel, taking into account the roll-your-own philosophy of Linux and the GPL license. Like many other distros of Linux they have adopted the "shim" approach, which is a small signed executable that has been signed by Microsoft's Certificate Authority (or CA, to their chagrin) that, in turn, loads GRUB2. This shim OS loader contains a certificate signed by SUSE Labs, but it also can read another UEFI variable (MOKList), that contains a list of signatures that can be used to verify GRUB2. This UEFI variable is marked both non-volatile (that is, it persists across resets) and boot-service (that is, it can't be used once the full kernel takes control). Attempts to modify this variable directly from under the OS will be rejected by UEFI firmware.

Instead, SUSE's management tools to manage the MOKList contents using another variable (MOKNew) which is encrypted. During the next boot, the presence of this variable will be detected, its contents verified, and then MOKList will be updated. This tool can be run by any Linux sysadmin.

The SUSE kernel then uses either the SUSE key or the MOKList contents to verify all kernel drivers. This allows the chain of trust to extend from the firmware, all the way up into the Linux kernel. The MOKList insures that the administrator of the Linux system can control which drivers can and cannot be launched on the system, or even which version of GRUB2 can and cannot be loaded by altering the MOKList contents.

UEFI secure boot isn't about locking other operating system's out. It is about making sure we know who they are and, knowing that, whether we trust them. MOKList extends this feature on SUSE Linux (also used on RedHat) allowing the administrator to know which kernel drivers are executing and whether they should be trusted.

Wednesday, September 11, 2013

UEFI At IDF13, Part 1: Creating UEFI Solutions for Optimized for Mobile Devices

Today's presentation focuses on how to apply UEFI to non-traditional computing devices, presented by Mark Doran (the head of UEFI) and Brian Richardson, Intel's UEFI evangelist. The requirements for mobile devices include Fast Boot, Small Footprint, Secure/Trusted Boot, Scalable Codebase and the ability to run multiple OS with one image.

Mark Doran contends that the same requirements are applicable between mobile devices and the traditional client devices. He further contends that the same toolset and codebase can be used across these devices.

Some people wonder whether UEFI is applicable when you only use one OS on a platform. But the truth of the matter is that ODMs (Original Device Manufacturer) tend to sell the same hardware platform (with minor tweaks) into many market segments, where different OS and OS configurations are used. Since they have to support multiple OS' many of the platform-specific features are actually supported in the BIOS, through SMBIOS, ACPI methods and SMI code. Essentially, the BIOS acts as a "platform driver" allowing the OS driver to be written once and keep the platform-specific details in the BIOS.

Here is the summary of the best-known methods that Brian Richardson mentioned:

  • Enable cache as soon as possible in the boot process.
  • Compress most modules after memory initialization.
  • Switch to higher speed early in the initialization process.
  • Fixed hardware configuration platforms can complete silicon initialization early.
  • Fixed memory platforms can use static (pre-trained) data to reduce boot time.
  • Start the eMMC controller as soon as possible
  • Execute unrelated code during initialization delays (i.e. while waiting for eMMC to finish initialization, initialize another device)
  • Place OS-specific drivers in separate firmware volumes, so that certain UEFI drivers are only loaded if a certain OS is loaded.
  • Don't initialize a storage device that you aren't booting from.
  • Use EDK2 because it is scalable. 

These are pretty standard for most BIOS companies.

Mark Doran then went on to talk about releasing their silicon support code as the Firmware Support Package (FSP) which basically puts all of the initialization code for the chipset in a single binary blob. They have now added support for this for EDK2 and CoreBoot for certain chipsets. The FSP currently has a single-in, single-out architecture for the binary blob. You pass in configuration information to the beginning that describes how you want the chipset to be initialized. He demonstrated this on a Minnow Board (although that FSP is not currently available).

Of course, the truth is that there are companies that already provide extensive support for Intel chips. But Intel is under pressure from ARM, and they are trying to remove the obstacles by at least providing the illusion that you can do it yourself. You could, but would it be production-worthy? No, at least not at this point. Of course, I work for a BIOS company, so that's easy for me to say. Over time it may change. But too many OEMs differentiate with features in the firmware.




Sunday, September 08, 2013

Creating New Object Types With SysLib

SysLib adds object-oriented container features to C, using standard structures, a library and some macros. This article talks about the basics of creating a new object type with the library, using the SysList object type as an example.

The SysList type is declared with the macro DECL_SYS_OBJ_TYPE, as follows:

DECL_SYS_OBJ_TYPE (SysList, SYS_LIST_SIGNATURE, &SysObjType, SysListInit, SysListEmpty, NULL, SysListIsValid, NULL);

SysList - The first argument (SysList) is the name of the object structure.  Here is the structure definition:

typedef struct _SYS_LIST {
  CONST SYS_OBJ_TYPE *Obj;

  SYS_LIST_ENTRY     Header;
} SYS_LIST;

typedef SYS_LIST SysList;

Notice that the first member of the structure is a pointer to SYS_OBJ_TYPE. This is a requirement for all of the objects in SysLib. Also, by my own convention, I actually name the object structure SYS_LIST and then use a typedef (typedef SYS_LIST SysList) to get the name SysList.

SYS_LIST_SIGNATURE - The second argument is a 32-bit unsigned integer that identifies that object type. This is not absolutely required, but it helps in debugging if you make the bytes of the number ASCII characters. Some of the default debugging functions will output this signature. The EDK2 macro SIGNATURE_32 merges 4 ASCII characters into a 32-bit unsigned integer and is handy for exactly this purpose.

#define SYS_LIST_SIGNATURE SIGNATURE_32 ('S','Y','S','L') 

&SysObjType - The third argument is the pointer to the parent object type. All object types should have a parent object type, even if it is the root type (SysObjType). Parent object types can provide default implementations of all 5 of the default member functions for SysLib objects, so that if you don't provide one here, the member function from the parent object type will be used.

SysListInit - The fourth argument is the name of the object initialization function. This is the function that will be called to initialize memory that has already been allocated to a valid initial state. It is possible that this function will fail, and if so, it will return NULL. Otherwise it will return the same pointer that was passed as an input parameter. You will notice that I declare this function as STATIC (so that it can only be called through the normal SysInit() macro) and that I don't call the parent object type SysObjInit, because it isn't really necessary. If this call is successful, then IsValid() sould return TRUE.

STATIC
SYS_LIST *
SysListInit (OUT SYS_LIST *p)
{
  p->Header.Prev = p->Header.Next = &p->Header; 
  p->Header.Value = 0;
  return p;
}

SysListEmpty - The fifth argument is the name of the object empty function. This is the function that will be called to free any memory used by the object, but will not free the object itself. This helps with memory leaks, and forms the basis for operations such as SysDelete() and SysTerm().

STATIC
SYS_LIST *
SysListEmpty (IN OUT SYS_LIST *p)
{
  if (!SysIsValid (p)) {
    SYSINFO ("unable to empty List container.");
    return NULL;
  }
  
  while (!SysListIsEmpty (p)) {    
    SysListRemoveHead (p);
  }
  
  return p;                 
}

This function first checks to verify that it was passed a valid object pointer, and then removes nodes from the list until the list is empty.

NULL - The sixth argument is the name of the object copy function. A NULL in this field indicates that the parent object type's copy function should be used. In this case, the default copy operator uses SysObjCopyError() will generate a run-time error if someone tries to copy a SysList, since the behavior is currently undefined. If you wanted a byte-by-byte copy, you can use the SysObjCopyDefault() also. Or you can create your own.

SysListIsValid - The seventh argument is the name of the validity checking function. A NULL in this field indicates that the parent object type's is-valid function should be used. In this case, the SysListIsValid() function does further tests on the member pointers:

STATIC
BOOLEAN
SysListIsValid (IN CONST SYS_LIST *p)
{
  if (p->Header.Prev == NULL || p->Header.Next == NULL) {
    return FALSE;
  }

  return TRUE;
}

NULL - The eighth argument is the name of the debug dump function. A NULL in this field indicates that the parent object type's dump function should be used. In this case, the default dump function uses SysObjDump() will dump the object type information, like the signature and size and all the bytes of the object.

Using this basic template, you can create a quite sophisticated series of derived objects. For example, SysLib contains both SysListO (for a list of objects) and SysListStrA (for a list of null-terminated ASCII strings), which are derived from SysList. These take advantage of the key member functions (like Empty() and Init()) to handle creation of specialized lists.

Since all of these are designed to run under UEFI, they make more powerful applications possible. Currently SysLib showcases this in both the IfrAsm and Setup applications. More coming soon.

UEFI 2.4 Review, Part 11: Timestamp Protocol

This article is the eleventh in a series looking at the changes introduced in the UEFI 2.4 specification. This time we take a look at the time stamp protocol (EFI_TIMESTAMP_PROTOCOL) in chapter 34.

Timestamp counters are common on various CPU architectures. So common, in fact, that many features have come to rely on them, such as ACPI's Firmware Performance Data Table (FPDT, chapter 5.2.23). On the x86 processors, the RDTSC instruction returns the current value. But, in various incarnations, this value has to be manipulated based on the number of cores, power-state, etc. So how to get a reliable number? Or, for that matter, how does this map on to ARM's generic timer?

I recently ran into this while trying to build the UEFI shell. There were recent changes to add the DP command, which displays the FPDT contents, to the UEFI shell. But this command had a dependency on the TimerLib, which was fixed at build time for a specific hardware implementation. That means that if the shell was run on another system, with a slightly different timer implementation, the DP command would not work or, worse, return the wrong value.

Whenever there is a hardware dependency like this, UEFI likes to abstract the hardware capability behind a protocol. In this case the EFI_TIMESTAMP_PROTOCOL was modeled after the TimerLib. It has two functions: GetTimeStamp() and GetProperties().

GetTimeStamp() simply returns the current time stamp value. There are a few restrictions though: the frequency of the time stamp must remain constant, regardless of power management state and the time stamp value may roll over. This latter is very important, especially for time stamp counters with fewer bits, since they can roll over in a few seconds.

GetProperties() returns the frequency (in Hz) of the time stamp counter, and the maximum value. This allows the caller to convert differences between time stamp counter values into actual time. It also allows for it to check for overflows. This is importance, since some implementations use the ACPI timer instead of an integrated CPU time stamp counter, and some implementations of the ACPI timer are only 24- or 32-bits in size.

With this new protocol, UEFI 2.4 allows utilities to track performance of a system in a way which spans the wide variety of system and CPU architectures supported.

Friday, September 06, 2013

EDK2 Supports Visual Studio 2012

When we wrote HOW-TO: Set Up the EDK2's Windows-Hosted UEFI Environment With Visual Studio 2012 for this blog, EDK2 did not support Visual Studio 2012. Just recently, EDK2 was made to support Visual Studio 2012, so the article had to be updated. 

The edited and updated article can be viewed here.

Thursday, September 05, 2013

UEFI 2.4 Review, Part 10: Support for ARM AArch64 Processors

This article is the tenth in a series going over the changes made in the UEFI 2.4 specification. This time we take a look at the ARM AArch64 processor bindings. AArch64 is the term used to describe the new 64-bit extensions to the ARM processor architecture. The first CPUs based on this architecture are expected to ship this year.

UEFI cares about processor bindings. Why? If all of the C code in your system was produced by the same compiler and linker (like GCC or Microsoft Visual Studio), then this would be less important. But since UEFI was designed to allow driver and application binaries written by one company to be called by the binaries written by another company. Those two companies might be using a different compiler and those compilers might use a different contract or convention or binding as to how parameters are passed. From personal experience, this leads to disastrous, hard-to-debug scenarios where you think you're passing A but the function thinks you passed B.

So section 2.3.6.4 statest explicitly that the calling convention must follow the one described in the Procedure Call Standard for the ARM 64-bit Architecture Version A-0.06 or later. It also lists some additional restrictions about usage of optional ARM instruction set features, it must operate in little-endian mode (UEFI is little endian) and the stack must be 16-byte aligned.

The UEFI specification further describes the operating environment of the UEFI drivers and applications (section 2.3.6): uniprocessor, highest-possible privilege level, unaligned memory access enabled, MMU enabled, cache enabled, floating point enabled, 4KB page tables, interrupts and generic timer enabled, 128KB stacks space. Finally, there are some rules detailing how ARM memory types map to UEFI memory types (2.3.6.1) and how pieces of code using from other operating environments can call into UEFI (2.3.6.3).

ARM AArch64 processors are fully supported in UEFI. For those of you following the tianocore.org progress, you can see that all of the information related to build tools and processor bindings is present. Love to play with a develop board, but all of the ARM v8 cores I know of still require an NDA even to talk about.

Wednesday, September 04, 2013

UEFI 2.4 Review, Part 9: Adapter Information Protocol

This is the ninth part in a series of articles looking in detail at the changes in the UEFI 2.4 specification. This time we will look at the Adapter Information Protocol, which provides access to operating state information for an adapter. Not a setting, but a state.

So, at first glance, the EFI_ADAPTER_INFORMATION_PROTOCOL (section 10.11) looks just like another Get/Set/Query API we've seen a thousand times before. Ah, but this is a Get/Set/Query API with a GUID. So what, you say? That means we can hang dozens of interesting pieces of data off of one adapter handle. A similar approach was also used EFI_FILE_PROTOCOL's GetInfo() function (see section 12.5) to provide additional information about a single file handle. But now we're going to do it for a device handle.

Now, per the spec, this isn't for every type of information. It is meant to be dynamic and return quickly, not blocking while waiting for some piece of data to return. In other words, not a performance pig.

What is it used for? Well, the UEFI 2.4 specification lists out three "standard" uses: the network device's media state (i.e. the network cable is plugged in, section 10.12.1), the native network boot capability of an iSCSI or FibreChannel-over-Ethernet (FCoE) target (section 10.12.2), and the 802.3 or FCoE SAN MAC address (10.12.3). But with a GUID, hey, who is stopping you from adding your own piece of adapter trivia for the consumption of adoring pre-OS applications?

No, really: this sort of information could have been published using separate protocols installed on the same driver handle. But all of those protocols would look very similar, and they would all have the same basic functionality, and they would all be produced by the same driver. So why make life difficult? Make it a single protocol.

So UEFI makes life easier for driver and those who want to manipulate them.

Monday, September 02, 2013

UEFI 2.4 Review, Part 8: NVMe Device Paths

This is the eighth part in a series examining in detail the changes made in the UEFI 2.4 specification. This time we focus on a series of related changes to supporting the NVMe specification, which "defines an optimized register interface, command set and feature set for PCI Express (PCIe®)-based Solid-State Drives (SSDs)."

Of course, if you want to boot from a device under UEFI, the standard mechanism is to write a driver that consumes the PCI I/O protocol and produces the Block I/O protocol. In fact, there is such a sample driver out on the tianocore.org EDK2 project repository (MdeModulePkg/Bus/Pci/NvmExpressDxe).

So if this support is standard (and both PCI I/O and Block I/O have been in the specification from day 1), then what is left to do? Good question. It all has to do with how UEFI represents hardware devices as device paths. A device path is a series of records (called nodes) that describe the logical path from the software running on the CPU through the various buses (like PCI and USB) and industry standard protocols (like SCSI or IPv4) until it reaches the logical or physical device. The UEFI firmware maintains a mapping between device paths and the handle associated with the device.

For example (using UEFI standard text representation of these binary records):

PciRoot(0)/PCI(1,0)/SAS(0x31000004CF13F6BD, 0, SAS)

This refers to a SAS (Serial-Attached SCSI) device attached to a SAS controller PCI device on device 1, function 0 on the first PCI root bus in the system. That device is further identified by 0x31000004cf13f6b (the SAS address), 0 (the SAS unit number) and the keyword SAS which refers to how the device has been configured.

Using the LocateDevicePath() function provided by UEFI, you can pass in a device path and the handle of the device (if any) will be returned to you. With that handle, you can then find other interfaces produced by the driver(s) managing that device, such as Block I/O.

What happens for devices based on newer or crazier technologies? Well, the UEFI specification allows you to create your own node type, called a "vendor-defined" type (see section 9.3.5.17 of the UEFI specification). In that node, you place a GUID that you create to differentiate you from all other vendor-defined nodes. The UEFI firmware doesn't really care, it just performs binary matches. And, in fact, some so-called vendor-defined nodes have made there way into the specification (see SAS or VTF-100).

These nodes tend to get really long, because the GUID guarantees that it will take at least 16 extra bytes. So the UEFI specification added a new messaging device path node (type 23) which is much shorter, containing only the required bits to identify the device uniquely behind the NVMe(r) controller: the Namespace Identifier (4 bytes) and the Extended Unique Identifier (EUI-64). 16 bytes rather than the 32 bytes that would be required if the vendor-defined version was created.

Of course, it seems that the text representation of this node was left out of the update (see table 88). So, for now, NVM device paths look like this:

PciRoot(0)/PCI(1,0)/Msg(23, 000000000101010101010101)

I expect they'll make the text version prettier. But for now, the binary version is short and sweet and lets us find and boot from NVMe devices in UEFI 2.4.