linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] x86: EFI stub support for large memory maps
@ 2013-08-23 21:02 Linn Crosetto
  2013-09-06 10:03 ` Matt Fleming
  2013-09-06 17:04 ` [PATCHv2] " Linn Crosetto
  0 siblings, 2 replies; 11+ messages in thread
From: Linn Crosetto @ 2013-08-23 21:02 UTC (permalink / raw)
  To: matt.fleming, hpa, tglx, mingo, x86, yinghai
  Cc: linux-efi, linux-kernel, Linn Crosetto

This patch fixes a problem with EFI memory maps larger than 128 entries
when booting using the EFI stub, which results in overflowing e820_map
in boot_params and an eventual halt when checking the map size in
sanitize_e820_map().

If the number of map entries is greater than what can fit in e820_map,
add the extra entries to the setup_data list using type SETUP_E820_EXT.
These extra entries are then picked up when the setup_data list is
parsed in parse_e820_ext().

Signed-off-by: Linn Crosetto <linn@hp.com>
---
 arch/x86/boot/compressed/eboot.c | 220 ++++++++++++++++++++++++++++-----------
 1 file changed, 157 insertions(+), 63 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index b7388a4..89fcfe6 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -982,20 +982,158 @@ fail:
 	return NULL;
 }
 
+static void add_e820ext(struct boot_params *params,
+			struct setup_data *e820ext, u32 nr_entries)
+{
+	struct setup_data *data;
+	efi_status_t status;
+	unsigned long size;
+
+	e820ext->type = SETUP_E820_EXT;
+	e820ext->len = nr_entries * sizeof(struct e820entry);
+	e820ext->next = 0;
+
+	data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
+
+	while (data && data->next)
+		data = (struct setup_data *)(unsigned long)data->next;
+
+	if (data)
+		data->next = (unsigned long)e820ext;
+	else
+		params->hdr.setup_data = (unsigned long)e820ext;
+}
+
+static efi_status_t setup_e820(struct boot_params *params,
+			       struct setup_data *e820ext, u32 e820ext_size)
+{
+	struct e820entry *e820_map = &params->e820_map[0];
+	struct efi_info *efi = &params->efi_info;
+	struct e820entry *prev = NULL;
+	u32 nr_entries;
+	u32 nr_desc;
+	int i;
+
+	nr_entries = 0;
+	nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size;
+
+	for (i = 0; i < nr_desc; i++) {
+		efi_memory_desc_t *d;
+		unsigned int e820_type = 0;
+		unsigned long m = efi->efi_memmap;
+
+		d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size));
+		switch (d->type) {
+		case EFI_RESERVED_TYPE:
+		case EFI_RUNTIME_SERVICES_CODE:
+		case EFI_RUNTIME_SERVICES_DATA:
+		case EFI_MEMORY_MAPPED_IO:
+		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+		case EFI_PAL_CODE:
+			e820_type = E820_RESERVED;
+			break;
+
+		case EFI_UNUSABLE_MEMORY:
+			e820_type = E820_UNUSABLE;
+			break;
+
+		case EFI_ACPI_RECLAIM_MEMORY:
+			e820_type = E820_ACPI;
+			break;
+
+		case EFI_LOADER_CODE:
+		case EFI_LOADER_DATA:
+		case EFI_BOOT_SERVICES_CODE:
+		case EFI_BOOT_SERVICES_DATA:
+		case EFI_CONVENTIONAL_MEMORY:
+			e820_type = E820_RAM;
+			break;
+
+		case EFI_ACPI_MEMORY_NVS:
+			e820_type = E820_NVS;
+			break;
+
+		default:
+			continue;
+		}
+
+		/* Merge adjacent mappings */
+		if (prev && prev->type == e820_type &&
+		    (prev->addr + prev->size) == d->phys_addr) {
+			prev->size += d->num_pages << 12;
+			continue;
+		}
+
+		if (nr_entries == ARRAY_SIZE(params->e820_map)) {
+			u32 need = (nr_desc - i) * sizeof(struct e820entry) +
+				   sizeof(struct setup_data);
+
+			if (!e820ext || e820ext_size < need)
+				return EFI_BUFFER_TOO_SMALL;
+
+			/* boot_params map full, switch to e820 extended */
+			e820_map = (struct e820entry *)e820ext->data;
+		}
+
+		e820_map->addr = d->phys_addr;
+		e820_map->size = d->num_pages << PAGE_SHIFT;
+		e820_map->type = e820_type;
+		prev = e820_map++;
+		nr_entries++;
+	}
+
+	if (nr_entries > ARRAY_SIZE(params->e820_map)) {
+		u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map);
+
+		add_e820ext(params, e820ext, nr_e820ext);
+		nr_entries -= nr_e820ext;
+	}
+
+	params->e820_entries = (u8)nr_entries;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
+				  u32 *e820ext_size)
+{
+	efi_status_t status;
+	unsigned long size;
+
+	size = sizeof(struct setup_data) +
+		sizeof(struct e820entry) * nr_desc;
+
+	if (*e820ext && size <= *e820ext_size)
+		return EFI_SUCCESS; /* Already allocated */
+
+	if (*e820ext)
+		efi_call_phys1(sys_table->boottime->free_pool, *e820ext);
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, size, e820ext);
+
+	if (status == EFI_SUCCESS)
+		*e820ext_size = size;
+
+	return status;
+}
+
 static efi_status_t exit_boot(struct boot_params *boot_params,
 			      void *handle)
 {
 	struct efi_info *efi = &boot_params->efi_info;
-	struct e820entry *e820_map = &boot_params->e820_map[0];
-	struct e820entry *prev = NULL;
 	unsigned long size, key, desc_size, _size;
 	efi_memory_desc_t *mem_map;
+	struct setup_data *e820ext;
+	__u32 e820ext_size;
+	__u32 nr_desc, prev_nr_desc;
 	efi_status_t status;
 	__u32 desc_version;
 	bool called_exit = false;
-	u8 nr_entries;
-	int i;
 
+	nr_desc = 0;
+	e820ext = NULL;
+	e820ext_size = 0;
 	size = sizeof(*mem_map) * 32;
 
 again:
@@ -1016,6 +1154,19 @@ get_map:
 	if (status != EFI_SUCCESS)
 		goto free_mem_map;
 
+	prev_nr_desc = nr_desc;
+	nr_desc = size / desc_size;
+	if (nr_desc > prev_nr_desc &&
+	    nr_desc > ARRAY_SIZE(boot_params->e820_map)) {
+		u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map);
+
+		status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size);
+		if (status != EFI_SUCCESS)
+			return status;
+
+		goto get_map; /* Allocated memory, get map again */
+	}
+
 	memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
 	efi->efi_systab = (unsigned long)sys_table;
 	efi->efi_memdesc_size = desc_size;
@@ -1049,66 +1200,9 @@ get_map:
 	/* Historic? */
 	boot_params->alt_mem_k = 32 * 1024;
 
-	/*
-	 * Convert the EFI memory map to E820.
-	 */
-	nr_entries = 0;
-	for (i = 0; i < size / desc_size; i++) {
-		efi_memory_desc_t *d;
-		unsigned int e820_type = 0;
-		unsigned long m = (unsigned long)mem_map;
-
-		d = (efi_memory_desc_t *)(m + (i * desc_size));
-		switch (d->type) {
-		case EFI_RESERVED_TYPE:
-		case EFI_RUNTIME_SERVICES_CODE:
-		case EFI_RUNTIME_SERVICES_DATA:
-		case EFI_MEMORY_MAPPED_IO:
-		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
-		case EFI_PAL_CODE:
-			e820_type = E820_RESERVED;
-			break;
-
-		case EFI_UNUSABLE_MEMORY:
-			e820_type = E820_UNUSABLE;
-			break;
-
-		case EFI_ACPI_RECLAIM_MEMORY:
-			e820_type = E820_ACPI;
-			break;
-
-		case EFI_LOADER_CODE:
-		case EFI_LOADER_DATA:
-		case EFI_BOOT_SERVICES_CODE:
-		case EFI_BOOT_SERVICES_DATA:
-		case EFI_CONVENTIONAL_MEMORY:
-			e820_type = E820_RAM;
-			break;
-
-		case EFI_ACPI_MEMORY_NVS:
-			e820_type = E820_NVS;
-			break;
-
-		default:
-			continue;
-		}
-
-		/* Merge adjacent mappings */
-		if (prev && prev->type == e820_type &&
-		    (prev->addr + prev->size) == d->phys_addr)
-			prev->size += d->num_pages << 12;
-		else {
-			e820_map->addr = d->phys_addr;
-			e820_map->size = d->num_pages << 12;
-			e820_map->type = e820_type;
-			prev = e820_map++;
-			nr_entries++;
-		}
-	}
-
-	boot_params->e820_entries = nr_entries;
+	status = setup_e820(boot_params, e820ext, e820ext_size);
 
-	return EFI_SUCCESS;
+	return status;
 
 free_mem_map:
 	low_free(_size, (unsigned long)mem_map);
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH] x86: EFI stub support for large memory maps
  2013-08-23 21:02 [PATCH] x86: EFI stub support for large memory maps Linn Crosetto
@ 2013-09-06 10:03 ` Matt Fleming
  2013-09-06 17:04 ` [PATCHv2] " Linn Crosetto
  1 sibling, 0 replies; 11+ messages in thread
From: Matt Fleming @ 2013-09-06 10:03 UTC (permalink / raw)
  To: Linn Crosetto
  Cc: matt.fleming, hpa, tglx, mingo, x86, yinghai, linux-efi, linux-kernel

On Fri, 23 Aug, at 03:02:51PM, Linn Crosetto wrote:
> This patch fixes a problem with EFI memory maps larger than 128 entries
> when booting using the EFI stub, which results in overflowing e820_map
> in boot_params and an eventual halt when checking the map size in
> sanitize_e820_map().
> 
> If the number of map entries is greater than what can fit in e820_map,
> add the extra entries to the setup_data list using type SETUP_E820_EXT.
> These extra entries are then picked up when the setup_data list is
> parsed in parse_e820_ext().
> 
> Signed-off-by: Linn Crosetto <linn@hp.com>
> ---
>  arch/x86/boot/compressed/eboot.c | 220 ++++++++++++++++++++++++++++-----------
>  1 file changed, 157 insertions(+), 63 deletions(-)

[...]

> @@ -1016,6 +1154,19 @@ get_map:
>  	if (status != EFI_SUCCESS)
>  		goto free_mem_map;
>  
> +	prev_nr_desc = nr_desc;
> +	nr_desc = size / desc_size;
> +	if (nr_desc > prev_nr_desc &&
> +	    nr_desc > ARRAY_SIZE(boot_params->e820_map)) {
> +		u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map);
> +
> +		status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size);
> +		if (status != EFI_SUCCESS)
> +			return status;

In the error path shouldn't we jump to free_mem_map to avoid leaking
memory?

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCHv2] x86: EFI stub support for large memory maps
  2013-08-23 21:02 [PATCH] x86: EFI stub support for large memory maps Linn Crosetto
  2013-09-06 10:03 ` Matt Fleming
@ 2013-09-06 17:04 ` Linn Crosetto
  2013-09-17 20:14   ` Matt Fleming
  2013-09-23  1:59   ` [PATCHv3] " Linn Crosetto
  1 sibling, 2 replies; 11+ messages in thread
From: Linn Crosetto @ 2013-09-06 17:04 UTC (permalink / raw)
  To: matt.fleming, hpa, tglx, mingo, x86, yinghai
  Cc: linux-efi, linux-kernel, Linn Crosetto

This patch fixes a problem with EFI memory maps larger than 128 entries
when booting using the EFI stub, which results in overflowing e820_map
in boot_params and an eventual halt when checking the map size in
sanitize_e820_map().

If the number of map entries is greater than what can fit in e820_map,
add the extra entries to the setup_data list using type SETUP_E820_EXT.
These extra entries are then picked up when the setup_data list is
parsed in parse_e820_ext().

Signed-off-by: Linn Crosetto <linn@hp.com>
---
Changes in v2:
* Free memory when error is returned from alloc_e820ext() as suggested by Matt
  Fleming
* Set pointer to NULL and size to 0 after freeing memory in alloc_e820ext()

 arch/x86/boot/compressed/eboot.c | 223 ++++++++++++++++++++++++++++-----------
 1 file changed, 160 insertions(+), 63 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index b7388a4..2ac395e 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -982,20 +982,161 @@ fail:
 	return NULL;
 }
 
+static void add_e820ext(struct boot_params *params,
+			struct setup_data *e820ext, u32 nr_entries)
+{
+	struct setup_data *data;
+	efi_status_t status;
+	unsigned long size;
+
+	e820ext->type = SETUP_E820_EXT;
+	e820ext->len = nr_entries * sizeof(struct e820entry);
+	e820ext->next = 0;
+
+	data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
+
+	while (data && data->next)
+		data = (struct setup_data *)(unsigned long)data->next;
+
+	if (data)
+		data->next = (unsigned long)e820ext;
+	else
+		params->hdr.setup_data = (unsigned long)e820ext;
+}
+
+static efi_status_t setup_e820(struct boot_params *params,
+			       struct setup_data *e820ext, u32 e820ext_size)
+{
+	struct e820entry *e820_map = &params->e820_map[0];
+	struct efi_info *efi = &params->efi_info;
+	struct e820entry *prev = NULL;
+	u32 nr_entries;
+	u32 nr_desc;
+	int i;
+
+	nr_entries = 0;
+	nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size;
+
+	for (i = 0; i < nr_desc; i++) {
+		efi_memory_desc_t *d;
+		unsigned int e820_type = 0;
+		unsigned long m = efi->efi_memmap;
+
+		d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size));
+		switch (d->type) {
+		case EFI_RESERVED_TYPE:
+		case EFI_RUNTIME_SERVICES_CODE:
+		case EFI_RUNTIME_SERVICES_DATA:
+		case EFI_MEMORY_MAPPED_IO:
+		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+		case EFI_PAL_CODE:
+			e820_type = E820_RESERVED;
+			break;
+
+		case EFI_UNUSABLE_MEMORY:
+			e820_type = E820_UNUSABLE;
+			break;
+
+		case EFI_ACPI_RECLAIM_MEMORY:
+			e820_type = E820_ACPI;
+			break;
+
+		case EFI_LOADER_CODE:
+		case EFI_LOADER_DATA:
+		case EFI_BOOT_SERVICES_CODE:
+		case EFI_BOOT_SERVICES_DATA:
+		case EFI_CONVENTIONAL_MEMORY:
+			e820_type = E820_RAM;
+			break;
+
+		case EFI_ACPI_MEMORY_NVS:
+			e820_type = E820_NVS;
+			break;
+
+		default:
+			continue;
+		}
+
+		/* Merge adjacent mappings */
+		if (prev && prev->type == e820_type &&
+		    (prev->addr + prev->size) == d->phys_addr) {
+			prev->size += d->num_pages << 12;
+			continue;
+		}
+
+		if (nr_entries == ARRAY_SIZE(params->e820_map)) {
+			u32 need = (nr_desc - i) * sizeof(struct e820entry) +
+				   sizeof(struct setup_data);
+
+			if (!e820ext || e820ext_size < need)
+				return EFI_BUFFER_TOO_SMALL;
+
+			/* boot_params map full, switch to e820 extended */
+			e820_map = (struct e820entry *)e820ext->data;
+		}
+
+		e820_map->addr = d->phys_addr;
+		e820_map->size = d->num_pages << PAGE_SHIFT;
+		e820_map->type = e820_type;
+		prev = e820_map++;
+		nr_entries++;
+	}
+
+	if (nr_entries > ARRAY_SIZE(params->e820_map)) {
+		u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map);
+
+		add_e820ext(params, e820ext, nr_e820ext);
+		nr_entries -= nr_e820ext;
+	}
+
+	params->e820_entries = (u8)nr_entries;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
+				  u32 *e820ext_size)
+{
+	efi_status_t status;
+	unsigned long size;
+
+	size = sizeof(struct setup_data) +
+		sizeof(struct e820entry) * nr_desc;
+
+	if (*e820ext && size <= *e820ext_size)
+		return EFI_SUCCESS; /* Already allocated */
+
+	if (*e820ext) {
+		efi_call_phys1(sys_table->boottime->free_pool, *e820ext);
+		*e820ext = NULL;
+		*e820ext_size = 0;
+	}
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, size, e820ext);
+
+	if (status == EFI_SUCCESS)
+		*e820ext_size = size;
+
+	return status;
+}
+
 static efi_status_t exit_boot(struct boot_params *boot_params,
 			      void *handle)
 {
 	struct efi_info *efi = &boot_params->efi_info;
-	struct e820entry *e820_map = &boot_params->e820_map[0];
-	struct e820entry *prev = NULL;
 	unsigned long size, key, desc_size, _size;
 	efi_memory_desc_t *mem_map;
+	struct setup_data *e820ext;
+	__u32 e820ext_size;
+	__u32 nr_desc, prev_nr_desc;
 	efi_status_t status;
 	__u32 desc_version;
 	bool called_exit = false;
-	u8 nr_entries;
-	int i;
 
+	nr_desc = 0;
+	e820ext = NULL;
+	e820ext_size = 0;
 	size = sizeof(*mem_map) * 32;
 
 again:
@@ -1016,6 +1157,19 @@ get_map:
 	if (status != EFI_SUCCESS)
 		goto free_mem_map;
 
+	prev_nr_desc = nr_desc;
+	nr_desc = size / desc_size;
+	if (nr_desc > prev_nr_desc &&
+	    nr_desc > ARRAY_SIZE(boot_params->e820_map)) {
+		u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map);
+
+		status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size);
+		if (status != EFI_SUCCESS)
+			goto free_mem_map;
+
+		goto get_map; /* Allocated memory, get map again */
+	}
+
 	memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
 	efi->efi_systab = (unsigned long)sys_table;
 	efi->efi_memdesc_size = desc_size;
@@ -1049,66 +1203,9 @@ get_map:
 	/* Historic? */
 	boot_params->alt_mem_k = 32 * 1024;
 
-	/*
-	 * Convert the EFI memory map to E820.
-	 */
-	nr_entries = 0;
-	for (i = 0; i < size / desc_size; i++) {
-		efi_memory_desc_t *d;
-		unsigned int e820_type = 0;
-		unsigned long m = (unsigned long)mem_map;
+	status = setup_e820(boot_params, e820ext, e820ext_size);
 
-		d = (efi_memory_desc_t *)(m + (i * desc_size));
-		switch (d->type) {
-		case EFI_RESERVED_TYPE:
-		case EFI_RUNTIME_SERVICES_CODE:
-		case EFI_RUNTIME_SERVICES_DATA:
-		case EFI_MEMORY_MAPPED_IO:
-		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
-		case EFI_PAL_CODE:
-			e820_type = E820_RESERVED;
-			break;
-
-		case EFI_UNUSABLE_MEMORY:
-			e820_type = E820_UNUSABLE;
-			break;
-
-		case EFI_ACPI_RECLAIM_MEMORY:
-			e820_type = E820_ACPI;
-			break;
-
-		case EFI_LOADER_CODE:
-		case EFI_LOADER_DATA:
-		case EFI_BOOT_SERVICES_CODE:
-		case EFI_BOOT_SERVICES_DATA:
-		case EFI_CONVENTIONAL_MEMORY:
-			e820_type = E820_RAM;
-			break;
-
-		case EFI_ACPI_MEMORY_NVS:
-			e820_type = E820_NVS;
-			break;
-
-		default:
-			continue;
-		}
-
-		/* Merge adjacent mappings */
-		if (prev && prev->type == e820_type &&
-		    (prev->addr + prev->size) == d->phys_addr)
-			prev->size += d->num_pages << 12;
-		else {
-			e820_map->addr = d->phys_addr;
-			e820_map->size = d->num_pages << 12;
-			e820_map->type = e820_type;
-			prev = e820_map++;
-			nr_entries++;
-		}
-	}
-
-	boot_params->e820_entries = nr_entries;
-
-	return EFI_SUCCESS;
+	return status;
 
 free_mem_map:
 	low_free(_size, (unsigned long)mem_map);
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCHv2] x86: EFI stub support for large memory maps
  2013-09-06 17:04 ` [PATCHv2] " Linn Crosetto
@ 2013-09-17 20:14   ` Matt Fleming
  2013-09-23  0:23     ` Linn Crosetto
  2013-09-23  1:59   ` [PATCHv3] " Linn Crosetto
  1 sibling, 1 reply; 11+ messages in thread
From: Matt Fleming @ 2013-09-17 20:14 UTC (permalink / raw)
  To: Linn Crosetto
  Cc: matt.fleming, hpa, tglx, mingo, x86, yinghai, linux-efi, linux-kernel

On Fri, 06 Sep, at 11:04:16AM, Linn Crosetto wrote:
> This patch fixes a problem with EFI memory maps larger than 128 entries
> when booting using the EFI stub, which results in overflowing e820_map
> in boot_params and an eventual halt when checking the map size in
> sanitize_e820_map().
> 
> If the number of map entries is greater than what can fit in e820_map,
> add the extra entries to the setup_data list using type SETUP_E820_EXT.
> These extra entries are then picked up when the setup_data list is
> parsed in parse_e820_ext().
> 
> Signed-off-by: Linn Crosetto <linn@hp.com>
> ---
> Changes in v2:
> * Free memory when error is returned from alloc_e820ext() as suggested by Matt
>   Fleming
> * Set pointer to NULL and size to 0 after freeing memory in alloc_e820ext()
> 
>  arch/x86/boot/compressed/eboot.c | 223 ++++++++++++++++++++++++++++-----------
>  1 file changed, 160 insertions(+), 63 deletions(-)

[...]

> +static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
> +				  u32 *e820ext_size)
> +{
> +	efi_status_t status;
> +	unsigned long size;
> +
> +	size = sizeof(struct setup_data) +
> +		sizeof(struct e820entry) * nr_desc;
> +
> +	if (*e820ext && size <= *e820ext_size)
> +		return EFI_SUCCESS; /* Already allocated */

Do we actually need this check? I thought the 'prev_nr_desc' below
ensures we only allocate 'e820ext' if we need more memory.

[...]

> @@ -1016,6 +1157,19 @@ get_map:
>  	if (status != EFI_SUCCESS)
>  		goto free_mem_map;
>  
> +	prev_nr_desc = nr_desc;
> +	nr_desc = size / desc_size;
> +	if (nr_desc > prev_nr_desc &&
> +	    nr_desc > ARRAY_SIZE(boot_params->e820_map)) {
> +		u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map);
> +
> +		status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size);
> +		if (status != EFI_SUCCESS)
> +			goto free_mem_map;
> +
> +		goto get_map; /* Allocated memory, get map again */
> +	}
> +

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCHv2] x86: EFI stub support for large memory maps
  2013-09-17 20:14   ` Matt Fleming
@ 2013-09-23  0:23     ` Linn Crosetto
  0 siblings, 0 replies; 11+ messages in thread
From: Linn Crosetto @ 2013-09-23  0:23 UTC (permalink / raw)
  To: Matt Fleming
  Cc: matt.fleming, hpa, tglx, mingo, x86, yinghai, linux-efi, linux-kernel

On Tue, Sep 17, 2013 at 09:14:52PM +0100, Matt Fleming wrote:
> > +static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
> > +				  u32 *e820ext_size)
> > +{
> > +	efi_status_t status;
> > +	unsigned long size;
> > +
> > +	size = sizeof(struct setup_data) +
> > +		sizeof(struct e820entry) * nr_desc;
> > +
> > +	if (*e820ext && size <= *e820ext_size)
> > +		return EFI_SUCCESS; /* Already allocated */
> 
> Do we actually need this check? I thought the 'prev_nr_desc' below
> ensures we only allocate 'e820ext' if we need more memory.

I am okay with removing the check. There is another bug in exit_boot, when
jumping to get_map the call to get the memory map is passed the previous map
size instead of the size of the allocated buffer. I'll make the changes and
resend.

> 
> [...]
> 
> > @@ -1016,6 +1157,19 @@ get_map:
> >  	if (status != EFI_SUCCESS)
> >  		goto free_mem_map;
> >  
> > +	prev_nr_desc = nr_desc;
> > +	nr_desc = size / desc_size;
> > +	if (nr_desc > prev_nr_desc &&
> > +	    nr_desc > ARRAY_SIZE(boot_params->e820_map)) {
> > +		u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map);
> > +
> > +		status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size);
> > +		if (status != EFI_SUCCESS)
> > +			goto free_mem_map;
> > +
> > +		goto get_map; /* Allocated memory, get map again */
> > +	}
> > +

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCHv3] x86: EFI stub support for large memory maps
  2013-09-06 17:04 ` [PATCHv2] " Linn Crosetto
  2013-09-17 20:14   ` Matt Fleming
@ 2013-09-23  1:59   ` Linn Crosetto
  2013-09-25 12:58     ` Matt Fleming
  1 sibling, 1 reply; 11+ messages in thread
From: Linn Crosetto @ 2013-09-23  1:59 UTC (permalink / raw)
  To: matt.fleming, hpa, tglx, mingo, x86, yinghai
  Cc: linux-efi, linux-kernel, Linn Crosetto

This patch fixes a problem with EFI memory maps larger than 128 entries
when booting using the EFI stub, which results in overflowing e820_map
in boot_params and an eventual halt when checking the map size in
sanitize_e820_map().

If the number of map entries is greater than what can fit in e820_map,
add the extra entries to the setup_data list using type SETUP_E820_EXT.
These extra entries are then picked up when the setup_data list is
parsed in parse_e820_ext().

Signed-off-by: Linn Crosetto <linn@hp.com>
---
Changes from v2:
 * Removed unnecessary optimization in alloc_e820ext() (Matt Fleming)
 * Fixed a bug where an incorrect buffer size may be passed to
   get_memory_map when jumping to get_map

 arch/x86/boot/compressed/eboot.c | 239 +++++++++++++++++++++++++++------------
 1 file changed, 167 insertions(+), 72 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index b7388a4..bfa177a 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -982,46 +982,198 @@ fail:
 	return NULL;
 }
 
+static void add_e820ext(struct boot_params *params,
+			struct setup_data *e820ext, u32 nr_entries)
+{
+	struct setup_data *data;
+	efi_status_t status;
+	unsigned long size;
+
+	e820ext->type = SETUP_E820_EXT;
+	e820ext->len = nr_entries * sizeof(struct e820entry);
+	e820ext->next = 0;
+
+	data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
+
+	while (data && data->next)
+		data = (struct setup_data *)(unsigned long)data->next;
+
+	if (data)
+		data->next = (unsigned long)e820ext;
+	else
+		params->hdr.setup_data = (unsigned long)e820ext;
+}
+
+static efi_status_t setup_e820(struct boot_params *params,
+			       struct setup_data *e820ext, u32 e820ext_size)
+{
+	struct e820entry *e820_map = &params->e820_map[0];
+	struct efi_info *efi = &params->efi_info;
+	struct e820entry *prev = NULL;
+	u32 nr_entries;
+	u32 nr_desc;
+	int i;
+
+	nr_entries = 0;
+	nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size;
+
+	for (i = 0; i < nr_desc; i++) {
+		efi_memory_desc_t *d;
+		unsigned int e820_type = 0;
+		unsigned long m = efi->efi_memmap;
+
+		d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size));
+		switch (d->type) {
+		case EFI_RESERVED_TYPE:
+		case EFI_RUNTIME_SERVICES_CODE:
+		case EFI_RUNTIME_SERVICES_DATA:
+		case EFI_MEMORY_MAPPED_IO:
+		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+		case EFI_PAL_CODE:
+			e820_type = E820_RESERVED;
+			break;
+
+		case EFI_UNUSABLE_MEMORY:
+			e820_type = E820_UNUSABLE;
+			break;
+
+		case EFI_ACPI_RECLAIM_MEMORY:
+			e820_type = E820_ACPI;
+			break;
+
+		case EFI_LOADER_CODE:
+		case EFI_LOADER_DATA:
+		case EFI_BOOT_SERVICES_CODE:
+		case EFI_BOOT_SERVICES_DATA:
+		case EFI_CONVENTIONAL_MEMORY:
+			e820_type = E820_RAM;
+			break;
+
+		case EFI_ACPI_MEMORY_NVS:
+			e820_type = E820_NVS;
+			break;
+
+		default:
+			continue;
+		}
+
+		/* Merge adjacent mappings */
+		if (prev && prev->type == e820_type &&
+		    (prev->addr + prev->size) == d->phys_addr) {
+			prev->size += d->num_pages << 12;
+			continue;
+		}
+
+		if (nr_entries == ARRAY_SIZE(params->e820_map)) {
+			u32 need = (nr_desc - i) * sizeof(struct e820entry) +
+				   sizeof(struct setup_data);
+
+			if (!e820ext || e820ext_size < need)
+				return EFI_BUFFER_TOO_SMALL;
+
+			/* boot_params map full, switch to e820 extended */
+			e820_map = (struct e820entry *)e820ext->data;
+		}
+
+		e820_map->addr = d->phys_addr;
+		e820_map->size = d->num_pages << PAGE_SHIFT;
+		e820_map->type = e820_type;
+		prev = e820_map++;
+		nr_entries++;
+	}
+
+	if (nr_entries > ARRAY_SIZE(params->e820_map)) {
+		u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map);
+
+		add_e820ext(params, e820ext, nr_e820ext);
+		nr_entries -= nr_e820ext;
+	}
+
+	params->e820_entries = (u8)nr_entries;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
+				  u32 *e820ext_size)
+{
+	efi_status_t status;
+	unsigned long size;
+
+	size = sizeof(struct setup_data) +
+		sizeof(struct e820entry) * nr_desc;
+
+	if (*e820ext) {
+		efi_call_phys1(sys_table->boottime->free_pool, *e820ext);
+		*e820ext = NULL;
+		*e820ext_size = 0;
+	}
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, size, e820ext);
+
+	if (status == EFI_SUCCESS)
+		*e820ext_size = size;
+
+	return status;
+}
+
 static efi_status_t exit_boot(struct boot_params *boot_params,
 			      void *handle)
 {
 	struct efi_info *efi = &boot_params->efi_info;
-	struct e820entry *e820_map = &boot_params->e820_map[0];
-	struct e820entry *prev = NULL;
-	unsigned long size, key, desc_size, _size;
+	unsigned long map_sz, key, desc_size, buf_sz;
 	efi_memory_desc_t *mem_map;
+	struct setup_data *e820ext;
+	__u32 e820ext_size;
+	__u32 nr_desc, prev_nr_desc;
 	efi_status_t status;
 	__u32 desc_version;
 	bool called_exit = false;
-	u8 nr_entries;
-	int i;
 
-	size = sizeof(*mem_map) * 32;
+	nr_desc = 0;
+	e820ext = NULL;
+	e820ext_size = 0;
+	map_sz = sizeof(*mem_map) * 32;
 
 again:
-	size += sizeof(*mem_map) * 2;
-	_size = size;
-	status = low_alloc(size, 1, (unsigned long *)&mem_map);
+	map_sz += sizeof(*mem_map) * 2;
+	buf_sz = map_sz;
+	status = low_alloc(buf_sz, 1, (unsigned long *)&mem_map);
 	if (status != EFI_SUCCESS)
 		return status;
 
 get_map:
-	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
+	map_sz = buf_sz;
+	status = efi_call_phys5(sys_table->boottime->get_memory_map, &map_sz,
 				mem_map, &key, &desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		low_free(_size, (unsigned long)mem_map);
+		low_free(buf_sz, (unsigned long)mem_map);
 		goto again;
 	}
 
 	if (status != EFI_SUCCESS)
 		goto free_mem_map;
 
+	prev_nr_desc = nr_desc;
+	nr_desc = map_sz / desc_size;
+	if (nr_desc > prev_nr_desc &&
+	    nr_desc > ARRAY_SIZE(boot_params->e820_map)) {
+		u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map);
+
+		status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size);
+		if (status != EFI_SUCCESS)
+			goto free_mem_map;
+
+		goto get_map; /* Allocated memory, get map again */
+	}
+
 	memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
 	efi->efi_systab = (unsigned long)sys_table;
 	efi->efi_memdesc_size = desc_size;
 	efi->efi_memdesc_version = desc_version;
 	efi->efi_memmap = (unsigned long)mem_map;
-	efi->efi_memmap_size = size;
+	efi->efi_memmap_size = map_sz;
 
 #ifdef CONFIG_X86_64
 	efi->efi_systab_hi = (unsigned long)sys_table >> 32;
@@ -1049,69 +1201,12 @@ get_map:
 	/* Historic? */
 	boot_params->alt_mem_k = 32 * 1024;
 
-	/*
-	 * Convert the EFI memory map to E820.
-	 */
-	nr_entries = 0;
-	for (i = 0; i < size / desc_size; i++) {
-		efi_memory_desc_t *d;
-		unsigned int e820_type = 0;
-		unsigned long m = (unsigned long)mem_map;
-
-		d = (efi_memory_desc_t *)(m + (i * desc_size));
-		switch (d->type) {
-		case EFI_RESERVED_TYPE:
-		case EFI_RUNTIME_SERVICES_CODE:
-		case EFI_RUNTIME_SERVICES_DATA:
-		case EFI_MEMORY_MAPPED_IO:
-		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
-		case EFI_PAL_CODE:
-			e820_type = E820_RESERVED;
-			break;
-
-		case EFI_UNUSABLE_MEMORY:
-			e820_type = E820_UNUSABLE;
-			break;
-
-		case EFI_ACPI_RECLAIM_MEMORY:
-			e820_type = E820_ACPI;
-			break;
-
-		case EFI_LOADER_CODE:
-		case EFI_LOADER_DATA:
-		case EFI_BOOT_SERVICES_CODE:
-		case EFI_BOOT_SERVICES_DATA:
-		case EFI_CONVENTIONAL_MEMORY:
-			e820_type = E820_RAM;
-			break;
-
-		case EFI_ACPI_MEMORY_NVS:
-			e820_type = E820_NVS;
-			break;
-
-		default:
-			continue;
-		}
-
-		/* Merge adjacent mappings */
-		if (prev && prev->type == e820_type &&
-		    (prev->addr + prev->size) == d->phys_addr)
-			prev->size += d->num_pages << 12;
-		else {
-			e820_map->addr = d->phys_addr;
-			e820_map->size = d->num_pages << 12;
-			e820_map->type = e820_type;
-			prev = e820_map++;
-			nr_entries++;
-		}
-	}
-
-	boot_params->e820_entries = nr_entries;
+	status = setup_e820(boot_params, e820ext, e820ext_size);
 
-	return EFI_SUCCESS;
+	return status;
 
 free_mem_map:
-	low_free(_size, (unsigned long)mem_map);
+	low_free(buf_sz, (unsigned long)mem_map);
 	return status;
 }
 
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCHv3] x86: EFI stub support for large memory maps
  2013-09-23  1:59   ` [PATCHv3] " Linn Crosetto
@ 2013-09-25 12:58     ` Matt Fleming
  2013-09-25 22:45       ` Linn Crosetto
  0 siblings, 1 reply; 11+ messages in thread
From: Matt Fleming @ 2013-09-25 12:58 UTC (permalink / raw)
  To: Linn Crosetto
  Cc: matt.fleming, hpa, tglx, mingo, x86, yinghai, linux-efi, linux-kernel

On Sun, 22 Sep, at 07:59:08PM, Linn Crosetto wrote:
> This patch fixes a problem with EFI memory maps larger than 128 entries
> when booting using the EFI stub, which results in overflowing e820_map
> in boot_params and an eventual halt when checking the map size in
> sanitize_e820_map().
> 
> If the number of map entries is greater than what can fit in e820_map,
> add the extra entries to the setup_data list using type SETUP_E820_EXT.
> These extra entries are then picked up when the setup_data list is
> parsed in parse_e820_ext().
> 
> Signed-off-by: Linn Crosetto <linn@hp.com>
> ---
> Changes from v2:
>  * Removed unnecessary optimization in alloc_e820ext() (Matt Fleming)
>  * Fixed a bug where an incorrect buffer size may be passed to
>    get_memory_map when jumping to get_map
> 
>  arch/x86/boot/compressed/eboot.c | 239 +++++++++++++++++++++++++++------------
>  1 file changed, 167 insertions(+), 72 deletions(-)

Thanks Linn. I applied this to the 'next' branch at,

  git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git

but it required a bit of massaging to apply on top of the changes
already there. Could you confirm that my changes are OK? I've included
the modified commit below.

---

>From 81c9e1df196ad531edabe726a2d7a610fb04fdd6 Mon Sep 17 00:00:00 2001
From: Linn Crosetto <linn@hp.com>
Date: Sun, 22 Sep 2013 19:59:08 -0600
Subject: [PATCH] x86: EFI stub support for large memory maps

This patch fixes a problem with EFI memory maps larger than 128 entries
when booting using the EFI stub, which results in overflowing e820_map
in boot_params and an eventual halt when checking the map size in
sanitize_e820_map().

If the number of map entries is greater than what can fit in e820_map,
add the extra entries to the setup_data list using type SETUP_E820_EXT.
These extra entries are then picked up when the setup_data list is
parsed in parse_e820_ext().

Signed-off-by: Linn Crosetto <linn@hp.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/boot/compressed/eboot.c | 220 ++++++++++++++++++++++++++++-----------
 1 file changed, 159 insertions(+), 61 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index beb07a4..482006e 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -519,71 +519,47 @@ fail:
 	return NULL;
 }
 
-static efi_status_t exit_boot(struct boot_params *boot_params,
-			      void *handle)
+static void add_e820ext(struct boot_params *params,
+			struct setup_data *e820ext, u32 nr_entries)
 {
-	struct efi_info *efi = &boot_params->efi_info;
-	struct e820entry *e820_map = &boot_params->e820_map[0];
-	struct e820entry *prev = NULL;
-	unsigned long size, key, desc_size, _size;
-	efi_memory_desc_t *mem_map;
+	struct setup_data *data;
 	efi_status_t status;
-	__u32 desc_version;
-	bool called_exit = false;
-	u8 nr_entries;
-	int i;
+	unsigned long size;
 
-get_map:
-	status = efi_get_memory_map(sys_table, &mem_map, &size, &desc_size,
-				    &desc_version, &key);
+	e820ext->type = SETUP_E820_EXT;
+	e820ext->len = nr_entries * sizeof(struct e820entry);
+	e820ext->next = 0;
 
-	if (status != EFI_SUCCESS)
-		return status;
-
-	memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
-	efi->efi_systab = (unsigned long)sys_table;
-	efi->efi_memdesc_size = desc_size;
-	efi->efi_memdesc_version = desc_version;
-	efi->efi_memmap = (unsigned long)mem_map;
-	efi->efi_memmap_size = size;
-
-#ifdef CONFIG_X86_64
-	efi->efi_systab_hi = (unsigned long)sys_table >> 32;
-	efi->efi_memmap_hi = (unsigned long)mem_map >> 32;
-#endif
+	data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
 
-	/* Might as well exit boot services now */
-	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
-				handle, key);
-	if (status != EFI_SUCCESS) {
-		/*
-		 * ExitBootServices() will fail if any of the event
-		 * handlers change the memory map. In which case, we
-		 * must be prepared to retry, but only once so that
-		 * we're guaranteed to exit on repeated failures instead
-		 * of spinning forever.
-		 */
-		if (called_exit)
-			goto free_mem_map;
+	while (data && data->next)
+		data = (struct setup_data *)(unsigned long)data->next;
 
-		called_exit = true;
-		efi_call_phys1(sys_table->boottime->free_pool, mem_map);
-		goto get_map;
-	}
+	if (data)
+		data->next = (unsigned long)e820ext;
+	else
+		params->hdr.setup_data = (unsigned long)e820ext;
+}
 
-	/* Historic? */
-	boot_params->alt_mem_k = 32 * 1024;
+static efi_status_t setup_e820(struct boot_params *params,
+			       struct setup_data *e820ext, u32 e820ext_size)
+{
+	struct e820entry *e820_map = &params->e820_map[0];
+	struct efi_info *efi = &params->efi_info;
+	struct e820entry *prev = NULL;
+	u32 nr_entries;
+	u32 nr_desc;
+	int i;
 
-	/*
-	 * Convert the EFI memory map to E820.
-	 */
 	nr_entries = 0;
-	for (i = 0; i < size / desc_size; i++) {
+	nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size;
+
+	for (i = 0; i < nr_desc; i++) {
 		efi_memory_desc_t *d;
 		unsigned int e820_type = 0;
-		unsigned long m = (unsigned long)mem_map;
+		unsigned long m = efi->efi_memmap;
 
-		d = (efi_memory_desc_t *)(m + (i * desc_size));
+		d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size));
 		switch (d->type) {
 		case EFI_RESERVED_TYPE:
 		case EFI_RUNTIME_SERVICES_CODE:
@@ -620,18 +596,140 @@ get_map:
 
 		/* Merge adjacent mappings */
 		if (prev && prev->type == e820_type &&
-		    (prev->addr + prev->size) == d->phys_addr)
+		    (prev->addr + prev->size) == d->phys_addr) {
 			prev->size += d->num_pages << 12;
-		else {
-			e820_map->addr = d->phys_addr;
-			e820_map->size = d->num_pages << 12;
-			e820_map->type = e820_type;
-			prev = e820_map++;
-			nr_entries++;
+			continue;
 		}
+
+		if (nr_entries == ARRAY_SIZE(params->e820_map)) {
+			u32 need = (nr_desc - i) * sizeof(struct e820entry) +
+				   sizeof(struct setup_data);
+
+			if (!e820ext || e820ext_size < need)
+				return EFI_BUFFER_TOO_SMALL;
+
+			/* boot_params map full, switch to e820 extended */
+			e820_map = (struct e820entry *)e820ext->data;
+		}
+
+		e820_map->addr = d->phys_addr;
+		e820_map->size = d->num_pages << PAGE_SHIFT;
+		e820_map->type = e820_type;
+		prev = e820_map++;
+		nr_entries++;
 	}
 
-	boot_params->e820_entries = nr_entries;
+	if (nr_entries > ARRAY_SIZE(params->e820_map)) {
+		u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map);
+
+		add_e820ext(params, e820ext, nr_e820ext);
+		nr_entries -= nr_e820ext;
+	}
+
+	params->e820_entries = (u8)nr_entries;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
+				  u32 *e820ext_size)
+{
+	efi_status_t status;
+	unsigned long size;
+
+	size = sizeof(struct setup_data) +
+		sizeof(struct e820entry) * nr_desc;
+
+	if (*e820ext) {
+		efi_call_phys1(sys_table->boottime->free_pool, *e820ext);
+		*e820ext = NULL;
+		*e820ext_size = 0;
+	}
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, size, e820ext);
+
+	if (status == EFI_SUCCESS)
+		*e820ext_size = size;
+
+	return status;
+}
+
+static efi_status_t exit_boot(struct boot_params *boot_params,
+			      void *handle)
+{
+	struct efi_info *efi = &boot_params->efi_info;
+	unsigned long map_sz, key, desc_size;
+	efi_memory_desc_t *mem_map;
+	struct setup_data *e820ext;
+	__u32 e820ext_size;
+	__u32 nr_desc, prev_nr_desc;
+	efi_status_t status;
+	__u32 desc_version;
+	bool called_exit = false;
+	u8 nr_entries;
+	int i;
+
+	nr_desc = 0;
+	e820ext = NULL;
+	e820ext_size = 0;
+
+get_map:
+	status = efi_get_memory_map(sys_table, &mem_map, &map_sz, &desc_size,
+				    &desc_version, &key);
+
+	if (status != EFI_SUCCESS)
+		return status;
+
+	prev_nr_desc = nr_desc;
+	nr_desc = map_sz / desc_size;
+	if (nr_desc > prev_nr_desc &&
+	    nr_desc > ARRAY_SIZE(boot_params->e820_map)) {
+		u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map);
+
+		status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size);
+		if (status != EFI_SUCCESS)
+			goto free_mem_map;
+
+		efi_call_phys1(sys_table->boottime->free_pool, mem_map);
+		goto get_map; /* Allocated memory, get map again */
+	}
+
+	memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
+	efi->efi_systab = (unsigned long)sys_table;
+	efi->efi_memdesc_size = desc_size;
+	efi->efi_memdesc_version = desc_version;
+	efi->efi_memmap = (unsigned long)mem_map;
+	efi->efi_memmap_size = map_sz;
+
+#ifdef CONFIG_X86_64
+	efi->efi_systab_hi = (unsigned long)sys_table >> 32;
+	efi->efi_memmap_hi = (unsigned long)mem_map >> 32;
+#endif
+
+	/* Might as well exit boot services now */
+	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
+				handle, key);
+	if (status != EFI_SUCCESS) {
+		/*
+		 * ExitBootServices() will fail if any of the event
+		 * handlers change the memory map. In which case, we
+		 * must be prepared to retry, but only once so that
+		 * we're guaranteed to exit on repeated failures instead
+		 * of spinning forever.
+		 */
+		if (called_exit)
+			goto free_mem_map;
+
+		called_exit = true;
+		efi_call_phys1(sys_table->boottime->free_pool, mem_map);
+		goto get_map;
+	}
+
+	/* Historic? */
+	boot_params->alt_mem_k = 32 * 1024;
+
+	status = setup_e820(boot_params, e820ext, e820ext_size);
 
 	return EFI_SUCCESS;
 
-- 
1.8.1.4


-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCHv3] x86: EFI stub support for large memory maps
  2013-09-25 12:58     ` Matt Fleming
@ 2013-09-25 22:45       ` Linn Crosetto
  2013-09-26 11:34         ` Matt Fleming
  0 siblings, 1 reply; 11+ messages in thread
From: Linn Crosetto @ 2013-09-25 22:45 UTC (permalink / raw)
  To: Matt Fleming
  Cc: matt.fleming, hpa, tglx, mingo, x86, yinghai, linux-efi, linux-kernel

On Wed, Sep 25, 2013 at 01:58:40PM +0100, Matt Fleming wrote:
> On Sun, 22 Sep, at 07:59:08PM, Linn Crosetto wrote:
> > This patch fixes a problem with EFI memory maps larger than 128 entries
> > when booting using the EFI stub, which results in overflowing e820_map
> > in boot_params and an eventual halt when checking the map size in
> > sanitize_e820_map().
> > 
> > If the number of map entries is greater than what can fit in e820_map,
> > add the extra entries to the setup_data list using type SETUP_E820_EXT.
> > These extra entries are then picked up when the setup_data list is
> > parsed in parse_e820_ext().
> > 
> > Signed-off-by: Linn Crosetto <linn@hp.com>
> > ---
> > Changes from v2:
> >  * Removed unnecessary optimization in alloc_e820ext() (Matt Fleming)
> >  * Fixed a bug where an incorrect buffer size may be passed to
> >    get_memory_map when jumping to get_map
> > 
> >  arch/x86/boot/compressed/eboot.c | 239 +++++++++++++++++++++++++++------------
> >  1 file changed, 167 insertions(+), 72 deletions(-)
> 
> Thanks Linn. I applied this to the 'next' branch at,
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git
> 
> but it required a bit of massaging to apply on top of the changes
> already there. Could you confirm that my changes are OK? I've included
> the modified commit below.

I have tested the 'next' branch on a system with a large number of entries in
the memory map and the merge appears to be functionally correct. 

With the change in commit ae8e9060, I noticed the memory map is no longer placed
in memory allocated with low_alloc(). I have not looked into what effect it
could have, if any.

> +	/* Historic? */
> +	boot_params->alt_mem_k = 32 * 1024;
> +
> +	status = setup_e820(boot_params, e820ext, e820ext_size);
>  
>  	return EFI_SUCCESS;

I might add the following to your merge for semantic reasons:

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 04b228d..a7677ba 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -730,6 +730,8 @@ get_map:
        boot_params->alt_mem_k = 32 * 1024;
 
        status = setup_e820(boot_params, e820ext, e820ext_size);
+       if (status != EFI_SUCCESS)
+               return status;
 
        return EFI_SUCCESS;



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCHv3] x86: EFI stub support for large memory maps
  2013-09-25 22:45       ` Linn Crosetto
@ 2013-09-26 11:34         ` Matt Fleming
  2013-09-27 23:46           ` Linn Crosetto
  0 siblings, 1 reply; 11+ messages in thread
From: Matt Fleming @ 2013-09-26 11:34 UTC (permalink / raw)
  To: Linn Crosetto
  Cc: matt.fleming, hpa, tglx, mingo, x86, yinghai, linux-efi, linux-kernel

On Wed, 25 Sep, at 04:45:41PM, Linn Crosetto wrote:
> On Wed, Sep 25, 2013 at 01:58:40PM +0100, Matt Fleming wrote:
> > On Sun, 22 Sep, at 07:59:08PM, Linn Crosetto wrote:
> > > This patch fixes a problem with EFI memory maps larger than 128 entries
> > > when booting using the EFI stub, which results in overflowing e820_map
> > > in boot_params and an eventual halt when checking the map size in
> > > sanitize_e820_map().
> > > 
> > > If the number of map entries is greater than what can fit in e820_map,
> > > add the extra entries to the setup_data list using type SETUP_E820_EXT.
> > > These extra entries are then picked up when the setup_data list is
> > > parsed in parse_e820_ext().
> > > 
> > > Signed-off-by: Linn Crosetto <linn@hp.com>
> > > ---
> > > Changes from v2:
> > >  * Removed unnecessary optimization in alloc_e820ext() (Matt Fleming)
> > >  * Fixed a bug where an incorrect buffer size may be passed to
> > >    get_memory_map when jumping to get_map
> > > 
> > >  arch/x86/boot/compressed/eboot.c | 239 +++++++++++++++++++++++++++------------
> > >  1 file changed, 167 insertions(+), 72 deletions(-)
> > 
> > Thanks Linn. I applied this to the 'next' branch at,
> > 
> >   git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git
> > 
> > but it required a bit of massaging to apply on top of the changes
> > already there. Could you confirm that my changes are OK? I've included
> > the modified commit below.
> 
> I have tested the 'next' branch on a system with a large number of entries in
> the memory map and the merge appears to be functionally correct. 
 
Excellent, thank you for verifying.

> With the change in commit ae8e9060, I noticed the memory map is no longer placed
> in memory allocated with low_alloc(). I have not looked into what effect it
> could have, if any.
 
Correct. I haven't run into any problems on my test machines.

> > +	/* Historic? */
> > +	boot_params->alt_mem_k = 32 * 1024;
> > +
> > +	status = setup_e820(boot_params, e820ext, e820ext_size);
> >  
> >  	return EFI_SUCCESS;
> 
> I might add the following to your merge for semantic reasons:
> 
> diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
> index 04b228d..a7677ba 100644
> --- a/arch/x86/boot/compressed/eboot.c
> +++ b/arch/x86/boot/compressed/eboot.c
> @@ -730,6 +730,8 @@ get_map:
>         boot_params->alt_mem_k = 32 * 1024;
>  
>         status = setup_e820(boot_params, e820ext, e820ext_size);
> +       if (status != EFI_SUCCESS)
> +               return status;
>  
>         return EFI_SUCCESS;

Aha, nice catch! Though if setup_e820() fails we should be jumping to
the 'free_mem_map' label so we don't leak the memory map, like so,

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 04b228d..602950b 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -730,8 +730,8 @@ get_map:
 	boot_params->alt_mem_k = 32 * 1024;
 
 	status = setup_e820(boot_params, e820ext, e820ext_size);
-
-	return EFI_SUCCESS;
+	if (status == EFI_SUCCESS)
+		return status;
 
 free_mem_map:
 	efi_call_phys1(sys_table->boottime->free_pool, mem_map);


I've fixed this up and pushed out a new patch.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCHv3] x86: EFI stub support for large memory maps
  2013-09-26 11:34         ` Matt Fleming
@ 2013-09-27 23:46           ` Linn Crosetto
  2013-09-30  9:22             ` Matt Fleming
  0 siblings, 1 reply; 11+ messages in thread
From: Linn Crosetto @ 2013-09-27 23:46 UTC (permalink / raw)
  To: Matt Fleming
  Cc: matt.fleming, hpa, tglx, mingo, x86, yinghai, linux-efi, linux-kernel

On Thu, Sep 26, 2013 at 12:34:00PM +0100, Matt Fleming wrote:
> > I might add the following to your merge for semantic reasons:
> > 
> > diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
> > index 04b228d..a7677ba 100644
> > --- a/arch/x86/boot/compressed/eboot.c
> > +++ b/arch/x86/boot/compressed/eboot.c
> > @@ -730,6 +730,8 @@ get_map:
> >         boot_params->alt_mem_k = 32 * 1024;
> >  
> >         status = setup_e820(boot_params, e820ext, e820ext_size);
> > +       if (status != EFI_SUCCESS)
> > +               return status;
> >  
> >         return EFI_SUCCESS;
> 
> Aha, nice catch! Though if setup_e820() fails we should be jumping to
> the 'free_mem_map' label so we don't leak the memory map, like so,
> 
> diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
> index 04b228d..602950b 100644
> --- a/arch/x86/boot/compressed/eboot.c
> +++ b/arch/x86/boot/compressed/eboot.c
> @@ -730,8 +730,8 @@ get_map:
>  	boot_params->alt_mem_k = 32 * 1024;
>  
>  	status = setup_e820(boot_params, e820ext, e820ext_size);
> -
> -	return EFI_SUCCESS;
> +	if (status == EFI_SUCCESS)
> +		return status;
>  
>  free_mem_map:
>  	efi_call_phys1(sys_table->boottime->free_pool, mem_map);

Given that we have already successfully called exit_boot_services, can we still
make this call to free_pool?

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCHv3] x86: EFI stub support for large memory maps
  2013-09-27 23:46           ` Linn Crosetto
@ 2013-09-30  9:22             ` Matt Fleming
  0 siblings, 0 replies; 11+ messages in thread
From: Matt Fleming @ 2013-09-30  9:22 UTC (permalink / raw)
  To: Linn Crosetto
  Cc: matt.fleming, hpa, tglx, mingo, x86, yinghai, linux-efi, linux-kernel

On Fri, 27 Sep, at 05:46:07PM, Linn Crosetto wrote:
> Given that we have already successfully called exit_boot_services, can
> we still make this call to free_pool?

Urgh, good point. The answer is "no, not really".

I'll add your change verbatim and push out the 'next' branch again.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2013-09-30  9:22 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-23 21:02 [PATCH] x86: EFI stub support for large memory maps Linn Crosetto
2013-09-06 10:03 ` Matt Fleming
2013-09-06 17:04 ` [PATCHv2] " Linn Crosetto
2013-09-17 20:14   ` Matt Fleming
2013-09-23  0:23     ` Linn Crosetto
2013-09-23  1:59   ` [PATCHv3] " Linn Crosetto
2013-09-25 12:58     ` Matt Fleming
2013-09-25 22:45       ` Linn Crosetto
2013-09-26 11:34         ` Matt Fleming
2013-09-27 23:46           ` Linn Crosetto
2013-09-30  9:22             ` Matt Fleming

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).