Skip to contentSkip to conversation

efi_loader: Reserve unaccessible memory

PATCH Superseded Alexander Graf 3 participants 4 messages Nov 30, 2018

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 efi_loader: Reserve unaccessible memory

On some systems, not all RAM may be usable within U-Boot. Maybe the memory maps are incomplete, maybe it's used as workaround for broken DMA. But whatever the reason may be, a platform can say that it does not wish to have its RAM accessed above a certain address by defining board_get_usable_ram_top().

In the efi_loader world, we ignored that hint, mostly because very few boards actually have real restrictions around this.

So let's honor the board's wish to not access high addresses during boot time. The best way to do so is by indicating the respective pages as "allocated by firmware". That way, Operating Systems will still use the pages after boot, but before boot no allocation will use them.

---

v1 -> v2:

- Reserve banks that start above ram_top
- Improve inline comments
- Fix 32bit target with ram_top = 0 (>=4G)

---
 include/common.h            | 11 +++++++++++
 lib/efi_loader/efi_memory.c | 32 +++++++++++++++++++++++++++++---
 2 files changed, 40 insertions(+), 3 deletions(-)
include/common.h +11 -0
index 3f69943887..8f295c2f30 100644
@@ -106,6 +106,17 @@ int mdm_init(void);
 void board_show_dram(phys_size_t size);
 
 /**
+ * Get the uppermost pointer that is valid to access
+ *
+ * Some systems may not map all of their address space. This function allows
+ * boards to indicate what their highest support pointer value is for DRAM
+ * access.
+ *
+ * @param total_size	Size of U-Boot (unused?)
+ */
+ulong board_get_usable_ram_top(ulong total_size);
+
+/**
  * arch_fixup_fdt() - Write arch-specific information to fdt
  *
  * Defined in arch/$(ARCH)/lib/bootm-fdt.c
lib/efi_loader/efi_memory.c +29 -3
index f225a9028c..3392af59a1 100644
@@ -551,8 +551,13 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
 
 __weak void efi_add_known_memory(void)
 {
+	u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK;
 	int i;
 
+	/* Fix for 32bit targets with ram_top at 4G */
+	if (!ram_top)
+		ram_top = U32_MAX;
+
 	/* Add RAM */
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
 		u64 ram_end, ram_start, pages;
@@ -564,11 +569,32 @@ __weak void efi_add_known_memory(void)
 		ram_end &= ~EFI_PAGE_MASK;
 		ram_start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
 
-		if (ram_end > ram_start) {
-			pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT;
+		if (ram_end <= ram_start) {
+			/* Invalid mapping, keep going. */
+			continue;
+		}
+
+		pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT;
 
+		efi_add_memory_map(ram_start, pages,
+				   EFI_CONVENTIONAL_MEMORY, false);
+
+		/*
+		 * Boards may indicate to the U-Boot memory core that they
+		 * can not support memory above ram_top. Let's honor this
+		 * in the efi_loader subsystem too by declaring any memory
+		 * above ram_top as "already occupied by firmware".
+		 */
+		if (ram_top < ram_start) {
+			/* ram_top is before this region, reserve all */
 			efi_add_memory_map(ram_start, pages,
-					   EFI_CONVENTIONAL_MEMORY, false);
+					   EFI_BOOT_SERVICES_DATA, true);
+		} else if ((ram_top >= ram_start) && (ram_top < ram_end)) {
+			/* ram_top is inside this region, reserve parts */
+			pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT;
+
+			efi_add_memory_map(ram_top, pages,
+					   EFI_BOOT_SERVICES_DATA, true);
 		}
 	}
 }
Signature · 1 line
2.12.3
#1 lore raw

PATCH v2 1/1 efi_loader: Reserve unaccessible memory

Quoted 15 lines · Alexander Graf

On some systems, not all RAM may be usable within U-Boot. Maybe the memory maps are incomplete, maybe it's used as workaround for broken DMA. But whatever the reason may be, a platform can say that it does not wish to have its RAM accessed above a certain address by defining board_get_usable_ram_top().

In the efi_loader world, we ignored that hint, mostly because very few boards actually have real restrictions around this.

So let's honor the board's wish to not access high addresses during boot time. The best way to do so is by indicating the respective pages as "allocated by firmware". That way, Operating Systems will still use the pages after boot, but before boot no allocation will use them.

lib/efi_loader/efi_memory.c efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, · line 559

Quoted 9 lines
lib/efi_loader/efi_memory.c +4 -0
 
  __weak void efi_add_known_memory(void)
  {
+	u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK;
  	int i;
 
+	/* Fix for 32bit targets with ram_top at 4G */
+	if (!ram_top)
+		ram_top = U32_MAX;

Don't you need to &= ~EFI_PAGE_MASK after the U32_MAX assignment too? Perhaps just do that once:

	u64 ram_top = board_get_usable_ram_top(0);
	/* Fix for 32bit targets with ram_top at 4G */
	if (!ram_top)
		ram_top = U32_MAX;
	ram_top &=  & ~EFI_PAGE_MASK;

Otherwise,

#2 lore raw

PATCH v2 1/1 efi_loader: Reserve unaccessible memory

In reply to Alexander Graf
Quoted 56 lines · Alexander Graf

On some systems, not all RAM may be usable within U-Boot. Maybe the memory maps are incomplete, maybe it's used as workaround for broken DMA. But whatever the reason may be, a platform can say that it does not wish to have its RAM accessed above a certain address by defining board_get_usable_ram_top().

In the efi_loader world, we ignored that hint, mostly because very few boards actually have real restrictions around this.

So let's honor the board's wish to not access high addresses during boot time. The best way to do so is by indicating the respective pages as "allocated by firmware". That way, Operating Systems will still use the pages after boot, but before boot no allocation will use them.

---

v1 -> v2:

- Reserve banks that start above ram_top
- Improve inline comments
- Fix 32bit target with ram_top = 0 (>=4G)

---
 include/common.h            | 11 +++++++++++
 lib/efi_loader/efi_memory.c | 32 +++++++++++++++++++++++++++++---
 2 files changed, 40 insertions(+), 3 deletions(-)
include/common.h +11 -0
index 3f69943887..8f295c2f30 100644
@@ -106,6 +106,17 @@ int mdm_init(void);
 void board_show_dram(phys_size_t size);
 
 /**
+ * Get the uppermost pointer that is valid to access
+ *
+ * Some systems may not map all of their address space. This function allows
+ * boards to indicate what their highest support pointer value is for DRAM
+ * access.
+ *
+ * @param total_size	Size of U-Boot (unused?)
+ */
+ulong board_get_usable_ram_top(ulong total_size);
+
+/**
  * arch_fixup_fdt() - Write arch-specific information to fdt
  *
  * Defined in arch/$(ARCH)/lib/bootm-fdt.c
lib/efi_loader/efi_memory.c +0 -0
index f225a9028c..3392af59a1 100644

lib/efi_loader/efi_memory.c efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, · line 559

Quoted 9 lines
lib/efi_loader/efi_memory.c +4 -0
@@ -551,8 +551,13 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
 
 __weak void efi_add_known_memory(void)
 {
+	u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK;
 	int i;
 
+	/* Fix for 32bit targets with ram_top at 4G */
+	if (!ram_top)
+		ram_top = U32_MAX;

Why don't you set ram_top to (1 << 32)? ram_top is u64 anyway. We do not want something ending with 0xfff to be passed to efi_add_memory_map.

Are we sure there will never be 64bit machines with a memory bank ending at 1 << 64? What would be the value of ram_end and ram_top in such a case?

Regards

Heinrich

Quoted 41 lines
lib/efi_loader/efi_memory.c +25 -3
+
 	/* Add RAM */
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
 		u64 ram_end, ram_start, pages;
@@ -564,11 +569,32 @@ __weak void efi_add_known_memory(void)
 		ram_end &= ~EFI_PAGE_MASK;
 		ram_start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
 
-		if (ram_end > ram_start) {
-			pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT;
+		if (ram_end <= ram_start) {
+			/* Invalid mapping, keep going. */
+			continue;
+		}
+
+		pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT;
 
+		efi_add_memory_map(ram_start, pages,
+				   EFI_CONVENTIONAL_MEMORY, false);
+
+		/*
+		 * Boards may indicate to the U-Boot memory core that they
+		 * can not support memory above ram_top. Let's honor this
+		 * in the efi_loader subsystem too by declaring any memory
+		 * above ram_top as "already occupied by firmware".
+		 */
+		if (ram_top < ram_start) {
+			/* ram_top is before this region, reserve all */
 			efi_add_memory_map(ram_start, pages,
-					   EFI_CONVENTIONAL_MEMORY, false);
+					   EFI_BOOT_SERVICES_DATA, true);
+		} else if ((ram_top >= ram_start) && (ram_top < ram_end)) {
+			/* ram_top is inside this region, reserve parts */
+			pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT;
+
+			efi_add_memory_map(ram_top, pages,
+					   EFI_BOOT_SERVICES_DATA, true);
 		}
 	}
 }
#3 lore raw

PATCH v2 1/1 efi_loader: Reserve unaccessible memory

Quoted 63 lines · Heinrich Schuchardt
Quoted 62 lines · Alexander Graf

On some systems, not all RAM may be usable within U-Boot. Maybe the memory maps are incomplete, maybe it's used as workaround for broken DMA. But whatever the reason may be, a platform can say that it does not wish to have its RAM accessed above a certain address by defining board_get_usable_ram_top().

In the efi_loader world, we ignored that hint, mostly because very few boards actually have real restrictions around this.

So let's honor the board's wish to not access high addresses during boot time. The best way to do so is by indicating the respective pages as "allocated by firmware". That way, Operating Systems will still use the pages after boot, but before boot no allocation will use them.

---

v1 -> v2:

- Reserve banks that start above ram_top
- Improve inline comments
- Fix 32bit target with ram_top = 0 (>=4G)

---
 include/common.h            | 11 +++++++++++
 lib/efi_loader/efi_memory.c | 32 +++++++++++++++++++++++++++++---
 2 files changed, 40 insertions(+), 3 deletions(-)
include/common.h +11 -0
index 3f69943887..8f295c2f30 100644
@@ -106,6 +106,17 @@ int mdm_init(void);
 void board_show_dram(phys_size_t size);
 
 /**
+ * Get the uppermost pointer that is valid to access
+ *
+ * Some systems may not map all of their address space. This function allows
+ * boards to indicate what their highest support pointer value is for DRAM
+ * access.
+ *
+ * @param total_size	Size of U-Boot (unused?)
+ */
+ulong board_get_usable_ram_top(ulong total_size);
+
+/**
  * arch_fixup_fdt() - Write arch-specific information to fdt
  *
  * Defined in arch/$(ARCH)/lib/bootm-fdt.c
lib/efi_loader/efi_memory.c +1 -0
index f225a9028c..3392af59a1 100644
@@ -551,8 +551,13 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
 
 __weak void efi_add_known_memory(void)
 {
+	u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK;
 	int i;
 
Quoted 6 lines

lib/efi_loader/efi_memory.c efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, · line 559

Quoted 3 lines
lib/efi_loader/efi_memory.c +3 -0
+	/* Fix for 32bit targets with ram_top at 4G */
+	if (!ram_top)
+		ram_top = U32_MAX;

Why don't you set ram_top to (1 << 32)? ram_top is u64 anyway. We do not want something ending with 0xfff to be passed to efi_add_memory_map.

Ugh, yes. Of course. And obviously that's 0x100000000ULL to make sure it works fine on 32bit :).

Quoted 2 lines

Are we sure there will never be 64bit machines with a memory bank ending at 1 << 64? What would be the value of ram_end and ram_top in such a case?

I think we can be pretty sure of that, yes :). No machine today even comes close to that many physical address bits supported.

Alex