UEFI News and Commentary

Sunday, February 28, 2010

UEFI HII (Part 11): Image Package Encoding

The UEFI specification describes a standard set of APIs for drawing bitmaps. The HII Image protocols (as well as the Graphics Output protocol) only deal with bitmaps as arrays of 32-bit pixels. But UEFI also describes a standard way that these bitmaps (or images, as the specification calls them) should be encoded as resources (or packages).

Within the packages, images are encoded in 5 different ways:
  • 1-bit per pixel with palette. Each pixel in the image is encoded as a single bit within a byte. Each row of the image is padded to a byte boundary (as with font glyphs). Both the 0 and 1 values can be translated to a full 24-bit color value if a palette entry is provided.
  • 4-bits per pixel with palette. Each pixel in the image is encoded as 4 bits within a byte. Each row of the image is padded to a byte boundary. Each of the 16 possible pixel values can be translated to a full 24-bit color value if a palette entry is provided.
  • 8-bits per pixel with palette. Each of the 256 possible pixel values can be translated to a full 24-bit color value if a palette entry is provided.
  • 24-bits per pixel. Each pixel takes up exactly 3 bytes, one each for red, green and blue.
  • JPEG. Support is required for high (1:1:1) and medium (4:1:1) quality JPEG encoding for R/G/B. There are many other sub-types of JPEG encoding, such as gray-scale encoding for medical imaging, which are not required to be supported.
Fortunately, developers normally never need to deal directly with these formats because they are all translated by the HII Image protocol into the standard 32-bit-per-pixel format used throughout the UEFI specification, including the drawing functions of the  Graphics Output protocol.  Tools are used at build time to convert bitmap files in various formats into of the UEFI encodings.

Image packages are very similar in concept to the string packages described before. They always start with the header:

typedef struct _EFI_HII_IMAGE_PACKAGE_HDR {


  UINT32 ImageInfoOffset;
  UINT32 PaletteInfoOffset;

There are two offsets: the first is to the series of image blocks which describe the images themselves. The second is to the palette information.

The palette information consists of zero or more palettes. Each palette is an array of 32-bit color values, assigned an index between 0 and 255. Images can refer to a palettte and can share a palette. Palettes are not required to carry all 2 (1-bit), 16 (4-bit) or 256 (8-bit) colors but rather only those actually necessary for the images carried in the package.

The image information consists of zero or more image blocks, terminated by a special image block of type IIBT_END. Images are encoded in ascending order by their image identifier, starting with the value 1. Each image block does one of the following:
  • Associate a normal image with the current image identifier value.
  • Assocate a transparent image with the current image identifier value. Transparent images specify that the color value 0 should not be drawn.
  • Associate a previous image with the current image identifier value.
  • Skip a specified number of image identifier values.
The following diagram shows a simple 1-bit image as it is encoded and then how it is translated using a palette.

Images are just one of the many types of HII-related resources supported by the UEFI specification. The images are included in package lists which can be attached as part of the PE/COFF resources or loaded as separate files or file sections. Next time we will start looking at the most interesting of the HII constructs: the form, which encapsulate configuration settings.

Tuesday, February 16, 2010

WHY: Why Do I Get Unresolved Externals For __allmul?

While I was working on my project this week, I kept running into the following error:

CLibApp.lib(String.obj) : error LNK2019: unresolved external symbol __allmul referenced in function _wcssize

CLibApp.lib(Cwd.obj) : error LNK2001: unresolved external symbol __allmul
This function appears nowhere in my code, nor does it appear in any of the EDK's code? So what's going on? It turns out that this function is one of several compiler support functions that are invoked explicitly by the Microsoft C/C++ compiler. In this case, this function is called whenever the 32-bit compiler needs to multiply two 64-bit integers together. The EDK does not link with Microsoft's libraries and does not provide this function.
So why don't all the other drivers and applications in the EDK generated unresolved externals, since they obviously do 64-bit math? The EDK authors skirted this problem by creating 64-bit math routines of their own, such as MultU64x64 and MultU64x32 and using these instead of the built-in C/C++ multiply (*) operator.
Are there other functions like this one? Sure, several more for 64-bit division, remainder and shifting.
Interestingly enough, the EDK does contain some Microsoft C/C++ compiler support. See CompilerStub.c where both memcpy and memset (from the C standard library) are defined. Why? It turns out that later versions of the compiler optimize certain code sequences by calling the library routines. Want a little stranger bit of trivia? The EfiCommonLib tries to optimize the SetMem function by special-casing a set to zero (i.e. SetMem (dest, count, 0)). But it turns out that the C compile convers both branches into a call to memset.
Anyway, here is my implementation of the multiplication routine. The Microsoft version is available, but has their license. The other versions available on the web look an awful lot like the Microsoft version, down to the comments, so I reverse engineered the calling convention (old-style STDCALL) and wrote it from scratch in MASM 9.0.
; allmul - 64-bit signed multiplication support function.


; _allmul
; This function is called by the Microsoft Visual C/C++ compiler for 32-
; bit executables to multiply two 64-bit integers and returning a 64-bit
; result. The X86 processors have only a 32-bit multiply instruction,
; thus the necessity for a library support function.
; The operands are divided into two 32-bit quantities. You can imagine
; that this works like simple 2-digit x 2-digit multiplication, except
; that each digit is 32-bits wide.
;   AB
; x CD
; ----
;   DB
;  DA0
;  CB0
; CA00
; ----
; You notice that the 3rd and 4th columns never will be used because the
; are the part of the result that is > 64-bits.
; R[0:31] = DB[0:31]
; R[32:63] = DB[32:63] + DA[0:31] + CB[0:31]
; There is a short cut, if both A and C are 0, then we can use the simple
; 32-bit instruction.
;    multiplicand - Right-hand operator (CD)
;    multiplier - Left-hand operator (AB)
;    EDX:EAX - Result.

_allmul PROC NEAR USES ESI, multiplicand:QWORD, multiplier:QWORD

 MA EQU DWORD PTR multiplier [4]
 MB EQU DWORD PTR multiplier
 MC EQU DWORD PTR multiplicand [4]
 MD EQU DWORD PTR multiplicand

 mov eax, MA
 mov ecx, MC
 or  ecx, eax    ; both zero?
 mov ecx, MD
 .if zero?      ; yes, use shortcut.
   mov eax, MB
   mul ecx      ; EDX:EAX = DB[0:63].
   mov eax, MA
   mul ecx      ; EDX:EAX = DA[0:63].
   mov esi, eax ; ESI = DA[0:31].

   mov eax, MB
   mul MC       ; EDX:EAX = CB[0:63]
   add esi, eax ; ESI = DA[0:31] + CB[0:31]

   mov eax, MB
   mul ecx      ; EDX:EAX = BD[0:63]
   add edx, esi ; EDX = DA[0:31] + CB[0:31] + DB[31:63]
                ; EAX = DB[0:31]

 ret 16 ; callee clears the stack.
_allmul ENDP


Friday, February 12, 2010

HOW-TO: Debug The EDK's Windows-Hosted UEFI Environment

Last time we took a quick look at how to set up the Windows-hosted (NT32) UEFI environment provided by the EDK. The NT32 environment is very useful for debugging UEFI applications which aren't tied to specific hardware devices. So this week, I'll show how to add on debugging support.

This article assumes that you have already loaded the Visual Studio project to build the EDK's NT32 platform in C:\EDK as described previously.

1. Select the NT32 project in the Solution Explorer.

2. Select Project|Properties

3. In the "NT32 Properties Pages" select "Debugging"

4. Select "Command" and enter "SecMain.exe".
5. Select "Working Directory" and enter "c:\edk\sample\platform\nt32\uefi\ia32".

6. Select "Environment" and select the ...

7. An editor will pop up. In the editor box, enter the following and the click "OK". These are environment variables which govern how the emulator works, how much memory it uses, what virtual devices it has access to, etc. We will discuss these more in the next article.


EFI_WIN_NT_GOP=Graphics Output Window 1!Graphics Output Window 2
EFI_WIN_NT_UGA=UGA Window 1!UGA Window 2
EFI_WIN_NT_CPU_MODEL=Intel(R) Processor Model

8. Click "OK"

Now you are ready to actually debug. Press F5 (or select Debug|Start Debugging). It will always ask you if you want to rebuild the project, since we havne't added any source files which Visual Studio can use to determine if the project source has been changed. For now, choose Yes and then you will see the program begin to execute normally.

You can halt the execution of the emulated UEFI environment at any time by selecting Debug|Break All. Then you can set some breakpoints on a specific function using Debug|New Breakpoint|Break At Function and type in the function name. You can force a breakpoint by inserting a __debugbreak() into your code.

As the code executes you will notice that the Visual Studio window talks about various DLLs being loaded. The Windows-hosted environment actually loads your UEFI drivers and applications as DLLs (look in C:\Edk\Sample\Platform\Nt32\Uefi\Ia32). The actual main program is called SecMain.exe.

So now we're debugging. But what else can we do with the Windows-hosted (NT32) environment? Next time we'll look at how the environment can be configured.

HOW-TO: Set Up The EDK's Windows-Hosted UEFI Environment With Visual Studio 2008.

Since I'm working on a little research project of my own using UEFI applications, I thought I'd use the Windows-hosted UEFI (aka NT32) environment provided with the EDK. From previous experience, I know that the ability to debug applications using the Visual Studio environment speeds up my development time considerably. So I thought I'd share how I set up my environment. Then next time, I'll share how I set up for debugging.

This article assumes that you have Visual Studio 2008 installed and that the EDK has been downloaded to C:\EDK.

Create New Visual Studio Project
In this step, the goal is to set up Visual Studio so that it can build the Windows-hosted (NT32) UEFI environment. The build files for this project are located in C:\EDK\SAMPLE\PLATFORM\NT32.

1. Select File|New|Project.

2. Select the "General" project category and select "Makefile Project". Then enter the Name as "NT32". Then click "OK".

3. The Makefile Project Wizard will pop up. Click "Finish"

4. Your new project will appear in the Solution Explorer.

5. Select the NT32 project with the mouse. Select Project|Properties.

6. On the "NT32 Property Pages", change "Configuration" to "All Configurations".

7. Select "Build Command Line" and then select the ... on the right edge.

8. This will pop up a separate editor box. On three separate lines, enter the following text and click "OK".

cd /D c:\edk\sample\platform\nt32
set EDK_SOURCE=c:\edk
call build.bat

9. Now, for "Rebuild All Command Line" enter:

cd /D c:\edk\sample\platform\nt32

set EDK_SOURCE=c:\edk
call build.bat clean
call build.bat

10. Now, for "Clean Command Line" enter:

cd /D c:\edk\sample\platform\nt32

set EDK_SOURCE=c:\edk
call build.bat clean

11. Finally, for the last step, enter:


12. Select OK.

13. Edit Config.env, which is located in C:\EDK\Sample\Platform\NT32\Build, and change USE_VC8 = YES. By default, it is set to NO, which will build with Visual Studio 2003 and a large number of build errors.

Launching The Windows-Hosted Environment.
At this point, should be able to build by selecting Build|Build (or using F7). You can run the emulated environment by going to the command prompt and typing:

cd /d c:\edk\sample\platform\nt32\uefi\ia32

You will see (at least) two windows: the debug output console window and then the graphics output window. The graphics output window will show a fake logo, a progress bar and then boot into the built-in EFI Shell.

Well, that gets us to the first step. Next time we'll discuss how to set up the debugger and how to make your code debugger friendly.