The Minnowboard Chronicles Episode 15: More UEFI Application Development in ‘C’

In Episode 12, I wrote a simple “Hello World!” application in ‘C’ using the built-in UEFI shell functions. In Episode 13, I failed in an attempt to re-write that application using standard ‘C’ library functions, such as printf(). I’ve learned a lot since then – here’s how to write more sophisticated programs.

From Episode 12 Writing UEFI Applications, I took a simple ‘C’ program and adapted it to print “Hello World” to the screen.

#include <Uefi.h>

#include <Library/UefiApplicationEntryPoint.h>

#include <Library/UefiLib.h>

EFI_STATUS

EFIAPI

UefiMain (

  IN EFI_HANDLE        ImageHandle,

  IN EFI_SYSTEM_TABLE  *SystemTable

  )

{

  Print(L"Hello World \n");

  return EFI_SUCCESS;

}

Note that this program differs from other simple ‘C’ programs in that the entry point is not the familiar main(INT argc, CHAR16 **argv) that I wanted to use to pass in a command line string. Also, it uses the UEFI shell “print” command rather than the “printf” that I am used to.

I decided to start with a program that echoes the command line to the screen, similar to the “echo” shell command.

So, one step at a time. I first wanted to learn how to modify my program to accept command line parameters and manipulate them. I found out that I needed to change the module entry point from “UefiMain” to “ShellAppMain” to pass parameters in on the command line. And, to do that, the HelloWorld.inf file must be updated to have ENTRY_POINT set to ShellCEntryLib, Packages must include ShellPkg/ShellPkg.dec, and LibraryClasses must include ShellCEntryLib. And finally, the DuetPkgX64.dsc file must have the path to ShellCEntryLib explicitly added:

ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf

So here is a “new and improved” version of MyHelloWorld.c that takes the user input from the command line and echoes it back on the screen:

/**

My Hello World!

**/

 

#include <Uefi.h>

#include <Library/UefiApplicationEntryPoint.h>

#include <Library/UefiLib.h>

 

INTN

EFIAPI

ShellAppMain (

  IN UINTN Argc,

  IN CHAR16  **Argv[]

  )

{

  int i;

  for (i = 1; i < Argc; i++)

  Print(L"%s ", Argv[i]);

  Print(L"\n");

  return EFI_SUCCESS;

}

The MyHelloWorld.inf file that is used within the build is as follows:

## @file

##

 

[Defines]

  INF_VERSION                    = 0x00010006

  BASE_NAME                      = MyHelloWorld

  FILE_GUID                      = 6467c5d1-d0f0-4b47-a6a4-0545624972ef

  MODULE_TYPE                    = UEFI_APPLICATION

  VERSION_STRING                 = 1.0

  ENTRY_POINT                    = ShellCEntryLib

#

# The following information is for reference only and not required by the build.

#

   VALID_ARCHITECTURES           = X64

#

 

[Sources]

  MyHelloWorld.c

 

[Packages]

  MdePkg/MdePkg.dec

  ShellPkg/ShellPkg.dec

 

[LibraryClasses]

  UefiApplicationEntryPoint

  UefiLib

  ShellCEntryLib

 

[Guids]

 

[Ppis]

 

[Protocols]

 

[FeaturePcd]

 

[Pcd]

And finally, the DuetPkgX64.dsc file which is the driver for the build (that is, it uses the defined source code, packages and library classes to build the application) is as follows:

## @file

#  An EFI/Framework Emulation Platform with UEFI HII interface supported.

#

#  Developer's UEFI Emulation. DUET provides an EFI/UEFI IA32/X64 environment on legacy BIOS,

#  to help developing and debugging native EFI/UEFI drivers.

#

#  Copyright (c) 2010 – 2013, Intel Corporation. All rights reserved.<BR>

#

#  This program and the accompanying materials

#  are licensed and made available under the terms and conditions of the BSD License

#  which accompanies this distribution. The full text of the license may be found at

#  http://opensource.org/licenses/bsd-license.php

#

#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

#

##

 

################################################################################

#

# Defines Section – statements that will be processed to create a Makefile.

#

################################################################################

[Defines]

  PLATFORM_NAME                  = DuetPkg

  PLATFORM_GUID                  = 199E24E0-0989-42aa-87F2-611A8C397E72

  PLATFORM_VERSION               = 0.4

  DSC_SPECIFICATION              = 0x00010005

  OUTPUT_DIRECTORY               = Build/DuetPkgX64

  SUPPORTED_ARCHITECTURES        = X64

  BUILD_TARGETS                  = DEBUG

  SKUID_IDENTIFIER               = DEFAULT

  FLASH_DEFINITION               = DuetPkg/DuetPkg.fdf

 

################################################################################

#

# Library Class section – list of all Library Classes needed by this Platform.

#

################################################################################

[LibraryClasses]

  #

  # Entry point

  #

  PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf

  DxeCoreEntryPoint|MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf

  UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf

  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf

  #

  # Basic

  #

  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf

  SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf

  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf

  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf

  CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf

  IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf

  PciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf

  PciCf8Lib|MdePkg/Library/BasePciCf8Lib/BasePciCf8Lib.inf

  PciExpressLib|MdePkg/Library/BasePciExpressLib/BasePciExpressLib.inf

  CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf

  PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf

  PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf

  #

  # UEFI & PI

  #

  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf

  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf

  UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf

  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf

  UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf

  HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf

  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf

  UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf

  DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf

  DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf

  UefiCpuLib|UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.inf

  ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf

 

  #

  # Generic Modules

  #

  UefiUsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf

  UefiScsiLib|MdePkg/Library/UefiScsiLib/UefiScsiLib.inf

  OemHookStatusCodeLib|MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf

  GenericBdsLib|IntelFrameworkModulePkg/Library/GenericBdsLib/GenericBdsLib.inf

  SecurityManagementLib|MdeModulePkg/Library/DxeSecurityManagementLib/DxeSecurityManagementLib.inf

  CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf

  PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf

  CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf

  #

  # Platform

  #

  PlatformBdsLib|DuetPkg/Library/DuetBdsLib/PlatformBds.inf

  TimerLib|DuetPkg/Library/DuetTimerLib/DuetTimerLib.inf

  #

  # Misc

  #

  PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf

  DebugAgentLib|MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf

  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf

  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf

  HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf

  ExtractGuidedSectionLib|MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf

  PlatformHookLib|MdeModulePkg/Library/BasePlatformHookLibNull/BasePlatformHookLibNull.inf

  SerialPortLib|MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.inf

  MtrrLib|UefiCpuPkg/Library/MtrrLib/MtrrLib.inf

  LockBoxLib|MdeModulePkg/Library/LockBoxNullLib/LockBoxNullLib.inf

  CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf

  LocalApicLib|UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf

 

  #

  # To save size, use NULL library for DebugLib and ReportStatusCodeLib.

  # If need status code output, do library instance overriden as below DxeMain.inf does

  #

  DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf

  DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf

  ReportStatusCodeLib|MdePkg/Library/BaseReportStatusCodeLibNull/BaseReportStatusCodeLibNull.inf

 

[LibraryClasses.common.DXE_CORE]

  HobLib|MdePkg/Library/DxeCoreHobLib/DxeCoreHobLib.inf

  MemoryAllocationLib|MdeModulePkg/Library/DxeCoreMemoryAllocationLib/DxeCoreMemoryAllocationLib.inf

 

################################################################################

#

# Pcd Section – list of all EDK II PCD Entries defined by this Platform

#

################################################################################

[PcdsFixedAtBuild]

  gEfiMdePkgTokenSpaceGuid.PcdReportStatusCodePropertyMask|0x0

  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x0

  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x0

  gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange|FALSE

 

[PcdsFeatureFlag]

  gEfiMdeModulePkgTokenSpaceGuid.PcdTurnOffUsbLegacySupport|TRUE

 

###################################################################################################

#

# Components Section – list of the modules and components that will be processed by compilation

#                      tools and the EDK II tools to generate PE32/PE32+/Coff image files.

#

# Note: The EDK II DSC file is not used to specify how compiled binary images get placed

#       into firmware volume images. This section is just a list of modules to compile from

#       source into UEFI-compliant binaries.

#       It is the FDF file that contains information on combining binary files into firmware

#       volume images, whose concept is beyond UEFI and is described in PI specification.

#       Binary modules do not need to be listed in this section, as they should be

#       specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi),

#       Logo (Logo.bmp), and etc.

#       There may also be modules listed in this section that are not required in the FDF file,

#       When a module listed here is excluded from FDF file, then UEFI-compliant binary will be

#       generated for it, but the binary will not be put into any firmware volume.

#

###################################################################################################

[Components]

  DuetPkg/DxeIpl/DxeIpl.inf {

    <LibraryClasses>

      #

      # If no following overriden for ReportStatusCodeLib library class,

      # All other module can *not* output debug information even they are use not NULL library

      # instance for DebugLib and ReportStatusCodeLib

      #

      ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf

  }

 

  MdeModulePkg/Core/Dxe/DxeMain.inf {

    #

    # Enable debug output for DxeCore module, this is a sample for how to enable debug output

    # for a module. If need turn on debug output for other module, please copy following overriden

    # PCD and library instance to other module's override section.

    #

    <PcdsFixedAtBuild>

      gEfiMdePkgTokenSpaceGuid.PcdReportStatusCodePropertyMask|0x07

      gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2F

      gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000042

    <LibraryClasses>

      DebugLib|IntelFrameworkModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.inf

      ReportStatusCodeLib|DuetPkg/Library/DxeCoreReportStatusCodeLibFromHob/DxeCoreReportStatusCodeLibFromHob.inf

  }

 

  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf

  MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf

  MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf

  MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf

 

  DuetPkg/FSVariable/FSVariable.inf

  MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf

  MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf

  MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf

  MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf

  MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf {

    <LibraryClasses>

      PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf

  }

  MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf

  MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf

  MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf

 

  MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf

  MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf

  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf

  MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf

 

 

  DuetPkg/SmbiosGenDxe/SmbiosGen.inf

  #DuetPkg/FvbRuntimeService/DUETFwh.inf

  DuetPkg/EfiLdr/EfiLdr.inf {

    <LibraryClasses>

      DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf

      NULL|IntelFrameworkModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf

  }

  IntelFrameworkModulePkg/Universal/BdsDxe/BdsDxe.inf {

    <LibraryClasses>

      PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf

  }

  MdeModulePkg/Universal/EbcDxe/EbcDxe.inf

  UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf

  UefiCpuPkg/CpuDxe/CpuDxe.inf

  PcAtChipsetPkg/8259InterruptControllerDxe/8259.inf

  DuetPkg/AcpiResetDxe/Reset.inf

  DuetPkg/LegacyMetronome/Metronome.inf

 

  PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf

  PcAtChipsetPkg/8254TimerDxe/8254Timer.inf

  DuetPkg/PciRootBridgeNoEnumerationDxe/PciRootBridgeNoEnumeration.inf

  DuetPkg/PciBusNoEnumerationDxe/PciBusNoEnumeration.inf

  IntelFrameworkModulePkg/Bus/Pci/VgaMiniPortDxe/VgaMiniPortDxe.inf

  IntelFrameworkModulePkg/Universal/Console/VgaClassDxe/VgaClassDxe.inf

 

  # IDE/AHCI Support

  DuetPkg/SataControllerDxe/SataControllerDxe.inf

  MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf

  MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf

  MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf

  MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf

 

  # Usb Support

  MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf

  MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf

  MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf

  MdeModulePkg/Bus/Usb/UsbKbDxe/UsbKbDxe.inf

  MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf

 

  # ISA Support

  PcAtChipsetPkg/IsaAcpiDxe/IsaAcpi.inf

  IntelFrameworkModulePkg/Bus/Isa/IsaBusDxe/IsaBusDxe.inf

  IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/IsaSerialDxe.inf

  IntelFrameworkModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2keyboardDxe.inf

  IntelFrameworkModulePkg/Bus/Isa/IsaFloppyDxe/IsaFloppyDxe.inf

 

  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf

  MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf

  MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf

 

  # Bios Thunk

  DuetPkg/BiosVideoThunkDxe/BiosVideo.inf

 

  #

  # Sample Application

  #

  # MdeModulePkg/Application/HelloWorld/HelloWorld.inf

 

  MyHelloWorld/MyHelloWorld.inf

 

###################################################################################################

#

# BuildOptions Section – Define the module specific tool chain flags that should be used as

#                        the default flags for a module. These flags are appended to any

#                        standard flags that are defined by the build process. They can be

#                        applied for any modules or only those modules with the specific

#                        module style (EDK or EDKII) specified in [Components] section.

#

###################################################################################################

[BuildOptions]

  MSFT:*_*_*_CC_FLAGS = /FAsc /FR$(@R).SBR

 

Now I’ll go through a detailed step-by-step process description for the build.

I put the ‘C’ source code, MyHelloWorld.inf file and the modified DuetPkgX64.dsc file into a folder entitled MyHelloWorld within the MyWorkSpace folder.

Firstly, launch “Developer Command Prompt for VS2013”.

Navigate (using the “cd” change directory command) to the MyWorkSpace directory that contains all the build files.

Type in “edksetup”.

Type in “build –p MyHelloWorld/DuetPkgX64.dsc”.

The application builds perfectly and echoes the command line arguments out to the screen. For example, if you type:

MyHelloWorld.efi This is a test!

You see the “This is a test!” echoed back on the following line. Pretty cool.

A couple of interesting notes:

On the HDMI monitor I’ve hooked the Minnowboard to, I simply see the text “This is a test!” echoed to the screen. But on the CoolTerm application I’ve got hooked into the serial port, I see the following:

FS0:\> MyHelloWorld.efi This is a test!

InstallProtocolInterface: 5B1B31A1-9562-11D2-8E3F-00A0C969723B 762CBC00

    PDB = c:\myworksspace2\Build\DuetPkgX64\DEBUG_VS2012x86\X64\MyHelloWorld5\MyHelloWorld\DEBUG\MyHelloWorld.pdb

Loading driver at 0x00077CD1000 EntryPoint=0x00077CD12C0 MyHelloWorld.efi

InstallProtocolInterface: BC62157E-3E33-4FEC-9920-2D3B36D750DF 762BAC58

InstallProtocolInterface: 752F3136-4E16-4FDC-A22A-E5F46812F4CA 78851848

This is a test!

Also, I’ve noted that the compiler rejects the following:

for (int i = 1; i > Argc; i++)

But, rather, it wants the variable declaration to be in a distinct statement:

int i;

for (i = 1; i > Argc; i++)

I don’t know why that is. Maybe I am using an older version of Visual Studio (VS2013)? That’s something to figure out for another day.

To summarize, in this episode I’ve graduated from creating a simple ‘C’ program that printed “Hello World” to the screen, to actually taking shell command parameters and echoing those out to the terminal. It may seem like a small step, but it really helped me understand how the ‘C’ source and build files interact with each other. This should enable me to write more sophisticated code going forward, and to understand how more complex programs, like drivers, are put together.

For others who might also like to take such a self-taught journey, a good debugger is indispensable. SourcePoint is probably the best UEFI hardware-assisted tool available; read more at the product website page SourcePoint for Intel Platforms.