Hi,

efiboot cannot load the kernel properly on some machines if booted
from CD-ROM.  In that case boot fails with a message like follow:

   booting cd0a:..... [359648read symbols: Unknown error: code 255

As far as Asou and my test, this happens on hosts on VMware ESXi 6.7,
7.0 and asou's physical machine.

The problem happens because efiboot calls ReadBlocks function with an
unaligned pointer for medias which requires an aligned pointer.  When
efiboot loads a kernel, the pointer becomes unaligned since there is
an ELF section located at unaligned place in CD-ROM.  Previously our
kernel didn't have such a section but it does after switching lld as
the default linker.

For test, let me show sample commands which creates a bootable cdrom
image for EFI:

        mkdir -p efiboot/EFI/BOOT
        cp /usr/mdec/BOOTX64.EFI efiboot/EFI/BOOT
        makefs -M 1m -m 1m -t msdos -o fat_type=12,sectors_per_cluster=1 \
            efiboot.img efiboot
        mkdir -p cd-dir/etc
        cp bsd.rd cd-dir/
        echo "set image bsd.rd" > cd-dir/etc/boot.conf
        makefs -t cd9660 -o 
'rockridge,bootimage=i386;/usr/mdec/cdbr,no-emul-boot,allow-multidot,bootimage=efi;efiboot.img,no-emul-boot'
 \
        boot.iso cd-dir

the diff is to fix the problem.

ok?

Index: sys/arch/amd64/stand/efiboot/efidev.c
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/arch/amd64/stand/efiboot/efidev.c,v
retrieving revision 1.32
diff -u -p -r1.32 efidev.c
--- sys/arch/amd64/stand/efiboot/efidev.c       9 Dec 2020 18:10:18 -0000       
1.32
+++ sys/arch/amd64/stand/efiboot/efidev.c       10 Mar 2021 10:58:35 -0000
@@ -84,7 +84,7 @@ efid_init(struct diskinfo *dip, void *ha
 static EFI_STATUS
 efid_io(int rw, efi_diskinfo_t ed, u_int off, int nsect, void *buf)
 {
-       u_int            blks, lba, i_lblks, i_tblks, i_nblks;
+       u_int            i, blks, lba, i_lblks, i_tblks, i_nblks;
        EFI_STATUS       status = EFI_SUCCESS;
        static u_char   *iblk = NULL;
        static u_int     iblksz = 0;
@@ -127,10 +127,29 @@ efid_io(int rw, efi_diskinfo_t ed, u_int
                            min(nsect, i_lblks) * DEV_BSIZE);
                }
                if (i_nblks > 0) {
-                       status = EFI_CALL(ed->blkio->ReadBlocks,
-                           ed->blkio, ed->mediaid, lba,
-                           ed->blkio->Media->BlockSize * (i_nblks / blks),
-                           buf + (i_lblks * DEV_BSIZE));
+                       /*
+                        * Pass the buffer directly to the EFI function only if
+                        * the buffer is properly aligned as the media requires
+                        */
+                       if (ed->blkio->Media->IoAlign > 1 &&
+                           ((UINTN)buf + i_lblks * DEV_BSIZE)
+                           % ed->blkio->Media->IoAlign == 0)
+                               status = EFI_CALL(ed->blkio->ReadBlocks,
+                                   ed->blkio, ed->mediaid, lba,
+                                   ed->blkio->Media->BlockSize * (i_nblks /
+                                   blks), buf + i_lblks * DEV_BSIZE);
+                       else {
+                               for (i = 0; i < i_nblks; i += blks) {
+                                       status = EFI_CALL(ed->blkio->ReadBlocks,
+                                           ed->blkio, ed->mediaid,
+                                           lba + i / blks,
+                                           ed->blkio->Media->BlockSize, iblk);
+                                       if (EFI_ERROR(status))
+                                               break;
+                                       memcpy(buf + i * DEV_BSIZE, iblk,
+                                           ed->blkio->Media->BlockSize);
+                               }
+                       }
                        if (EFI_ERROR(status))
                                goto on_eio;
                }
@@ -160,10 +179,30 @@ efid_io(int rw, efi_diskinfo_t ed, u_int
                            ed->blkio->Media->BlockSize, iblk);
                }
                if (i_nblks > 0) {
-                       status = EFI_CALL(ed->blkio->WriteBlocks,
-                           ed->blkio, ed->mediaid, lba,
-                           ed->blkio->Media->BlockSize * (i_nblks / blks),
-                           buf + (i_lblks * DEV_BSIZE));
+                       /*
+                        * Pass the buffer directly to the EFI function only if
+                        * the buffer is properly aligned as the media requires
+                        */
+                       if (ed->blkio->Media->IoAlign > 1 &&
+                           ((UINTN)buf + i_lblks * DEV_BSIZE)
+                           % ed->blkio->Media->IoAlign == 0)
+                               status = EFI_CALL(ed->blkio->WriteBlocks,
+                                   ed->blkio, ed->mediaid, lba,
+                                   ed->blkio->Media->BlockSize * (i_nblks /
+                                   blks), buf + i_lblks * DEV_BSIZE);
+                       else {
+                               for (i = 0; i < i_nblks; i += blks) {
+                                       memcpy(iblk, buf + i * DEV_BSIZE,
+                                           ed->blkio->Media->BlockSize);
+                                       status = EFI_CALL(
+                                           ed->blkio->WriteBlocks,
+                                           ed->blkio, ed->mediaid,
+                                           lba + i / blks,
+                                           ed->blkio->Media->BlockSize, iblk);
+                                       if (EFI_ERROR(status))
+                                               break;
+                               }
+                       }
                        if (EFI_ERROR(status))
                                goto on_eio;
                }

Reply via email to