Automation via UEFI

2 hours ago 1

In our datacenter, we faced a recurring challenge when configuring several servers tasks such as setting the system time, updating passwords, and, most importantly, configuring the RAID controller. Our goal was to combine two 10 TB disks into a RAID 1 group for installing VMware ESXi, which only requires about 2 GB of disk space. This approach, however, meant wasting over 9 TB of usable storage just to install a lightweight hypervisor. Using stateless ESXi wasn’t an option, so we needed a better way to manage the RAID configuration directly in order to create two Virtual Drives one with 100GB for the ESXi and rest of it as data disk.

While the REDFISH standard can automate many BIOS-level configurations through a RESTful API, our servers didn’t fully support it, limiting our options. We were left with two potential automation approaches:

  1. Booting into a lightweight live OS (around 100–200 MB) and injecting the required utilities such as the RAID controller tool.
  2. Using a UEFI pre-OS application that included the same RAID management utility.

Fortunately, our RAID controller provided both Linux and UEFI executables. This sparked my curiosity could I automate server provisioning directly through UEFI, without relying on a live OS? That question became the starting point for my deep dive into UEFI how it works, and how it can enable firmware-level automation in modern datacenters.

Introduction to UEFI

The Unified Extensible Firmware Interface (UEFI) is the modern replacement for the legacy BIOS (Basic Input/Output System) that has powered computers since the 1980s. Unlike BIOS, which operates in 16-bit real mode with strict size limitations and rudimentary drivers, UEFI runs in 32-bit or 64-bit protected mode, offering a modular, extensible, and secure environment for initializing hardware and booting operating systems. Its design provides significant advantages such as faster boot times, support for larger disks (over 2 TB with GPT), network and remote management capabilities, and pre-OS applications for diagnostics or provisioning. In modern datacenter and cloud environments, UEFI is preferred because it enables automated, programmable provisioning workflows, secure boot chains, and firmware-level APIs that integrate seamlessly with DevOps automation tools.

To boot a cluster of servers using UEFI, I needed to create a bootable USB or CD. However, creating a bootable CD isn’t as straightforward as it may sound there are several important factors to consider during the process. You might ask, why not just use a USB drive instead? The reason lies in the server’s firmware compatibility. If a server’s BIOS doesn’t support UEFI boot mode, then the system cannot recognize or boot from a FAT32-formatted USB drive. In such cases, the only viable option is to create a bootable CD, since legacy BIOS lacks the capability to interpret UEFI file systems like FAT32.

  • UEFI looks for a FAT32 partition with a specific directory structure:
  • Legacy BIOS looks for a boot sector at the beginning of the disk (MBR).

For making a MBR bootable CD you need tools such as xorriso and the CD image has to be EL Torito compliant.

The EFI system follows a simple rule for booting: the bootloader must be placed in a specific directory path /EFI/BOOT/BOOTX64.EFI.

This is the default fallback location that the UEFI firmware looks for when starting up. It’s important to note that if you are using a different architecture (for example, ARM), the X64 part of the filename should be changed accordingly (e.g., BOOTAA64.EFI for ARM64).

How to Create a Bootable CD

To create an ISO9660/UDF bootable CD on Linux, here’s the approach I used:

First, I created a virtual block device to serve as the target for copying the EFI bootable files.
Run the following commands with root privileges: (Special thanks to my friend Abolfazl for his guidance on the correct usage of xorriso.)

dd if=/dev/zero of=efiboot.img bs=4k count=5000

mkfs.vfat efiboot.img

mount efiboot.img ./efiimage

mkdir -p ./efiimage/EFI/BOOT

cp BOOTX64.EFI ./efiimage/EFI/BOOT

mkdir ./efiboot # copy whatever tools you need here then copy the efiboot.img into this folder as well

umount ./efiimage

cp efiboot.img ./efiboot/

xorriso -as mkisofs -iso-level 3 -l -J -R -boot-load-size 4 -full-iso9660-filenames -volid "UEFI_TOOLS" -eltorito-alt-boot -e efiboot.img -no-emul-boot -isohybrid-gpt-basdat -output uefi-tools-img.iso efiboot/

  • -as mkisofs : xorriso is a modern tool that can emulate mkisofs or genisoimage behavior. Using -as mkisofs makes it accept mkisofs-style syntax useful when following old guides.
  • -iso-level 3 : ISO9660 has several “levels” defining file-name and size limits:
    • Level 1: 8.3 filenames, ≤2 GB file size.
    • Level 2: 31-character filenames.
    • Level 3: removes file-size limit (files > 4 GB allowed). Needed for large UEFI images or installer ISOs.
  • -l : Without -l, ISO9660 enforces the old 8.3 format. This allows longer filenames in the ISO directory tree.
  • -J : Enable Joliet extensions (for Windows). Adds secondary directory records so Windows can see long Unicode filenames.
  • -R : Enable Rock Ridge extensions (for Unix/Linux). Adds POSIX file attributes (permissions, symlinks, etc.) to the ISO.
  • -boot-load-size 4 : Specify number of 512-byte sectors to load for the boot image. For El Torito (BIOS) boot entries this defines how many sectors BIOS should load initially (default = 4). Although you’re primarily targeting UEFI (which doesn’t use this), some tools still expect it for compatibility.
  • -full-iso9660-filenames : Don’t truncate ISO9660 filenames to 31 characters. Forces inclusion of complete filenames even if they exceed standard limits. Useful when you care about readability in older ISO9660-only environments.
  • -volid "UEFI_TOOLS" : Set the ISO volume label (Volume ID).
  • -eltorito-alt-boot : The El Torito specification allows multiple boot entries (e.g. BIOS + UEFI). This flag tells xorriso the next boot parameters apply to a new entry in this case, your UEFI boot image.
  • -e efiboot.img : Specify the EFI System Partition image (the UEFI boot image). This file must be a FAT32 image that contains: /EFI/BOOT/BOOTX64.BIN It gets embedded into the ISO as the El Torito “EFI Boot Image.” UEFI firmware mounts this image internally and executes BOOTX64.EFI.
  • -no-emul-boot : Mark the boot image as “no-emulation” type. Means firmware should not treat it as a floppy or hard-disk emulation instead, load and execute it directly. Required for both UEFI and modern BIOS boots.
  • -isohybrid-gpt-basdat : Make the ISO “hybrid” usable as both an ISO file and a raw disk image (for USB sticks). This adds:
    • A protective GPT partition table
    • An MBR for BIOS boot
    • Correct “basdat” (bootable-as-data) flags
  • -output uefi-tools-img.iso : Set the output filename for the generated ISO image.
  • efiboot/ : Directory to include as ISO root. All files inside efiboot/ will be placed at the root of the ISO filesystem. Typically this folder contains your /EFI/BOOT/BOOTX64.EFI and possibly supporting files.

Well if your bios is supporting UEFI in the first place chances are you don’t need these long commands just copy the BOOTX64.BIN into the /EFI/BOOT/ of the usb and reboot your system.

Where to Get The Boot Loader?

You might be wondering where to get the BOOTX64.EFI file. While many hardware manufacturers such as HP provide their own versions of the UEFI bootloader, there’s also an open-source implementation available through the EDK2 project on GitHub. However, there’s a catch: the EDK2 repository does not include precompiled binaries, so if you want to use it directly, you’ll need to compile it yourself.

The compilation process is relatively straightforward, and it also allows you to customize and enable additional modules. For example, ShellPkg provides the UEFI shell executable, and the DXE libraries extend functionality for custom bootloaders or pre-OS applications.

If you prefer not to compile it manually, there’s a community project that regularly builds and publishes precompiled versions of EDK2, including the UEFI Shell. You can find it here: https://github.com/pbatard/UEFI-Shell

Now What?

Now that we have a bootable medium, we can copy the HDD driver into the UEFI disk and create a startup.nsh file to run our script as soon as we boot into the UEFI shell and change the RAID configuration we want. For example, this is my startup.nsh content:

# delete all volumes

storcli /c0/vall del force

# reboot the system

reset -c

This way, we can automate what we want to achieve via the UEFI shell.

Key Takeaway

There is no built-in tool available in UEFI to send logs to a remote log server utilities like curl or wget simply don’t exist in this environment.

If you run UEFI on a fresh server with no storage available for writing logs, there used to be a ramdisk executable for creating a virtual drive in memory, but that file is no longer available. I could only find a 32-bit version, which requires modification to run on modern servers. However, the ramdisk DXE module is still present in the EDK2 source code.

The only way I found to extract logs from the server was by connecting to the BIOS output through Serial-over-LAN (SOL) and recording the output from another machine.

The size of the bootable CD is relatively small, typically between 20 MB and 50 MB, depending on the binaries you include which is an important factor when deploying changes across a datacenter.

References

Read Entire Article