Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 15 Mar 2017 19:48:22 +0000 (UTC)
From:      Toomas Soome <tsoome@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r315326 - head/sys/boot/efi/boot1
Message-ID:  <201703151948.v2FJmMG7016152@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: tsoome
Date: Wed Mar 15 19:48:22 2017
New Revision: 315326
URL: https://svnweb.freebsd.org/changeset/base/315326

Log:
  boot1.efi: can't boot from ZFS on 4kn HDD
  
  The boot1.efi immediate issue from PR216964 is that we are reading into
  too small buffer, from UEFI spec 2.6:
  
  The size of the Buffer in bytes. This must be a multiple of the intrinsic block size of the device.
  
  The secondary issue is that LBA calculation does not check reminder from
  division.
  
  This fix does check the provided buffer size and if we read less than
  media sector size or the read offset is not aligned to sector boundary,
  we allocate bounce buffer and perform the read by single sector.
  
  PR:		216964
  Reported by:	Sergey Kozlov
  Reviewed by:	allanjude, Sergey Kozlov
  Approved by:	allanjude (mentor)
  MFC after:	2 weeks
  Differential Revision:	https://reviews.freebsd.org/D9870

Modified:
  head/sys/boot/efi/boot1/zfs_module.c

Modified: head/sys/boot/efi/boot1/zfs_module.c
==============================================================================
--- head/sys/boot/efi/boot1/zfs_module.c	Wed Mar 15 19:10:04 2017	(r315325)
+++ head/sys/boot/efi/boot1/zfs_module.c	Wed Mar 15 19:48:22 2017	(r315326)
@@ -44,23 +44,61 @@ static int
 vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
 {
 	dev_info_t *devinfo;
-	off_t lba;
+	uint64_t lba;
+	size_t size, remainder, rb_size, blksz;
+	char *bouncebuf = NULL, *rb_buf;
 	EFI_STATUS status;
 
 	devinfo = (dev_info_t *)priv;
 	lba = off / devinfo->dev->Media->BlockSize;
+	remainder = off % devinfo->dev->Media->BlockSize;
 
-	status = devinfo->dev->ReadBlocks(devinfo->dev,
-	    devinfo->dev->Media->MediaId, lba, bytes, buf);
-	if (status != EFI_SUCCESS) {
-		DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %jd, size: %zu,"
-		    " status: %lu\n", devinfo->dev,
-		    devinfo->dev->Media->MediaId, (intmax_t)lba, bytes,
-		    EFI_ERROR_CODE(status));
-		return (-1);
+	rb_buf = buf;
+	rb_size = bytes;
+
+	/*
+	 * If we have remainder from off, we need to add remainder part.
+	 * Since buffer must be multiple of the BlockSize, round it all up.
+	 */
+	size = roundup2(bytes + remainder, devinfo->dev->Media->BlockSize);
+	blksz = size;
+	if (remainder != 0 || size != bytes) {
+		rb_size = devinfo->dev->Media->BlockSize;
+		bouncebuf = malloc(rb_size);
+		if (bouncebuf == NULL) {
+			printf("vdev_read: out of memory\n");
+			return (-1);
+		}
+		rb_buf = bouncebuf;
+		blksz = rb_size - remainder;
 	}
 
+	while (bytes > 0) {
+		status = devinfo->dev->ReadBlocks(devinfo->dev,
+		    devinfo->dev->Media->MediaId, lba, rb_size, rb_buf);
+		if (EFI_ERROR(status))
+				goto error;
+		if (bytes < blksz)
+			blksz = bytes;
+		if (bouncebuf != NULL)
+			memcpy(buf, rb_buf + remainder, blksz);
+		buf = (void *)((uintptr_t)buf + blksz);
+		bytes -= blksz;
+		lba++;
+		remainder = 0;
+		blksz = rb_size;
+	}
+
+	free(bouncebuf);
 	return (0);
+
+error:
+	free(bouncebuf);
+	DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %ju, size: %zu,"
+	    " rb_size: %zu, status: %lu\n", devinfo->dev,
+	    devinfo->dev->Media->MediaId, (uintmax_t)lba, bytes, rb_size,
+	    EFI_ERROR_CODE(status));
+	return (-1);
 }
 
 static EFI_STATUS



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201703151948.v2FJmMG7016152>