The Minnowboard Chronicles Episode 10: The UEFI Shell

Today, I created my first UEFI shell script. And, by a happy coincidence, I noticed a new book in Amazon, Harnessing the UEFI Shell, by Michael Rothman, Vince Zimmer, and Tim Lewis.


In last week’s Episode 9 of the Minnowboard Chronicles, I used the macro command language within SourcePoint to print “Hello World”, read Intel MSRs, and display devices on the Minnowboard’s JTAG chain, among other things. I really liked the power of the command environment: its ability to execute command line functions, chain multiple commands on a single line, wrap multiple commands over multiple lines, and of course, execute scripts. The SourcePoint command language has rich support for conditional logic, looping, branching, and the other capabilities you might expect out of a scripting language. And since it comprehends the ‘C’ programming language and is identical to the legacy Intel ITP I programming language, my learning curve was extremely short.

Since my interest is in UEFI, this week I decided to explore its shell, and see what capabilities lay therein. It’s worthwhile to note that UEFI has support for both scripts and applications. Scripts end with an “.nsh” suffix, whereas applications end with “.efi”. Creating UEFI applications is a little more complicated, so I’ll leave that for another day. Instead, let’s see what goes into creating a simple looping “Hello World” script, like I did last week with SourcePoint in Episode 9.

The convenient thing about the Minnowboard is that it boots out of the box into the UEFI shell. Typing “Help” at the Shell prompt displays the following list of commands:

alias

Displays, creates, or deletes UEFI Shell aliases.

attrib

Displays or modifies the attributes of files or directories.

bcfg

Manages the boot and driver options that are stored in NVRAM.

cd

Displays or changes the current directory.

cls

Clears standard output and optionally changes background color.

comp

Compares the contents of two files on a byte-for-byte basis.

connect

Binds a driver to a specific device and starts the driver.

cp

Copies one or more files or directories to another location.

date

Displays and sets the current date for the system.

dblk

Displays one or more blocks from a block device.

devices

Displays the list of devices managed by UEFI drivers.

devtree

Displays the UEFI Driver Model compliant device tree.

dh

Displays the device handles in the UEFI environment.

disconnect

Disconnects one or more drivers from the specified devices.

dmem

Displays the contents of system or device memory.

dmpstore

Manages all UEFI variables.

drivers

Displays the UEFI driver list.

drvcfg

Invokes the driver configuration.

drvdiag

Invokes the Driver Diagnostics Protocol.

echo

Controls script file command echoing or displays a message.

edit

Provides a full screen text editor for ASCII or UCS-2 files.

eficompress

Compresses a file using UEFI Compression Algorithm.

efidecompress

Decompresses a file using UEFI Decompression Algorithm.

else

Identifies the code executed when 'if' is FALSE.

endfor

Ends a 'for' loop.

endif

Ends the block of a script controlled by an 'if' statement.

exit

Exits the UEFI Shell or the current script.

for

Starts a loop based on 'for' syntax.

getmtc

Gets the MTC from BootServices and displays it.

goto

Moves around the point of execution in a script.

help

Displays the UEFI Shell command list or verbose command help.

hexedit

Provides a full screen hex editor for files, block devices, or memory.

if

Executes commands in specified conditions.

ifconfig

Modifies the default IP address of the UEFI IPv4 Network Stack.

load

Loads a UEFI driver into memory.

loadpcirom

Loads a PCI Option ROM.

ls

Lists the contents of a directory or file information.

map

Displays or defines file system mappings.

memmap

Displays the memory map maintained by the UEFI environment.

mkdir

Creates one or more new directories.

mm

Displays or modifies MEM/MMIO/IO/PCI/PCIE address space.

mode

Displays or changes the console output device mode.

mv

Moves one or more files to a destination within or between file systems.

openinfo

Displays the protocols and agents associated with a handle.

parse

Retrieves a value from a standard format output file.

pause

Pauses a script and waits for an operator to press a key.

pci

Displays a PCI device list or PCI function configuration space of a device.

ping

Pings the target host with an IPv4 or IPv6 stack.

reconnect

Reconnects drivers to the specific device.

reset

Resets the system.

rm

Deletes one or more files or directories.

sermode

Sets serial port attributes.

set

Displays or modifies UEFI Shell environment variables.

setsize

Adjusts the size of a file.

setvar

Displays or modifies a UEFI variable.

shift

Shifts in-script parameter positions.

smbiosview

Displays SMBIOS information.

stall

Stalls the operation for a specified number of microseconds.

time

Displays or sets the current time for the system.

timezone

Displays or sets time zone information.

touch

Updates the filename timestamp with the current system date and time.

type

Sends the contents of a file to the standard output device.

unload

Unloads a driver image that was already loaded.

ver

Displays UEFI Firmware version information.

vol

Displays or modifies information about a disk volume.

For example, typing “ver” yields the following:

Shell> ver

UEFI Interactive Shell v2.1

EDK II

UEFI v2.50 (EDK II, 0x00010000)

It is possible to print “Hello World” to the terminal with the “echo” command:

Shell> echo Hello World

Hello World

Shell>

Interestingly, it doesn’t seem possible to use the looping/branching commands on the command line, as you can do with the shells in SourcePoint, Python, and other platforms. During my first attempt to echo “Hello World” to the screen ten times, I got the below error message:

Shell> for (i=0; i<10; i++) echo Hello World!

The command 'for' is incorrect outside of a script

Command Error Status: Aborted

At this point, I decided it was time to look at the documentation. The UEFI Forum Specifications page seemed like a good place to start, where I found the UEFI Shell Specification Version 2.2 (dated January 26, 2016) being the most current. This manual was fairly difficult to plumb through, but I finally managed to figure out that the equivalent UEFI shell script to print “Hello World” ten times looks like this:

echo -off

for %i run (0 10 1)

echo Hello World!

endfor

There’s a fairly easy-to-use editor built into the UEFI Shell, appropriately named “edit”. I decided to create a shell script named “junk.nsh” with the above program, and save it to the USB stick plugged into my Minnowboard; then it’s just a simple matter of typing it at the command line to get my program running:

FS0:\> junk.nsh

FS0:\> echo -off

Hello World!

Hello World!

Hello World!

Hello World!

Hello World!

Hello World!

Hello World!

Hello World!

Hello World!

Hello World!

Hello World!

FS0:\>

And now for the amazing coincidence; after the above experiments, I happened to be browsing on my LinkedIn account, and noticed that one of my connections had “Liked” a new book on the UEFI Shell. Clicking on the link took me to Amazon, where I found Harnessing the UEFI Shell: Moving the Platform Beyond DOS, written by Michael Rothman, Vince Zimmer, and Tim Lewis. It’s recently published (March 6, 2017) and an update to a book that was originally released in 2010, so I purchased it (warning, it’s not cheap!). An excerpt of the book’s preview in Amazon is below:

Focusing on the use of the UEFI Shell and its recently released formal specification, this book unlocks a wide range of usage models which can help people best utilize the shell solutions. This text also expands on the obvious intended utilization of the shell and explains how it can be used in various areas such as security, networking, configuration, and other anticipated uses such as manufacturing, diagnostics, etc. Among other topics, Harnessing the UEFI Shell demonstrates how to write Shell scripts, how to write a Shell application, how to use provisioning options and more. Since the Shell is also a UEFI component, the book will make clear how the two things interoperate and how both Shell developers as well as UEFI developers can dip into the other's field to further expand the power of their solutions.

Harnessing the UEFI Shell is authored by the three chairs of the UEFI working sub-teams, Michael Rothman (Intel, chair of the UEFI Configuration and UEFI Shell sub-teams), Vincent Zimmer (Intel, chair of the UEFI networking sub-team and security sub-team), and Tim Lewis (Insyde Software, chair of the UEFI security sub-team). This book is perfect for any OEMs that ship UEFI-based solutions (which is all of the MNCs such as IBM, Dell, HP, Apple, etc.), software developers who are focused on delivering solutions targeted to manufacturing, diagnostics, hobbyists, or stand-alone kiosk environments.

I’ll review the book in an upcoming episode of The Minnowboard Chronicles. I’m hoping the book will give me some tips on creating an actual .efi UEFI application, after which I’ll debug it using SourcePoint.

For last week's episode of the Minnowboard Chronicles, go here: The Minnowboard Chronicles – Episode 9; where I covered the SourcePoint macro language.

Episode 11 gives a powerful demonstration of Processor Trace on the more recent Intel parts, such as the Bay Trail-I which is on the Minnowboard Turbot. 

For a preview of how I’ll be using SourcePoint to debug my app, have a look at our eBook, UEFI Framework Debugging (note: requires registration).