Hi,

I was tested attached patch on following machines and worked fine on
those machines.

  - ESXi vitrual machine
  - AMD TYZEN 7
  - Intel corei7
--
ASOU Masato
  
From: YASUOKA Masahiko <yasu...@openbsd.org>
Date: Thu, 11 Mar 2021 15:05:11 +0900 (JST)

> On Wed, 10 Mar 2021 13:15:58 +0100 (CET)
> Mark Kettenis <mark.kette...@xs4all.nl> wrote:
>>> 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.
> 
> Indeed.  It became much simpler.  As I tested on ESXi 7.0, vaio, and
> qemu, I don't feel significant performance regression.
> 
> ok?
> 
> Index: sys/arch/amd64/stand/efiboot/efidev.c
> ===================================================================
> RCS file: /var/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     11 Mar 2021 05:59:41 -0000
> @@ -84,10 +84,10 @@ 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            blks, start, end;
>       EFI_STATUS       status = EFI_SUCCESS;
> -     static u_char   *iblk = NULL;
> -     static u_int     iblksz = 0;
> +     static u_char   *ibuf = NULL;
> +     static u_int     ibufsz = 0;
>  
>       /* block count of the intrisic block size in DEV_BSIZE */
>       blks = EFI_BLKSPERSEC(ed);
> @@ -95,90 +95,46 @@ efid_io(int rw, efi_diskinfo_t ed, u_int
>               /* block size < 512.  HP Stream 13 actually has such a disk. */
>               return (EFI_UNSUPPORTED);
>  
> -     /* leading and trailing unaligned blocks in intrisic block */
> -     i_lblks = ((off % blks) == 0)? 0 : blks - (off % blks);
> -     i_tblks = (nsect > i_lblks)? (off + nsect) % blks : 0;
> -
> -     /* aligned blocks in intrisic block */
> -     i_nblks = (nsect > i_lblks + i_tblks)? nsect - (i_lblks + i_tblks) : 0;
> -
> -     lba = (off + i_lblks) / blks;
> -
> -     /* allocate the space for reading unaligned blocks */
> -     if (ed->blkio->Media->BlockSize != DEV_BSIZE) {
> -             if (iblk && iblksz < ed->blkio->Media->BlockSize) {
> -                     free(iblk, iblksz);
> -                     iblk = NULL;
> -             }
> -             if (iblk == NULL) {
> -                     iblk = alloc(ed->blkio->Media->BlockSize);
> -                     iblksz = ed->blkio->Media->BlockSize;
> -             }
> +     start = off / blks;
> +     end = (off + nsect + blks - 1) / blks;
> +     /*
> +      * Prepare a buffer to use an aligned memory always that might be
> +      * required by some medias
> +      */
> +     if (ibuf && ibufsz < (end - start) * ed->blkio->Media->BlockSize) {
> +             free(ibuf, ibufsz);
> +             ibuf = NULL;
> +     }
> +     if (ibuf == NULL) {
> +             ibufsz = (end - start) * ed->blkio->Media->BlockSize;
> +             ibuf = alloc(ibufsz);
>       }
> +
>       switch (rw) {
>       case F_READ:
> -             if (i_lblks > 0) {
> -                     status = EFI_CALL(ed->blkio->ReadBlocks,
> -                         ed->blkio, ed->mediaid, lba - 1,
> -                         ed->blkio->Media->BlockSize, iblk);
> -                     if (EFI_ERROR(status))
> -                             goto on_eio;
> -                     memcpy(buf, iblk + (blks - i_lblks) * DEV_BSIZE,
> -                         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));
> -                     if (EFI_ERROR(status))
> -                             goto on_eio;
> -             }
> -             if (i_tblks > 0) {
> -                     status = EFI_CALL(ed->blkio->ReadBlocks,
> -                         ed->blkio, ed->mediaid, lba + (i_nblks / blks),
> -                         ed->blkio->Media->BlockSize, iblk);
> -                     if (EFI_ERROR(status))
> -                             goto on_eio;
> -                     memcpy(buf + (i_lblks + i_nblks) * DEV_BSIZE, iblk,
> -                         i_tblks * DEV_BSIZE);
> -             }
> +             status = EFI_CALL(ed->blkio->ReadBlocks,
> +                 ed->blkio, ed->mediaid, start, 
> +                 (end - start) * ed->blkio->Media->BlockSize, ibuf);
> +             if (EFI_ERROR(status))
> +                     goto on_eio;
> +             memcpy(buf, ibuf + DEV_BSIZE * (off - start * blks),
> +                 DEV_BSIZE * nsect);
>               break;
>       case F_WRITE:
> -             if (ed->blkio->Media->ReadOnly)
> -                     goto on_eio;
> -             if (i_lblks > 0) {
> -                     status = EFI_CALL(ed->blkio->ReadBlocks,
> -                         ed->blkio, ed->mediaid, lba - 1,
> -                         ed->blkio->Media->BlockSize, iblk);
> -                     if (EFI_ERROR(status))
> -                             goto on_eio;
> -                     memcpy(iblk + (blks - i_lblks) * DEV_BSIZE, buf,
> -                         min(nsect, i_lblks) * DEV_BSIZE);
> -                     status = EFI_CALL(ed->blkio->WriteBlocks,
> -                         ed->blkio, ed->mediaid, lba - 1,
> -                         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));
> -                     if (EFI_ERROR(status))
> -                             goto on_eio;
> -             }
> -             if (i_tblks > 0) {
> +             if (off % blks != 0 || nsect % blks != 0) {
>                       status = EFI_CALL(ed->blkio->ReadBlocks,
> -                         ed->blkio, ed->mediaid, lba + (i_nblks / blks),
> -                         ed->blkio->Media->BlockSize, iblk);
> +                         ed->blkio, ed->mediaid, start, 
> +                         (end - start) * ed->blkio->Media->BlockSize, ibuf);
>                       if (EFI_ERROR(status))
>                               goto on_eio;
> -                     memcpy(iblk, buf + (i_lblks + i_nblks) * DEV_BSIZE,
> -                         i_tblks * DEV_BSIZE);
> -                     status = EFI_CALL(ed->blkio->WriteBlocks,
> -                         ed->blkio, ed->mediaid, lba + (i_nblks / blks),
> -                         ed->blkio->Media->BlockSize, iblk);
>               }
> +             memcpy(ibuf + DEV_BSIZE * (off - start * blks), buf,
> +                 DEV_BSIZE * nsect);
> +             status = EFI_CALL(ed->blkio->WriteBlocks,
> +                 ed->blkio, ed->mediaid, start, 
> +                 (end - start) * ed->blkio->Media->BlockSize, ibuf);
> +             if (EFI_ERROR(status))
> +                     goto on_eio;
>               break;
>       }
>       return (EFI_SUCCESS);
> 

Reply via email to