RPi4B: modern firmware vs. Device tree loaded to 0x4000 (size 0xbe0c) [fails] vs. to 0x1f0000 (size 0xbd90) [works]?

Mark Millard marklmi at yahoo.com
Mon Oct 12 17:37:56 UTC 2020


On 2020-Oct-12, at 04:35, Klaus Cucinauomo via freebsd-arm <freebsd-arm at freebsd.org> wrote:

>> Am 12.10.2020 um 01:13 schrieb Robert Crowston <crowston at protonmail.com>:
>> ...
>>>> mmc0 is current device
>>> 
>>> should not be mmc0 if you boot from USB/SSD
>> 
>> I'm still using sd cards.
>> ...
>> 
>> — RHC.
> 
> The one and only pcie-BOSS itself is forced by armstubs to boot from a poor 10Mb/sWrite SD-card-fingernail…
> This terrible misery must end. ;-) Ha Ha,
> …... that’s why we want to use the xhci-boot feature of 2020.10
> 

Well, depending on what all one wants to do with the RPi4B:

There is an alternative that boots via USB3 SSDs just fine
(no microsd card) via modern firmware and modern .dtb files,
even for 8 MiByte RPI4B's that have the MSD USB updates:
uefi/ACPI v1.20 . See:

https://github.com/pftf/RPi4/releases/tag/v1.20/

and the installation notes at:

https://github.com/pftf/RPi4


The FreeBSD fix is known so that 3072 MiByte does not have to
be selected: the full memory can be used. (Add the use of a
"subtract 1" at a particular point in the the patch for handling
_DMA ACPI information. See https://reviews.freebsd.org/D25219 and
https://lists.freebsd.org/pipermail/freebsd-arm/2020-October/022500.html
for more. I'll include the svnlite diff later below, one
based on head -r365932 .)

There are the tradeoffs of needing to use USB based
devices for:

Ethernet
microsd cards

(I do not know about controlling pins and such, thus the
"depending on what all one wants to do".)

There is also the performance tradeoff in that the uefi/ACPI
context seems to ignore sdram_freq_min=3200 in config.txt .
(Last I checked the u-boot context was putting that to use.)


For reference (probable whitespace oddities from being
sent via e-mail):

# svnlite diff /usr/src/sys/dev/acpica/acpi.c
Index: /usr/src/sys/dev/acpica/acpi.c
===================================================================
--- /usr/src/sys/dev/acpica/acpi.c	(revision 365932)
+++ /usr/src/sys/dev/acpica/acpi.c	(working copy)
@@ -184,6 +184,7 @@
 static void	acpi_hint_device_unit(device_t acdev, device_t child,
 		    const char *name, int *unitp);
 static void	acpi_reset_interfaces(device_t dev);
+static bus_dma_tag_t acpi_get_dma_tag(device_t dev, device_t child);
 
 static device_method_t acpi_methods[] = {
     /* Device interface */
@@ -218,6 +219,7 @@
     DEVMETHOD(bus_hint_device_unit,	acpi_hint_device_unit),
     DEVMETHOD(bus_get_cpus,		acpi_get_cpus),
     DEVMETHOD(bus_get_domain,		acpi_get_domain),
+    DEVMETHOD(bus_get_dma_tag,		acpi_get_dma_tag),
 
     /* ACPI bus */
     DEVMETHOD(acpi_id_probe,		acpi_device_id_probe),
@@ -431,6 +433,123 @@
     return (0);
 }
 
+struct dma_limits {
+	bus_addr_t lowaddr;
+};
+
+static ACPI_STATUS
+dma_on_resource(ACPI_RESOURCE *res, void *arg)
+{
+	struct dma_limits *limits = arg;
+	bus_addr_t min, len;
+
+	/*
+	 * The minimum and maximum are device-side. To get the CPU-side minimum,
+	 * we add the translation offset. This can overflow to signify lower addresses
+	 * on the CPU than the device, e.g. "Bus 0xC0000000 -> CPU 0x00000000"
+	 * on the RPi4 is represented as 0xC0000000 min + 0xFFFFFFFF40000000 offset.
+	 */
+
+	switch (res->Type) {
+	case ACPI_RESOURCE_TYPE_ADDRESS16:
+		min = (uint16_t)(res->Data.Address16.Address.Minimum +
+		    res->Data.Address16.Address.TranslationOffset);
+		len = res->Data.Address16.Address.AddressLength;
+		break;
+	case ACPI_RESOURCE_TYPE_ADDRESS32:
+		min = (uint32_t)(res->Data.Address32.Address.Minimum +
+		    res->Data.Address32.Address.TranslationOffset);
+		len = res->Data.Address32.Address.AddressLength;
+		break;
+	case ACPI_RESOURCE_TYPE_ADDRESS64:
+		min = (uint64_t)(res->Data.Address64.Address.Minimum +
+		    res->Data.Address64.Address.TranslationOffset);
+		len = res->Data.Address64.Address.AddressLength;
+		break;
+	case ACPI_RESOURCE_TYPE_END_TAG:
+		return (AE_OK);
+	default:
+		printf("ACPI: warning: DMA limit with unsupported resource type %d\n",
+			res->Type);
+		return (AE_OK);
+	}
+
+	if (min != 0)
+		printf("ACPI: warning: DMA limit with non-zero minimum address"
+		    " not supported yet\n");
+
+	limits->lowaddr = MIN(limits->lowaddr, min + len);
+
+	return (AE_OK);
+}
+
+static int
+get_dma_tag(ACPI_HANDLE handle, bus_dma_tag_t *result)
+{
+	ACPI_HANDLE parent;
+	unsigned int coherent;
+	struct dma_limits limits = {
+		.lowaddr = BUS_SPACE_MAXADDR,
+	};
+
+	if (ACPI_FAILURE(AcpiWalkResources(handle, "_DMA",
+	    dma_on_resource, (void *)&limits))) {
+		/* Inherit resources from parent handle if we don't have our own */
+		if (ACPI_SUCCESS(AcpiGetParent(handle, &parent)))
+			return (get_dma_tag(parent, result));
+
+		/* The root (which has no parent) has no restrictions */
+		*result = NULL;
+		return (0);
+	}
+
+	if (ACPI_FAILURE(acpi_GetInteger(handle, "_CCA", &coherent)))
+		coherent = 0;
+
+	/*
+	 * First off, a note about lowaddr values. What sysctl showed
+	 * me was (during investigation of u-boot/dtb/fdt oddities
+	 * that had the same style of solution that I am trying here):
+	 *
+	 * . . .
+	 * hw.busdma.zone2.lowaddr: 0x3c000fff
+	 * . . .
+	 * hw.busdma.zone1.lowaddr: 0x3fffffff
+	 * . . .
+	 * hw.busdma.zone0.lowaddr: 0xffffffff
+	 * . . .
+	 *
+	 * So I've guessed that lowaddr should identify the
+	 * end page of the possibly-use-it region, not the
+	 * first do-not-use-it page. If I've guessed wrong,
+	 * at most it would bounce one page that it could
+	 * avoid bouncing.  But, if I guessed correct, it
+	 * might bounce a page that it should instead of
+	 * not doing so. Thus the "-1" below.
+	 */
+	if (bus_dma_tag_create(NULL, 1, 0,
+		limits.lowaddr-1, BUS_SPACE_MAXADDR, NULL, NULL,
+		BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED, BUS_SPACE_MAXSIZE,
+		coherent ? BUS_DMA_COHERENT : 0, NULL, NULL,
+		result) != 0)
+		return (ENOMEM);
+
+	return (0);
+}
+
+static bus_dma_tag_t
+acpi_get_dma_tag(device_t dev, device_t child)
+{
+	bus_dma_tag_t result;
+
+	if (get_dma_tag(acpi_get_handle(child), &result) != 0) {
+		device_printf(child, "could not get ACPI DMA limits\n");
+		return (NULL);
+	}
+
+	return (result);
+}
+
 /*
  * Fetch some descriptive data from ACPI to put in our attach message.
  */


I later found the code that means that the "-1" is required, confirming
what I had guessed: sys/arm64/arm64/busdma_machdep.c 's
common_bus_dma_tag_create has:

common->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1);
common->highaddr = trunc_page((vm_paddr_t)highaddr) + (PAGE_SIZE - 1);

and so forces reference to the last byte of the page identified by lowaddr
(and highaddr). Thus lowaddr needs to identify the last page that can avoid
bouncing (or earlier).

===
Mark Millard
marklmi at yahoo.com
( dsl-only.net went
away in early 2018-Mar)



More information about the freebsd-arm mailing list