> Date: Wed, 10 Mar 2021 20:42:42 +0900 (JST)
> From: YASUOKA Masahiko <yasu...@openbsd.org>
> 
> Sorry for making noise, let me update the diff.
> 
> > +                   if (ed->blkio->Media->IoAlign > 1 &&
> > +                       ((UINTN)buf + i_lblks * DEV_BSIZE)
> > +                       % ed->blkio->Media->IoAlign == 0)
> 
> first condition was reversed..
> 
> On Wed, 10 Mar 2021 20:35:41 +0900 (JST)
> YASUOKA Masahiko <yasu...@openbsd.org> wrote:
> > 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?

Maybe it is better to always bounce through an aligned buffer?  That
would make the code a little bit slower but a lot simpler.  And the
overhead of doing the copy should be small compared to the actual I/O.

> 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 11:41:39 -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