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;

Root Cause

By modifying github.com/pief/tftptest.efi to call TFTP_GET_FILE_SIZE we are able to see that this function normally succeeds. It fails when the caller is iPXE!

#!ipxe
imgload tftp://192.168.0.2/tftptest.efi
boot

Indeed, efi_pxe.c returns ENOTSUP for any operation that is not TFTP_READ_FILE.

References

Network Protocols — UDP and MTFTP