]> xenbits.xensource.com Git - freebsd.git/commitdiff
loader.efi: efipart needs to use ioalign
authortsoome <tsoome@FreeBSD.org>
Tue, 17 Sep 2019 13:50:25 +0000 (13:50 +0000)
committertsoome <tsoome@FreeBSD.org>
Tue, 17 Sep 2019 13:50:25 +0000 (13:50 +0000)
UEFI specification 2.7A, EFI_BLOCK_IO_PROTOCOL, page 566.

The ioalign property does define the alignment of data buffer.

If the alignment is required and our buffer is not aligned, or if
the data buffer is not multiple of Blocksize, we need to use bounce buffer
to perform the block IO. This is much like with BIOS version, except
there the INT13 needs buffer to be located in low memory.

Additionally, we need to handle disk writes properly.

stand/efi/libefi/efipart.c

index 973ad8f7722f678d6f58c4c68de0dde383dcbc1b..2affab1c42745de0ac3babecb508d6e09697dc49 100644 (file)
@@ -64,6 +64,9 @@ static int efipart_printhd(int);
 #define        PNP0700 0x700
 #define        PNP0701 0x701
 
+/* Bounce buffer max size */
+#define        BIO_BUFFER_SIZE 0x4000
+
 struct devsw efipart_fddev = {
        .dv_name = "fd",
        .dv_type = DEVT_FD,
@@ -266,6 +269,12 @@ efipart_inithandles(void)
                        continue;
                }
 
+               /* Allowed values are 0, 1 and power of 2. */
+               if (blkio->Media->IoAlign > 1 &&
+                   !powerof2(blkio->Media->IoAlign)) {
+                       continue;
+               }
+
                /* This is bad. */
                if ((pd = calloc(1, sizeof(*pd))) == NULL) {
                        printf("efipart_inithandles: Out of memory.\n");
@@ -979,8 +988,10 @@ efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
        EFI_BLOCK_IO *blkio;
        uint64_t off, disk_blocks, d_offset = 0;
        char *blkbuf;
-       size_t blkoff, blksz;
-       int error;
+       size_t blkoff, blksz, bio_size;
+       unsigned ioalign;
+       bool need_buf;
+       int rc;
        uint64_t diskend, readstart;
 
        if (dev == NULL || blk < 0)
@@ -1028,40 +1039,118 @@ efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
                size = size * blkio->Media->BlockSize;
        }
 
-       if (rsize != NULL)
-               *rsize = size;
-
+       need_buf = true;
+       /* Do we need bounce buffer? */
        if ((size % blkio->Media->BlockSize == 0) &&
            (off % blkio->Media->BlockSize == 0))
-               return (efipart_readwrite(blkio, rw,
-                   off / blkio->Media->BlockSize,
-                   size / blkio->Media->BlockSize, buf));
+               need_buf = false;
+
+       /* Do we have IO alignment requirement? */
+       ioalign = blkio->Media->IoAlign;
+       if (ioalign == 0)
+               ioalign++;
+
+       if (ioalign > 1 && (uintptr_t)buf != roundup2((uintptr_t)buf, ioalign))
+               need_buf = true;
+
+       if (need_buf) {
+               for (bio_size = BIO_BUFFER_SIZE; bio_size > 0;
+                   bio_size -= blkio->Media->BlockSize) {
+                       blkbuf = memalign(ioalign, bio_size);
+                       if (blkbuf != NULL)
+                               break;
+               }
+       } else {
+               blkbuf = buf;
+               bio_size = size;
+       }
 
-       /*
-        * The buffer size is not a multiple of the media block size.
-        */
-       blkbuf = malloc(blkio->Media->BlockSize);
        if (blkbuf == NULL)
                return (ENOMEM);
 
-       error = 0;
+       if (rsize != NULL)
+               *rsize = size;
+
+       rc = 0;
        blk = off / blkio->Media->BlockSize;
        blkoff = off % blkio->Media->BlockSize;
-       blksz = blkio->Media->BlockSize - blkoff;
+
        while (size > 0) {
-               error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
-               if (error)
+               size_t x = min(size, bio_size);
+
+               if (x < blkio->Media->BlockSize)
+                       x = 1;
+               else
+                       x /= blkio->Media->BlockSize;
+
+               switch (rw & F_MASK) {
+               case F_READ:
+                       blksz = blkio->Media->BlockSize * x - blkoff;
+                       if (size < blksz)
+                               blksz = size;
+
+                       rc = efipart_readwrite(blkio, rw, blk, x, blkbuf);
+                       if (rc != 0)
+                               goto error;
+
+                       if (need_buf)
+                               bcopy(blkbuf + blkoff, buf, blksz);
                        break;
-               if (size < blksz)
-                       blksz = size;
-               bcopy(blkbuf + blkoff, buf, blksz);
+               case F_WRITE:
+                       rc = 0;
+                       if (blkoff != 0) {
+                               /*
+                                * We got offset to sector, read 1 sector to
+                                * blkbuf.
+                                */
+                               x = 1;
+                               blksz = blkio->Media->BlockSize - blkoff;
+                               blksz = min(blksz, size);
+                               rc = efipart_readwrite(blkio, F_READ, blk, x,
+                                   blkbuf);
+                       } else if (size < blkio->Media->BlockSize) {
+                               /*
+                                * The remaining block is not full
+                                * sector. Read 1 sector to blkbuf.
+                                */
+                               x = 1;
+                               blksz = size;
+                               rc = efipart_readwrite(blkio, F_READ, blk, x,
+                                   blkbuf);
+                       } else {
+                               /* We can write full sector(s). */
+                               blksz = blkio->Media->BlockSize * x;
+                       }
+
+                       if (rc != 0)
+                               goto error;
+                       /*
+                        * Put your Data In, Put your Data out,
+                        * Put your Data In, and shake it all about
+                        */
+                       if (need_buf)
+                               bcopy(buf, blkbuf + blkoff, blksz);
+                       rc = efipart_readwrite(blkio, F_WRITE, blk, x, blkbuf);
+                       if (rc != 0)
+                               goto error;
+                       break;
+               default:
+                       /* DO NOTHING */
+                       rc = EROFS;
+                       goto error;
+               }
+
+               blkoff = 0;
                buf += blksz;
                size -= blksz;
-               blk++;
-               blkoff = 0;
-               blksz = blkio->Media->BlockSize;
+               blk += x;
        }
 
-       free(blkbuf);
-       return (error);
+error:
+       if (rsize != NULL)
+               *rsize -= size;
+
+       if (need_buf)
+               free(blkbuf);
+       return (rc);
 }