Eric Radman : a Journal

EFI/PXE Debugging

These are the steps to diagnosing a problem booting OpenBSD using EFI and PXE.

probing: pc0 com0 mem[636K 1887M 16M 84K 88K 752K 16K 4M 584K 7M 6M 6148M]
disk: hd0* hd1* hd2* hd3* hd4
>> OpenBSD/amd64 BOOTX64 3.69
boot>
cannot open tftp:/etc/random.seed: No such file or directory
booting tftp:/bsd: open tftp:/bsd: No such file or directory
 failed(2). will try /bsd

Edit/Build/Install

Download and unpack openbsd/src-master, and build/install after each change

cd ~/src/src-master/sys/arch/amd64/stand/efiboot
ag -l | entr -s 'make && doas cp ./bootx64/obj/BOOTX64.EFI /tftpboot/efi/BOOTX64.EFI'

Add debug statements to sys/arch/amd64/stand/efiboot/efipxe.c

/* L144-156 */
status = PXE->Mtftp(PXE, EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, NULL,
                    FALSE, &size, NULL, &servip, path, NULL, FALSE);
printf("status: %d == %d?\n", status, EFI_SUCCESS);
printf("PXE: %p\n", PXE);
printf("tftp_opcode: %d\n", EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE);
printf("servip v4: %d.%d.%d.%d\n",
    servip.v4.Addr[0],
    servip.v4.Addr[1],
    servip.v4.Addr[2],
    servip.v4.Addr[3]);
printf("path: %s\n", path);
printf("size: %d\n", size);
printf("Mtftp: %p\n", PXE->Mtftp);

Network boot, observe that status is 3.

According to sys/dev/efi/efi.h

#define EFI_SUCCESS 0
#define EFI_INVALID_PARAMETER  EFIERR(2)
#define EFI_UNSUPPORTED        EFIERR(3)

EDK2 docs for TFTP callback funcions indicate that GET_FILE_SIZE is not supported

/*
For MTFTP "get file size" operations, if the MTFTP server does not support
the "get file size" option, EFI_UNSUPPORTED will be returned.
*/

Workaround

diff --git a/sys/arch/amd64/stand/efiboot/efipxe.c b/sys/arch/amd64/stand/efiboot/efipxe.c
index 316f2f81d..cb3f79b53 100644
--- a/sys/arch/amd64/stand/efiboot/efipxe.c
+++ b/sys/arch/amd64/stand/efiboot/efipxe.c
@@ -139,7 +139,10 @@ tftp_open(char *path, struct open_file *f)

    status = PXE->Mtftp(PXE, EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, NULL,
        FALSE, &size, NULL, &servip, path, NULL, FALSE);
-   if (status != EFI_SUCCESS) {
+   if (status == EFI_UNSUPPORTED) {
+       size = 5 * 1024 * 1024;  /* actual size unknown, allocate enough space for bsd.rd */
+   }
+   else if (status != EFI_SUCCESS) {
        free(tftpfile, sizeof(*tftpfile));
        return ENOENT;
    }
@@ -158,6 +161,7 @@ tftp_open(char *path, struct open_file *f)

    status = PXE->Mtftp(PXE, EFI_PXE_BASE_CODE_TFTP_READ_FILE,
        tftpfile->inbuf, FALSE, &size, NULL, &servip, path, NULL, FALSE);
+   tftpfile->inbufsize = size;
    if (status != EFI_SUCCESS) {
        free(tftpfile, sizeof(*tftpfile));
        return ENXIO;

Verify UEFI/TFTP boot with QEMU and iPXE

Configure Network Bridge
ip link add br0 type bridge
ip address add dev br0 172.16.0.1/24
ip link set dev br0 up
ip tuntap add dev tap0 mode tap
ip link set tap0 master br0

Install/configure TFTP/DHCP

apt install tftpd-hpa isc-dhcp-server

cat <<EOF >/etc/dhcp/dhcpd.conf
subnet 172.16.0.0 netmask 255.255.255.0 {
  next-server 172.16.0.1;
}

host tftptest {
  hardware ethernet 00:16:3e:34:bb:5f;
  fixed-address 172.16.0.10;
  filename "tftptest.efi";
}
EOF

echo 'INTERFACESv4="br0"' > /etc/default/isc-dhcp-server
systemctl restart isc-dhcp-server

Build/test EFI application

sudo apt install gnu-efi build-essential

git clone https://github.com/eradman/tftptest.efi.git
cd tftptest.efi/
make
sudo cp tftptest.efi /srv/tftp/

TFTP boot using QEMU and iPXE

sudo apt install qemu-system-x86

Fetch iPXE Image

cd
curl -O https://boot.ipxe.org/ipxe.iso

Start VM

sudo qemu-system-x86_64 \
    -nographic \
    -bios /usr/share/ovmf/OVMF.fd \
    -cdrom ~/ipxe.iso \
    -nic tap,ifname=tap0,model=virtio-net-pci,mac=00:16:3e:34:bb:5f

By removing -cdrom ~/ipxe.iso we can see that EDK II tools do support this operation.

References

Network Protocols — UDP and MTFTP

iPXE issue #1620: Support for TFTP_GET_FILE_SIZE