Tuesday, September 1, 2015

How to Identify a User

This page describes the scenario of user identification. For example, how does a web site verify the user in web environment?

I separate the concept into 3 topics. First, we use the simplest but no efficient way to solve it. Second, we apply the digital signature way. Finally, we introduce secure boot that has the same concept.


Let's consider the use case. A client sends a request M1 to a server, and the server want to verify the M1 is sent by the the client. We can use asymmetric encryption/decryption to build a secure communication.
  1. The client generates key pairs, public key (PubC) and private key (PrvC).
  2. The client keeps PrvC and sends PubC to the server.
  3. Before the client sends the request M1, it encrypt M1 with PrvC to get Cipher.
  4. The client sends M1+Cipher to the server.
  5. The server decrypt Cipher with PubC to get M.
  6. The server compare M and M1. If both are same, the server make sure that M1 is owned by Client.
There is a problem that the RSA algorithm has bad performance for large data. We can apply digital signature to solve the performance.



We use Digest instead of Cipher. Digest is calculated by SHA algorithm for M1. The size of digest SHA-1 is 20 bytes. The size of digest SHA-256 is 32 bytes. The digest is too small so that we can use RSA to encrypt/decrypt it.


We can apply digital signature in secure boot, BIOS verifies if an OS loader is released by a OS vendor before running the OS loader. Before the OS vendor releases a version OS, the vendor signs the OS loader with private key and provides the public key to a BIOS vendor. Please refer the pages for the details of secure boot. The concept of secure boot comes from digital signature.


-Count

How to Draw a Beautiful Flow Chart?

This page describes how to draw a beautiful flow chart. Let's use "secure boot" as an example.

The below picture 1 is a standard flow chart that embeds a text in a rectangle. There is a problem that if the text is too long, we must enlarge the rectangle to contain the text.

Picture 1

We enhances the flow chart as the below picture 2. We just draws many beautiful fixed-size circulars that don't contain any texts. We put a text outside a circular. Now we liberate the limitation of writing texts. We can describe a longer text with more details. We can group these circulars and texts as a concept that is described by another big text. We can also write comments for a circular.



Picture 2

How do you feel the both flow charts?

-Count

Thursday, August 20, 2015

Use UEFI Shell Script to Automatically Test Your UEFI Module

We know that test program is very import for developing software components. It is not only for QA staff, but also for ourselves programmer to program quickly and to be quality.

Software developing is actually a serial of automatic processes. Code is an automatic process run by computer. It is more fast and exact than human brain. that is why a computer was developed to encrypt and decrypt messages. Test is a process we can do it automatically to reduce our effort and assurance the quality of software product. Design is a process, but we only design with our brain now. Maybe design will be delivered by a computer in the future. Programming is a process that is also be written by programmers, but Hollywood movies show us code can update itself.

This page focus on automatically testing UEFI module with UEFI shell script.

The idea is very simple. For example, we want to use a smart way to erase data on flash ROM with size in 4096-byte alignment. Flash ROM supports the following commands to erase data:

4K-Erase: Erasing data in 4K bytes
32K-Erase: Erasing data in 32k bytes
64K-Erase: Erasing data in 64k bytes

The performance is bad if we only use 4K-Erase command to erase large data. For example, To erase data with 1024K, we send 256 commands of 4K-Erase. If we use 64K-Erase command, we only sends 16 commands. We cannot always use 64K-Erase command because it is only be used in 64K alignment.

For example, if we want to erase 548K data at 100K offset, we only use the following 14 commands of 4K-Erase, 32K-Erase, and 64K-Erase.

100K Offset, 4K-Erase
104K Offset, 4K-Erase
108K Offset, 4K-Erase
112K Offset, 4K-Erase
116K Offset, 4K-Erase
120K Offset, 4K-Erase
128K Offset, 64K-Erase
192K Offset, 64K-Erase
256K Offset, 64K-Erase
320K Offset, 64K-Erase
384K Offset, 64K-Erase
448K Offset, 64K-Erase
512K Offset, 32K-Erase
544K Offset, 4K-Erase

We define the SmartFlashErase() for this requirement.

EFI_STATIS
EFIAPI
SmartFlashErase (
  IN UINT64 Offset
  IN UINT32 Size
  );

Our test program is SmartFlashEraseTest.efi with the following usage.

SmartFlashEraseTest.efi -offset offset -size -size

Out test script is SmartFlashEraseTest.nsh as below.

@echo [Case]
SmartFlashEraseTest.efi -offset 100k -size 548k
@if not %LastError% == 0 then
  @echo [Error] LastError = %LastError%
  goto exit
@endif
@echo [CheckPoint] Status = 0 (Success)

:exit

We can use redirect the NSH to a log file.
SmarFlashEraseTest.nsh > Case.log

The log file contains tags, [Case], [Error], [CheckPoint]. We can write a python program to analyze these tags. I'll write another page for this topic.

This way not only for UEFI shell, any environment that supports script can use the way to automatically test our software components.

-Count

Thursday, June 25, 2015

Use C Library in EDK II UEFI Driver

When we use C library in EDK II UEFI Driver, build failure occurs. Please refer UEFI原理與編程 for the detail. This page describes my way to solve it.

We want to use sprintf() of C library in UEFI driver to provide the driver information (e.g., build-date). I like to write the code as below,

#include <stdio.h>

EFI_STATUS
EFIAPI
LibCDxeEntryPoint (
  IN EFI_HANDLE ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  CHAR8 Info [256];
            
  sprintf (Info, "Build Date: %s", __DATE__);

  return EFI_SUCCESS;
}  

, but build failure occurs. Please,

1. Add the following statements in your package DSC file.

These statements declare that we will build C library for UEFI driver.

[LibraryClasses.Common.UEFI_DRIVER]  
  ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
  FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf  
  SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
  
  LibC|StdLib/LibC/LibC_ForDriver.inf
  #LibC|StdLib/LibC/LibC.inf
  LibCType|StdLib/LibC/Ctype/Ctype.inf  
  LibLocale|StdLib/LibC/Locale/Locale.inf  
  LibSignal|StdLib/LibC/Signal/Signal.inf  
  LibStdio|StdLib/LibC/Stdio/Stdio.inf  
  LibStdLib|StdLib/LibC/StdLib/StdLib.inf
  LibString|StdLib/LibC/String/String.inf  
  LibTime|StdLib/LibC/Time/Time.inf  
  LibUefi|StdLib/LibC/Uefi/Uefi.inf  
  LibWchar|StdLib/LibC/Wchar/Wchar.inf  
  
  LibGen|StdLib/PosixLib/Gen/LibGen.inf  
  LibIIO|StdLib/LibC/Uefi/InteractiveIO/IIO.inf

  LibContainer|StdLib/LibC/Containers/ContainerLib.inf   
  
  LibGdtoa|StdLib/LibC/gdtoa/gdtoa.inf  
  DevConsole|StdLib/LibC/Uefi/Devices/daConsole.inf
  DevUtility|StdLib/LibC/Uefi/Devices/daUtility.inf

2. Use LibC_ForDriver.inf instead of LibC.inf.

We removes ShellCEntryLib from LibC_ForDriver.inf. If we don't do it, the error occurs.

error 1001: Module type [UEFI_DRIVER] is not supported by library instance [...\MdePkg\Library\UefiApplicationEntryPoint\UefiApplicationEntryPoint.inf]

LibC_ForDriver.inf

[LibraryClasses]
#  ShellCEntryLib
  UefiLib
  BaseLib
  BaseMemoryLib
  MemoryAllocationLib
  LibStdLib
  LibStdio
  LibString
  DevConsole

3. Add dummy main().

After we follow the steps, the link error occurs, the main symbol is not defined. Please add dummy main() in your driver.

int
main (
  IN int Argc,
  IN char **Argv
  )
{
  return 0;
}

Now we can build the UEFI driver with C standard library.

-Count

Wednesday, June 17, 2015

Make Your Code Portable

The following are principles to make your code portable.

1) Use C instead of C++.

If you want your library more portable, please use C instead of C++.

2) Use C standard library.

Because EDK II supports C standard library, we just use it.

Please refer Use C Library in EDK II UEFI Driver

3) As far as possible, allocate memory space for a returned parameter before calling a public function.

For example,

EFI_STATUS
CompressData (
  IN UINT8 *Buffer,
  IN UINTN BufferSize,
  OUT UINT8 **Output,
  OUT UINTN *OutputSize
  );

This Compress function allocates memory space for the returned Output. The problem is that the memory management mechanisms are different in environments. In UEFI DXE environment, gBS->AllocatePool() supports the memory space allocation. In SMM mode, UEFI provides SmmAllocatePool(). In Windows KMDF, we should use WdfMemoryCreate to allocate memory space.

Therefore please rework the function as below otherwise we need to create many stubs to wrap the memory management functions for different environments.

EFI_STATUS
GetCompressedDataSize (
  IN UINT8 *Buffer,
  IN UINTN BufferSize,
  OUT UINTN *OutputSize
  );

EFI_STATUS
CompressData (
  IN UINT8 *Buffer,
  IN UINTN BufferSize,
  OUT UINT8 *Output,
  IN UINTN OutputSize
  );

4) The sizes of fields in C struct must be determined.

Don't use UINTN or ulong, the sizes are undetermined, in your struct. If you define struct in UEFI environment, please use UINT8, UINT16, UINT32, CHAR8, CHAR16 of which sizes are always same in IA32 and X64.

-Count

Use C++ in EDK II

There are two ways to write C++ code in EDK II.
1. Make your whole files as CPP
2. Low level files are CPP, High level files are C.

I prefer the second way because we always use C to develop UEFI driver and sometimes we want to reuse C++ components.

Please refer the UEFI原理与编程 for the detail of C++ in EDK II, where my idea comes from.

Below is my source code of UEFI application that uses C++ directly.

CppTest.inf -

The INF file defines the UEFI application, CppTest.c calls C functions of Wrap.cpp that wraps C++ of MyObject.cpp.

[Sources]
  CppTest.c
  MyObject.cpp
  MyObject.h
  Wrap.cpp
  Wrap.h

MyObject.h -

Declare the class MY_OBJECT.

class MY_OBJECT
{
  private:
    UINT8 Value1;
    UINT8 Value2;

  public:

    MY_OBJECT (UINT8 Value1, UINT8 Value2);
    UINT8 GetValue1 ();
    UINT8 GetValue2 ();    
    ~MY_OBJECT ();
};

MyObject.cpp - 

Implement the class MY_OBJECT.

MY_OBJECT::MY_OBJECT (
  IN UINT8 Value1, 
  IN UINT8 Value2
  )
{
  this->Value1 = Value1;
  this->Value2 = Value2;
}

UINT8 
MY_OBJECT::GetValue1 ()
{
  return this->Value1;
}

UINT8 
MY_OBJECT::GetValue2 ()
{
  return this->Value2;
}

MY_OBJECT::~MY_OBJECT ()
{
  this->Value1 = 0;
  this->Value2 = 0;
}

Wrap.h - 

Declare the C functions that wraps the class MY_OBJECT. The term, extern "C", declares that the following functions are name mangling in C. The value of __cplusplus is 1 when CPP file includes the H file so that the term enables. That is why we use #ifdef __cplusplus to wrap the term.

#ifdef __cplusplus
extern "C" {
#endif

VOID 
EFIAPI
CreateMyObject (VOID);

EFI_STATUS
EFIAPI
GetValuesOfMyObject (
  OUT CHAR8 *Buffer,
  IN UINTN BufferSize
  );
  
VOID 
EFIAPI
DestroyMyObj (VOID);  

#ifdef __cplusplus
}
#endif

Wrap.cpp -

Implement the C wrapping functions. We use the term, extern "C", previous to each function.It is interesting that we implement new and delete operators. If we don't do it, link errors about new and delete occurs.

MY_OBJECT *mMyObject;

void * operator new (size_t Size)
{
  VOID *RetVal;
  RetVal = AllocatePool (Size);
  return RetVal;
}

void operator delete (void *p)
{
  FreePool (p);
}

extern "C"
VOID 
EFIAPI
CreateMyObject (VOID)
{
  mMyObject = new MY_OBJECT (10, 20);
}

extern "C"
EFI_STATUS
EFIAPI
GetValuesOfMyObject (
  OUT CHAR8 *Buffer,
  IN UINTN BufferSize
  )
{
  UINTN i;
  i = 0;

  if (i == BufferSize) {
    return EFI_BUFFER_TOO_SMALL;
  }
  Buffer[i++] = mMyObject->GetValue1 ();

  if (i == BufferSize) {
    return EFI_BUFFER_TOO_SMALL;
  }
  Buffer[i++] = mMyObject->GetValue2 ();
  
  return EFI_SUCCESS;
}  

extern "C"
VOID 
EFIAPI
DestroyMyObj (VOID)
{
  delete mMyObject;
}

CppTest.c -

The C main program that tests the C wrapping function.

int
main (
  IN int Argc,
  IN char **Argv
  )
{
  UINT8 Buffer [10];
  MY_STRUCT *MyStruct;
  
  CreateMyObject ();
  
  GetValuesOfMyObject (Buffer, 10);
  printf ("Buffer [0] = %d\n", Buffer [0]);
  printf ("Buffer [1] = %d\n", Buffer [1]);
  
  DestroyMyObj ();
  
  return 0;
}


-Count

Wednesday, June 3, 2015

Link C++ Library in EDK II Module

It is hard to write C++ code in cpp and put it in EDK II INF file to build a driver with EDK2 build system because build failures occur. There are two ways to solve it.

1) We use native C++ compiler (e.g., VC++) to pack C++ code in a library with wrapped pure C function first, copy the library in your UEFI driver source code, and put the lib file in the EDK II INF file.

2) We write C++ code in a cpp file and to put the cpp file in the driver INF file.

This page describes the first way. I will describe the second way in another page, Use C++ in EDK II.

When we select the first way, build failure occurs on link errors,

error LNK2001: unresolved external symbol "void __cdecl `eh vector constructor iterator' ...

error LNK2001: unresolved external symbol "void __cdecl `eh vector destructor iterator' ...

The root cause is, we use new/delete operator to create/destroy an object array. VC++ needs the both symbols to handle the new-delete-object-array operations. The errors don't occur when building a Windows program because Windows environment provides symbols in a Windows specific library. But the link error occurs in UEFI building environment because it lacks the library.

I cannot find a regular and simple solution. My workaround is to modify C++ code to avoid to use new operator in class array.

MY_OBJ *ObjArray = new MY_OBJ [100];
delete[] ObjArray;                  

I use malloc/free instead of new/delete as below for an object array;

MY_OBJ **ObjArray = (MY_OBJ **) malloc (sizeof (MY_OBJ *) * 100);
for (int i = 0; i < 100; i++) {                                  
  ObjArray [i] = new MY_OBJ;                                     
}                                                                
                                                                 
for (int i = 0; i < 100; i++) {                                  
  delete ObjArray [i];                                           
}                                                                
free (ObjArray);                                                 

The regular solution is to use C struct instead of C++ class for platform portability.

Another link error occurs as below.

error LNK2001: unresolved external symbol __CxxFrameHandler3

The symbol __CxxFrameHandler3 is to check buffer overflow. The UEFI building environment lacks the symbol so that the link error occurs.

My solution is to create a dummy __CxxFrameHandler3 function in UEFI source code to avoid the link error.

void __CxxFrameHandler3 (void)
{
  return;
}

-Count