NetBSD Problem Report #54554

From www@netbsd.org  Tue Sep 17 14:06:19 2019
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id AF97C7A156
	for <gnats-bugs@gnats.NetBSD.org>; Tue, 17 Sep 2019 14:06:19 +0000 (UTC)
Message-Id: <20190917140618.ADE657A1C7@mollari.NetBSD.org>
Date: Tue, 17 Sep 2019 14:06:18 +0000 (UTC)
From: simon@simonsouth.net
Reply-To: simon@simonsouth.net
To: gnats-bugs@NetBSD.org
Subject: Booting via U-Boot fails due to buffer misalignment
X-Send-Pr-Version: www-1.0

>Number:         54554
>Category:       port-evbarm
>Synopsis:       Booting via U-Boot fails due to buffer misalignment
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    jmcneill
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Sep 17 14:10:00 +0000 2019
>Closed-Date:    Sun Sep 22 19:24:14 +0000 2019
>Last-Modified:  Sun Sep 22 19:24:14 +0000 2019
>Originator:     Simon South
>Release:        CURRENT (9.99.11) 2019-09-15 12:10 UTC
>Organization:
>Environment:
NetBSD arm64 9.99.11 NetBSD 9.99.11 (GENERIC64) #8: Mon Sep 16 17:09:14 EDT 2019
>Description:
Booting NetBSD on a PINE64 ROCK64 fails using the current revision of
U-Boot in that project's git repository. NetBSD's EFI bootloader is
unable to open any kernel, with output like the following:

    >> NetBSD/evbarm EFI Boot (aarch64), Revision 1.11 (Thu Sep 12 13:07:08 UTC 2019) (from NetBSD 9.99.11)
    Press return to boot now, any other key for boot prompt
    booting netbsd - starting in 0 seconds.
    open netbsd: Invalid argument
    boot: netbsd: Invalid argument
    booting netbsd.gz - starting in 0 seconds.
    open netbsd.gz: Invalid argument
    boot: netbsd.gz: Invalid argument
    ...

The same OS image boots fine when using an earlier version of U-Boot.

This is due to a recent change to U-Boot, commit f59f0825e8 on 5
September 2019, that updates their implementation of the UEFI
ReadBlocks function so it checks the alignment of the buffer passed to
it against the alignment requested by the underlying device, as
required by the UEFI spec. NetBSD's EFI bootloader makes no effort to
ensure the buffer it passes to this function is correctly aligned,
causing (on at least some systems) attempts to open or read from files
to fail with an "Invalid argument" error as shown above.
>How-To-Repeat:
1. Build a NetBSD release image as usual and write it to a microSD
   card.

2. Build U-Boot, configured for the ROCK64, from the current version
   in the project's git repository (with a second problematic commit,
   899c3b3523, reverted), and write this to the microSD card as well.

3. Place the microSD card in the ROCK64 and attempt to boot it.
>Fix:
Functions in sys/stand/efiboot/efiblock.c that call the UEFI
environment's ReadBlocks function need to be modified to ensure they
always pass a buffer aligned as requested by the device in use.

I've made these changes locally and will attach a patch to this
PR. With the changes applied (and the second problematic U-Boot
commit, noted above, reverted) I can boot NetBSD without issue using
the current revision of U-Boot.

>Release-Note:

>Audit-Trail:
From: Simon South <simon@simonsouth.net>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: port-evbarm/54554
Date: Tue, 17 Sep 2019 11:27:50 -0400

 This is a multi-part message in MIME format.
 --------------42AFB12FB7BE06A1DACFB984
 Content-Type: text/plain; charset=utf-8; format=flowed
 Content-Transfer-Encoding: 7bit

 Here's a patch that fixes this problem by ensuring calls to ReadBlocks 
 in efiblock.c always pass a buffer aligned as requested by the device.

 In the case of efi_block_strategy(), this is done by checking the 
 alignment of the buffer passed by the caller and, if necessary, 
 allocating a second buffer, writing into it at the correct alignment and 
 copying the result back into the caller's buffer.

 Note I've taken the liberty of wrapping at 80 columns the lines I modified.

 I've tested this successfully on a PINE64 ROCK64 (only).

 -- 
 Simon South
 simon@simonsouth.net

 --------------42AFB12FB7BE06A1DACFB984
 Content-Type: text/x-patch; charset=UTF-8;
  name="54554-efiboot-align-buffers.patch"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="54554-efiboot-align-buffers.patch"

 Index: sys/stand/efiboot/efiblock.c
 ===================================================================
 RCS file: /cvsroot/src/sys/stand/efiboot/efiblock.c,v
 retrieving revision 1.5
 diff -u -r1.5 efiblock.c
 --- sys/stand/efiboot/efiblock.c	9 Mar 2019 13:16:42 -0000	1.5
 +++ sys/stand/efiboot/efiblock.c	17 Sep 2019 15:23:59 -0000
 @@ -106,19 +106,22 @@
  	struct partition *p;
  	EFI_STATUS status;
  	EFI_LBA lba;
 -	uint8_t *buf;
 +	uint8_t *buf, *aligned_buf;
  	UINT32 sz;
  	int n;

  	sz = __MAX(sizeof(d), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 +	buf = AllocatePool(sz + bdev->bio->Media->IoAlign - 1);
  	if (!buf)
  		return ENOMEM;
 +	aligned_buf = (uint8_t *)(((intptr_t)buf + bdev->bio->Media->IoAlign - 1) &
 +		~(bdev->bio->Media->IoAlign - 1));

  	lba = (((EFI_LBA)start + LABELSECTOR) * DEV_BSIZE) / bdev->bio->Media->BlockSize;
 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, lba, sz, buf);
 -	if (EFI_ERROR(status) || getdisklabel(buf, &d) != NULL) {
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		lba, sz, aligned_buf);
 +	if (EFI_ERROR(status) || getdisklabel(aligned_buf, &d) != NULL) {
  		FreePool(buf);
  		return EIO;
  	}
 @@ -159,22 +162,25 @@
  	struct mbr_sector mbr;
  	struct mbr_partition *mbr_part;
  	EFI_STATUS status;
 -	uint8_t *buf;
 +	uint8_t *buf, *aligned_buf;
  	UINT32 sz;
  	int n;

  	sz = __MAX(sizeof(mbr), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 +	buf = AllocatePool(sz + bdev->bio->Media->IoAlign - 1);
  	if (!buf)
  		return ENOMEM;
 +	aligned_buf = (uint8_t *)(((intptr_t)buf + bdev->bio->Media->IoAlign - 1) &
 +		~(bdev->bio->Media->IoAlign - 1));

 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, 0, sz, buf);
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		0, sz, aligned_buf);
  	if (EFI_ERROR(status)) {
  		FreePool(buf);
  		return EIO;
  	}
 -	memcpy(&mbr, buf, sizeof(mbr));
 +	memcpy(&mbr, aligned_buf, sizeof(mbr));
  	FreePool(buf);

  	if (le32toh(mbr.mbr_magic) != MBR_MAGIC)
 @@ -241,20 +247,23 @@
  	struct gpt_ent ent;
  	EFI_STATUS status;
  	UINT32 sz, entry;
 -	uint8_t *buf;
 +	uint8_t *buf, *aligned_buf;

  	sz = __MAX(sizeof(hdr), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 +	buf = AllocatePool(sz + bdev->bio->Media->IoAlign - 1);
  	if (!buf)
  		return ENOMEM;
 +	aligned_buf = (uint8_t *)(((intptr_t)buf + bdev->bio->Media->IoAlign - 1) &
 +		~(bdev->bio->Media->IoAlign - 1));

 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, GPT_HDR_BLKNO, sz, buf);
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		GPT_HDR_BLKNO, sz, aligned_buf);
  	if (EFI_ERROR(status)) {
  		FreePool(buf);
  		return EIO;
  	}
 -	memcpy(&hdr, buf, sizeof(hdr));
 +	memcpy(&hdr, aligned_buf, sizeof(hdr));
  	FreePool(buf);

  	if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0)
 @@ -264,18 +273,22 @@

  	sz = __MAX(le32toh(hdr.hdr_entsz) * le32toh(hdr.hdr_entries), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 +	buf = AllocatePool(sz + bdev->bio->Media->IoAlign - 1);
  	if (!buf)
  		return ENOMEM;
 +	aligned_buf = (uint8_t *)(((intptr_t)buf + bdev->bio->Media->IoAlign - 1) &
 +		~(bdev->bio->Media->IoAlign - 1));

 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, le64toh(hdr.hdr_lba_table), sz, buf);
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		le64toh(hdr.hdr_lba_table), sz, aligned_buf);
  	if (EFI_ERROR(status)) {
  		FreePool(buf);
  		return EIO;
  	}

  	for (entry = 0; entry < le32toh(hdr.hdr_entries); entry++) {
 -		memcpy(&ent, buf + (entry * le32toh(hdr.hdr_entsz)), sizeof(ent));
 +		memcpy(&ent, aligned_buf + (entry * le32toh(hdr.hdr_entsz)),
 +			sizeof(ent));
  		efi_block_find_partitions_gpt_entry(bdev, &hdr, &ent, entry);
  	}

 @@ -493,6 +506,7 @@
  {
  	struct efi_block_part *bpart = devdata;
  	EFI_STATUS status;
 +	void *allocated_buf, *aligned_buf;

  	if (rw != F_READ)
  		return EROFS;
 @@ -518,9 +532,30 @@
  		return EINVAL;
  	}

 -	status = uefi_call_wrapper(bpart->bdev->bio->ReadBlocks, 5, bpart->bdev->bio, bpart->bdev->media_id, dblk, size, buf);
 -	if (EFI_ERROR(status))
 +	if (((intptr_t)buf & ~(bpart->bdev->bio->Media->IoAlign - 1)) == 0) {
 +		allocated_buf = NULL;
 +		aligned_buf = buf;
 +	} else {
 +		allocated_buf = AllocatePool(size +
 +			bpart->bdev->bio->Media->IoAlign - 1);
 +		if (!allocated_buf)
 +			return ENOMEM;
 +		aligned_buf = (void *)(((intptr_t)allocated_buf +
 +			bpart->bdev->bio->Media->IoAlign - 1) &
 +			~(bpart->bdev->bio->Media->IoAlign - 1));
 +	}
 +
 +	status = uefi_call_wrapper(bpart->bdev->bio->ReadBlocks, 5,
 +		bpart->bdev->bio, bpart->bdev->media_id, dblk, size, aligned_buf);
 +	if (EFI_ERROR(status)) {
 +		if (allocated_buf)
 +			FreePool(allocated_buf);
  		return EIO;
 +	}
 +	if (allocated_buf) {
 +		memcpy(buf, aligned_buf, size);
 +		FreePool(allocated_buf);
 +	}

  	*rsize = size;


 --------------42AFB12FB7BE06A1DACFB984--

From: Simon South <simon@simonsouth.net>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: port-evbarm/54554
Date: Tue, 17 Sep 2019 11:59:37 -0400

 With regards to testing I should add that U-Boot works to boot NetBSD on 
 the ROCK64 only if it (U-Boot) is built using the binary-only TPL 
 supplied by Rockchip at https://github.com/rockchip-linux/rkbin. NetBSD 
 currently fails to boot if U-Boot's own TPL is used.

 (I'll be submitting separate PRs for this issue and the issue caused by 
 U-Boot's commit 899c3b3523, which removes code related to the RK3328's 
 clock.)

 -- 
 Simon South
 simon@simonsouth.net

From: Simon South <simon@simonsouth.net>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: port-evbarm/54554
Date: Wed, 18 Sep 2019 07:36:47 -0400

 It turns out this patch fixes the second problem I encountered with 
 U-Boot, too, so the current revision of U-Boot can be used (with 
 Rockchip's TPL) without needing to revert any commits.

 The issue with NetBSD failing to boot using U-Boot's TPL I've submitted 
 as port-evbarm/54557:

 https://gnats.netbsd.org/54557

 -- 
 Simon South
 simon@simonsouth.net

From: Simon South <simon@simonsouth.net>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: port-evbarm/54554
Date: Wed, 18 Sep 2019 07:58:38 -0400

 Just spotted a problem with the patch: The tilde on line 535 of the 
 patched file needs to be removed for the alignment check to work correctly.

 In other words, sys/stand/efiboot/efiblock.c:535 should be changed from

      if (((intptr_t)buf & ~(bpart->bdev->bio->Media->IoAlign - 1)) == 0) {

 to

      if (((intptr_t)buf & (bpart->bdev->bio->Media->IoAlign - 1)) == 0) {

 -- 
 Simon South
 simon@simonsouth.net

Responsible-Changed-From-To: port-evbarm-maintainer->jmcneill
Responsible-Changed-By: jmcneill@NetBSD.org
Responsible-Changed-When: Wed, 18 Sep 2019 13:35:19 +0000
Responsible-Changed-Why:
take


From: Jared McNeill <jmcneill@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc: port-evbarm-maintainer@netbsd.org, netbsd-bugs@netbsd.org, 
    gnats-admin@netbsd.org, simon@simonsouth.net
Subject: Re: port-evbarm/54554 (Booting via U-Boot fails due to buffer
 misalignment)
Date: Wed, 18 Sep 2019 13:48:24 +0000 (UTC)

 Thanks for looking at this!

 According to UEFI IoAlign may be 0 or 1 ("no alignment required"). Can you 
 update the patch to take this into consideration?

From: Simon South <simon@simonsouth.net>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: port-evbarm/54554 (Booting via U-Boot fails due to buffer
 misalignment)
Date: Wed, 18 Sep 2019 10:00:52 -0400

 On 2019-09-18 9:48 a.m., Jared McNeill wrote:
 > According to UEFI IoAlign may be 0 or 1 ("no alignment required"). Can 
 > you update the patch to take this into consideration?

 Certainly, but that doesn't seem to match U-Boot's interpretation of the 
 IoAlign field. Where are you reading this description? (And how would we 
 know what to align the buffer to when IoAlign is 1?)

 The relevant code[1] in U-Boot is

      /* media->io_align is a power of 2 */
      if ((uintptr_t)buffer & (this->media->io_align - 1))
          return EFI_INVALID_PARAMETER;

 which clearly uses IoAlign directly to check for alignment.

 [1] 
 https://gitlab.denx.de/u-boot/u-boot/blob/a9fa70b7b7167487affc5d919e541872c99e814b/lib/efi_loader/efi_disk.c#L124

 -- 
 Simon South
 simon@simonsouth.net

From: Simon South <simon@simonsouth.net>
To: gnats-bugs@netbsd.org, jmcneill@netbsd.org
Cc: 
Subject: Re: port-evbarm/54554 (Booting via U-Boot fails due to buffer
 misalignment)
Date: Wed, 18 Sep 2019 10:18:37 -0400

 D'oh, of course you mean _in addition_ IoAlign can have either of those 
 values.

 Yes, I'll update the patch to handle that case.

 -- 
 Simon South
 simon@simonsouth.net

From: Simon South <simon@simonsouth.net>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: port-evbarm/54554 (Booting via U-Boot fails due to buffer
 misalignment)
Date: Wed, 18 Sep 2019 17:42:48 -0400

 This is a multi-part message in MIME format.
 --------------6AB8168758FF6A2572F5CA1E
 Content-Type: text/plain; charset=utf-8; format=flowed
 Content-Transfer-Encoding: 7bit

 On 2019-09-18 9:48 a.m., Jared McNeill wrote:
 > According to UEFI IoAlign may be 0 or 1 ("no alignment required"). Can 
 > you update the patch to take this into consideration?

 Here's an updated patch that handles this case. I've also reformatted 
 the modified code slightly to match the style guide.

 I've tested this on my ROCK64 and it seems to work fine.

 -- 
 Simon South
 simon@simonsouth.net

 --------------6AB8168758FF6A2572F5CA1E
 Content-Type: text/x-patch; charset=UTF-8;
  name="54554-efiboot-align-buffers-2.patch"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="54554-efiboot-align-buffers-2.patch"

 Index: sys/stand/efiboot/efiblock.c
 ===================================================================
 RCS file: /cvsroot/src/sys/stand/efiboot/efiblock.c,v
 retrieving revision 1.5
 diff -u -r1.5 efiblock.c
 --- sys/stand/efiboot/efiblock.c	9 Mar 2019 13:16:42 -0000	1.5
 +++ sys/stand/efiboot/efiblock.c	18 Sep 2019 21:23:14 -0000
 @@ -106,19 +106,28 @@
  	struct partition *p;
  	EFI_STATUS status;
  	EFI_LBA lba;
 -	uint8_t *buf;
 +	uint8_t *buf, *aligned_buf;
  	UINT32 sz;
  	int n;

  	sz = __MAX(sizeof(d), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 -	if (!buf)
 -		return ENOMEM;
 +
 +	if (bdev->bio->Media->IoAlign <= 1) {
 +		if ((buf = AllocatePool(sz)) == NULL)
 +			return ENOMEM;
 +		aligned_buf = buf;
 +	} else {
 +		if ((buf = AllocatePool(sz + bdev->bio->Media->IoAlign - 1)) == NULL)
 +			return ENOMEM;
 +		aligned_buf = (uint8_t *)(((intptr_t)buf +
 +			bdev->bio->Media->IoAlign - 1) & ~(bdev->bio->Media->IoAlign - 1));
 +	}

  	lba = (((EFI_LBA)start + LABELSECTOR) * DEV_BSIZE) / bdev->bio->Media->BlockSize;
 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, lba, sz, buf);
 -	if (EFI_ERROR(status) || getdisklabel(buf, &d) != NULL) {
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		lba, sz, aligned_buf);
 +	if (EFI_ERROR(status) || getdisklabel(aligned_buf, &d) != NULL) {
  		FreePool(buf);
  		return EIO;
  	}
 @@ -159,22 +168,31 @@
  	struct mbr_sector mbr;
  	struct mbr_partition *mbr_part;
  	EFI_STATUS status;
 -	uint8_t *buf;
 +	uint8_t *buf, *aligned_buf;
  	UINT32 sz;
  	int n;

  	sz = __MAX(sizeof(mbr), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 -	if (!buf)
 -		return ENOMEM;

 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, 0, sz, buf);
 +	if (bdev->bio->Media->IoAlign <= 1) {
 +		if ((buf = AllocatePool(sz)) == NULL)
 +			return ENOMEM;
 +		aligned_buf = buf;
 +	} else {
 +		if ((buf = AllocatePool(sz + bdev->bio->Media->IoAlign - 1)) == NULL)
 +			return ENOMEM;
 +		aligned_buf = (uint8_t *)(((intptr_t)buf +
 +			bdev->bio->Media->IoAlign - 1) & ~(bdev->bio->Media->IoAlign - 1));
 +	}
 +
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		0, sz, aligned_buf);
  	if (EFI_ERROR(status)) {
  		FreePool(buf);
  		return EIO;
  	}
 -	memcpy(&mbr, buf, sizeof(mbr));
 +	memcpy(&mbr, aligned_buf, sizeof(mbr));
  	FreePool(buf);

  	if (le32toh(mbr.mbr_magic) != MBR_MAGIC)
 @@ -241,20 +259,29 @@
  	struct gpt_ent ent;
  	EFI_STATUS status;
  	UINT32 sz, entry;
 -	uint8_t *buf;
 +	uint8_t *buf, *aligned_buf;

  	sz = __MAX(sizeof(hdr), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 -	if (!buf)
 -		return ENOMEM;

 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, GPT_HDR_BLKNO, sz, buf);
 +	if (bdev->bio->Media->IoAlign <= 1) {
 +		if ((buf = AllocatePool(sz)) == NULL)
 +			return ENOMEM;
 +		aligned_buf = buf;
 +	} else {
 +		if ((buf = AllocatePool(sz + bdev->bio->Media->IoAlign - 1)) == NULL)
 +			return ENOMEM;
 +		aligned_buf = (uint8_t *)(((intptr_t)buf +
 +			bdev->bio->Media->IoAlign - 1) & ~(bdev->bio->Media->IoAlign - 1));
 +	}
 +
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		GPT_HDR_BLKNO, sz, aligned_buf);
  	if (EFI_ERROR(status)) {
  		FreePool(buf);
  		return EIO;
  	}
 -	memcpy(&hdr, buf, sizeof(hdr));
 +	memcpy(&hdr, aligned_buf, sizeof(hdr));
  	FreePool(buf);

  	if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0)
 @@ -264,18 +291,28 @@

  	sz = __MAX(le32toh(hdr.hdr_entsz) * le32toh(hdr.hdr_entries), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 -	if (!buf)
 -		return ENOMEM;

 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, le64toh(hdr.hdr_lba_table), sz, buf);
 +	if (bdev->bio->Media->IoAlign <= 1) {
 +		if ((buf = AllocatePool(sz)) == NULL)
 +			return ENOMEM;
 +		aligned_buf = buf;
 +	} else {
 +		if ((buf = AllocatePool(sz + bdev->bio->Media->IoAlign - 1)) == NULL)
 +			return ENOMEM;
 +		aligned_buf = (uint8_t *)(((intptr_t)buf +
 +			bdev->bio->Media->IoAlign - 1) & ~(bdev->bio->Media->IoAlign - 1));
 +	}
 +
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		le64toh(hdr.hdr_lba_table), sz, aligned_buf);
  	if (EFI_ERROR(status)) {
  		FreePool(buf);
  		return EIO;
  	}

  	for (entry = 0; entry < le32toh(hdr.hdr_entries); entry++) {
 -		memcpy(&ent, buf + (entry * le32toh(hdr.hdr_entsz)), sizeof(ent));
 +		memcpy(&ent, aligned_buf + (entry * le32toh(hdr.hdr_entsz)),
 +			sizeof(ent));
  		efi_block_find_partitions_gpt_entry(bdev, &hdr, &ent, entry);
  	}

 @@ -493,6 +530,7 @@
  {
  	struct efi_block_part *bpart = devdata;
  	EFI_STATUS status;
 +	void *allocated_buf, *aligned_buf;

  	if (rw != F_READ)
  		return EROFS;
 @@ -518,9 +556,30 @@
  		return EINVAL;
  	}

 -	status = uefi_call_wrapper(bpart->bdev->bio->ReadBlocks, 5, bpart->bdev->bio, bpart->bdev->media_id, dblk, size, buf);
 -	if (EFI_ERROR(status))
 +	if ((bpart->bdev->bio->Media->IoAlign <= 1) ||
 +		((intptr_t)buf & (bpart->bdev->bio->Media->IoAlign - 1)) == 0) {
 +		allocated_buf = NULL;
 +		aligned_buf = buf;
 +	} else {
 +		if ((allocated_buf = AllocatePool(size +
 +			bpart->bdev->bio->Media->IoAlign - 1)) == NULL)
 +			return ENOMEM;
 +		aligned_buf = (void *)(((intptr_t)allocated_buf +
 +			bpart->bdev->bio->Media->IoAlign - 1) &
 +			~(bpart->bdev->bio->Media->IoAlign - 1));
 +	}
 +
 +	status = uefi_call_wrapper(bpart->bdev->bio->ReadBlocks, 5,
 +		bpart->bdev->bio, bpart->bdev->media_id, dblk, size, aligned_buf);
 +	if (EFI_ERROR(status)) {
 +		if (allocated_buf != NULL)
 +			FreePool(allocated_buf);
  		return EIO;
 +	}
 +	if (allocated_buf != NULL) {
 +		memcpy(buf, aligned_buf, size);
 +		FreePool(allocated_buf);
 +	}

  	*rsize = size;


 --------------6AB8168758FF6A2572F5CA1E--

From: Jared McNeill <jmcneill@invisible.ca>
To: gnats-bugs@netbsd.org
Cc: jmcneill@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org, 
    simon@simonsouth.net
Subject: Re: port-evbarm/54554 (Booting via U-Boot fails due to buffer
 misalignment)
Date: Thu, 19 Sep 2019 07:50:08 -0300 (ADT)

 There's a bit of code duplication here, maybe we should break this out 
 into a separate function? Even if you have to memcpy for each transfer, I 
 think we can clean things up a bit. What do you think?

From: Simon South <simon@simonsouth.net>
To: Jared McNeill <jmcneill@invisible.ca>, gnats-bugs@netbsd.org
Cc: 
Subject: Re: port-evbarm/54554 (Booting via U-Boot fails due to buffer
 misalignment)
Date: Thu, 19 Sep 2019 07:43:52 -0400

 On 2019-09-19 6:50 a.m., Jared McNeill wrote:
 > There's a bit of code duplication here, maybe we should break this out 
 > into a separate function?

 I had the same thought but resisted doing this, only because

 1. The original code was also duplicated in each spot, and as I'm new to 
 NetBSD I'm trying to change as little as possible; and

 2. The resulting function signature seems a bit ugly, something along 
 the lines of

         static void *
         efi_block_allocate_device_buffer(struct efi_block_dev *bdev, 
 UINTN size, void **buffer_start)

     since we need to return a pointer both to the "base" buffer as well 
 as to its first aligned element.

 Does that seem reasonable, though? I'm happy to make this change as well 
 but you would have a better feel for it than me at this point.

 -- 
 Simon South
 simon@simonsouth.net

From: Simon South <simon@simonsouth.net>
To: Jared McNeill <jmcneill@invisible.ca>, gnats-bugs@netbsd.org
Cc: 
Subject: Re: port-evbarm/54554 (Booting via U-Boot fails due to buffer
 misalignment)
Date: Thu, 19 Sep 2019 09:06:02 -0400

 This is a multi-part message in MIME format.
 --------------BA0A3E4561685F2555A0034B
 Content-Type: text/plain; charset=utf-8; format=flowed
 Content-Transfer-Encoding: 7bit

 Here's how I'd refactor this. Tests fine on my ROCK64.

 -- 
 Simon South
 simon@simonsouth.net

 --------------BA0A3E4561685F2555A0034B
 Content-Type: text/x-patch; charset=UTF-8;
  name="54554-efiboot-align-buffers-3.patch"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="54554-efiboot-align-buffers-3.patch"

 Index: sys/stand/efiboot/efiblock.c
 ===================================================================
 RCS file: /cvsroot/src/sys/stand/efiboot/efiblock.c,v
 retrieving revision 1.5
 diff -u -r1.5 efiblock.c
 --- sys/stand/efiboot/efiblock.c	9 Mar 2019 13:16:42 -0000	1.5
 +++ sys/stand/efiboot/efiblock.c	19 Sep 2019 12:48:06 -0000
 @@ -98,6 +98,23 @@
  	MD5Final(bpart->hash, &md5ctx);
  }

 +static void *
 +efi_block_allocate_device_buffer(struct efi_block_dev *bdev, UINTN size,
 +	void **buf_start)
 +{
 +	void *buf;
 +
 +	if (bdev->bio->Media->IoAlign <= 1)
 +		*buf_start = buf = AllocatePool(size);
 +	else {
 +		buf = AllocatePool(size + bdev->bio->Media->IoAlign - 1);
 +		*buf_start = (buf == NULL) ? NULL : (void *)(((intptr_t)buf +
 +			bdev->bio->Media->IoAlign - 1) & ~(bdev->bio->Media->IoAlign - 1));
 +	}
 +
 +	return buf;
 +}
 +
  static int
  efi_block_find_partitions_disklabel(struct efi_block_dev *bdev, struct mbr_sector *mbr, uint32_t start, uint32_t size)
  {
 @@ -106,19 +123,19 @@
  	struct partition *p;
  	EFI_STATUS status;
  	EFI_LBA lba;
 -	uint8_t *buf;
 +	void *buf, *buf_start;
  	UINT32 sz;
  	int n;

  	sz = __MAX(sizeof(d), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 -	if (!buf)
 +	if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
  		return ENOMEM;

  	lba = (((EFI_LBA)start + LABELSECTOR) * DEV_BSIZE) / bdev->bio->Media->BlockSize;
 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, lba, sz, buf);
 -	if (EFI_ERROR(status) || getdisklabel(buf, &d) != NULL) {
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		lba, sz, buf_start);
 +	if (EFI_ERROR(status) || getdisklabel(buf_start, &d) != NULL) {
  		FreePool(buf);
  		return EIO;
  	}
 @@ -159,22 +176,22 @@
  	struct mbr_sector mbr;
  	struct mbr_partition *mbr_part;
  	EFI_STATUS status;
 -	uint8_t *buf;
 +	void *buf, *buf_start;
  	UINT32 sz;
  	int n;

  	sz = __MAX(sizeof(mbr), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 -	if (!buf)
 +	if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
  		return ENOMEM;

 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, 0, sz, buf);
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		0, sz, buf_start);
  	if (EFI_ERROR(status)) {
  		FreePool(buf);
  		return EIO;
  	}
 -	memcpy(&mbr, buf, sizeof(mbr));
 +	memcpy(&mbr, buf_start, sizeof(mbr));
  	FreePool(buf);

  	if (le32toh(mbr.mbr_magic) != MBR_MAGIC)
 @@ -240,21 +257,21 @@
  	struct gpt_hdr hdr;
  	struct gpt_ent ent;
  	EFI_STATUS status;
 +	void *buf, *buf_start;
  	UINT32 sz, entry;
 -	uint8_t *buf;

  	sz = __MAX(sizeof(hdr), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 -	if (!buf)
 +	if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
  		return ENOMEM;

 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, GPT_HDR_BLKNO, sz, buf);
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		GPT_HDR_BLKNO, sz, buf_start);
  	if (EFI_ERROR(status)) {
  		FreePool(buf);
  		return EIO;
  	}
 -	memcpy(&hdr, buf, sizeof(hdr));
 +	memcpy(&hdr, buf_start, sizeof(hdr));
  	FreePool(buf);

  	if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0)
 @@ -264,18 +281,19 @@

  	sz = __MAX(le32toh(hdr.hdr_entsz) * le32toh(hdr.hdr_entries), bdev->bio->Media->BlockSize);
  	sz = roundup(sz, bdev->bio->Media->BlockSize);
 -	buf = AllocatePool(sz);
 -	if (!buf)
 +	if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
  		return ENOMEM;

 -	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id, le64toh(hdr.hdr_lba_table), sz, buf);
 +	status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
 +		le64toh(hdr.hdr_lba_table), sz, buf_start);
  	if (EFI_ERROR(status)) {
  		FreePool(buf);
  		return EIO;
  	}

  	for (entry = 0; entry < le32toh(hdr.hdr_entries); entry++) {
 -		memcpy(&ent, buf + (entry * le32toh(hdr.hdr_entsz)), sizeof(ent));
 +		memcpy(&ent, buf_start + (entry * le32toh(hdr.hdr_entsz)),
 +			sizeof(ent));
  		efi_block_find_partitions_gpt_entry(bdev, &hdr, &ent, entry);
  	}

 @@ -493,6 +511,7 @@
  {
  	struct efi_block_part *bpart = devdata;
  	EFI_STATUS status;
 +	void *allocated_buf, *aligned_buf;

  	if (rw != F_READ)
  		return EROFS;
 @@ -518,9 +537,25 @@
  		return EINVAL;
  	}

 -	status = uefi_call_wrapper(bpart->bdev->bio->ReadBlocks, 5, bpart->bdev->bio, bpart->bdev->media_id, dblk, size, buf);
 -	if (EFI_ERROR(status))
 +	if ((bpart->bdev->bio->Media->IoAlign <= 1) ||
 +		((intptr_t)buf & (bpart->bdev->bio->Media->IoAlign - 1)) == 0) {
 +		allocated_buf = NULL;
 +		aligned_buf = buf;
 +	} else if ((allocated_buf = efi_block_allocate_device_buffer(bpart->bdev,
 +		size, &aligned_buf)) == NULL)
 +		return ENOMEM;
 +
 +	status = uefi_call_wrapper(bpart->bdev->bio->ReadBlocks, 5,
 +		bpart->bdev->bio, bpart->bdev->media_id, dblk, size, aligned_buf);
 +	if (EFI_ERROR(status)) {
 +		if (allocated_buf != NULL)
 +			FreePool(allocated_buf);
  		return EIO;
 +	}
 +	if (allocated_buf != NULL) {
 +		memcpy(buf, aligned_buf, size);
 +		FreePool(allocated_buf);
 +	}

  	*rsize = size;


 --------------BA0A3E4561685F2555A0034B--

From: "Jared D. McNeill" <jmcneill@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/54554 CVS commit: src/sys/stand/efiboot
Date: Sat, 21 Sep 2019 10:19:37 +0000

 Module Name:	src
 Committed By:	jmcneill
 Date:		Sat Sep 21 10:19:37 UTC 2019

 Modified Files:
 	src/sys/stand/efiboot: efiblock.c

 Log Message:
 Honour block device's IO alignment requirements.

 Patch from Simon South <simon@simonsouth.net> in PR# 54554


 To generate a diff of this commit:
 cvs rdiff -u -r1.5 -r1.6 src/sys/stand/efiboot/efiblock.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: open->pending-pullups
State-Changed-By: jmcneill@NetBSD.org
State-Changed-When: Sat, 21 Sep 2019 10:20:31 +0000
State-Changed-Why:
Patch applied, thanks!


From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/54554 CVS commit: [netbsd-9] src/sys/stand/efiboot
Date: Sun, 22 Sep 2019 12:37:39 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Sun Sep 22 12:37:39 UTC 2019

 Modified Files:
 	src/sys/stand/efiboot [netbsd-9]: efiblock.c

 Log Message:
 Pull up following revision(s) (requested by jmcneill in ticket #225):

 	sys/stand/efiboot/efiblock.c: revision 1.6

 Honour block device's IO alignment requirements.
 Patch from Simon South <simon%simonsouth.net@localhost> in PR# 54554


 To generate a diff of this commit:
 cvs rdiff -u -r1.5 -r1.5.6.1 src/sys/stand/efiboot/efiblock.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: pending-pullups->closed
State-Changed-By: jmcneill@NetBSD.org
State-Changed-When: Sun, 22 Sep 2019 19:24:14 +0000
State-Changed-Why:
Pulled up to netbsd-9.


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.43 2018/01/16 07:36:43 maya Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2017 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.