134841303SHeinrich Schuchardt // SPDX-License-Identifier: GPL-2.0+
234841303SHeinrich Schuchardt /*
334841303SHeinrich Schuchardt  * efi_selftest_memory
434841303SHeinrich Schuchardt  *
534841303SHeinrich Schuchardt  * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
634841303SHeinrich Schuchardt  *
734841303SHeinrich Schuchardt  * This unit test checks the following runtime services:
834841303SHeinrich Schuchardt  * AllocatePages, FreePages, GetMemoryMap
9*34c96659SHeinrich Schuchardt  *
10*34c96659SHeinrich Schuchardt  * The memory type used for the device tree is checked.
1134841303SHeinrich Schuchardt  */
1234841303SHeinrich Schuchardt 
1334841303SHeinrich Schuchardt #include <efi_selftest.h>
1434841303SHeinrich Schuchardt 
1534841303SHeinrich Schuchardt #define EFI_ST_NUM_PAGES 8
1634841303SHeinrich Schuchardt 
17*34c96659SHeinrich Schuchardt static const efi_guid_t fdt_guid = EFI_FDT_GUID;
1834841303SHeinrich Schuchardt static struct efi_boot_services *boottime;
19*34c96659SHeinrich Schuchardt static u64 fdt_addr;
2034841303SHeinrich Schuchardt 
2134841303SHeinrich Schuchardt /**
2234841303SHeinrich Schuchardt  * setup() - setup unit test
2334841303SHeinrich Schuchardt  *
2434841303SHeinrich Schuchardt  * @handle:	handle of the loaded image
2534841303SHeinrich Schuchardt  * @systable:	system table
2634841303SHeinrich Schuchardt  * Return:	EFI_ST_SUCCESS for success
2734841303SHeinrich Schuchardt  */
setup(const efi_handle_t handle,const struct efi_system_table * systable)2834841303SHeinrich Schuchardt static int setup(const efi_handle_t handle,
2934841303SHeinrich Schuchardt 		 const struct efi_system_table *systable)
3034841303SHeinrich Schuchardt {
31*34c96659SHeinrich Schuchardt 	size_t i;
32*34c96659SHeinrich Schuchardt 
3334841303SHeinrich Schuchardt 	boottime = systable->boottime;
3434841303SHeinrich Schuchardt 
35*34c96659SHeinrich Schuchardt 	for (i = 0; i < systable->nr_tables; ++i) {
36*34c96659SHeinrich Schuchardt 		if (!efi_st_memcmp(&systable->tables[i].guid, &fdt_guid,
37*34c96659SHeinrich Schuchardt 				   sizeof(efi_guid_t))) {
38*34c96659SHeinrich Schuchardt 			if (fdt_addr) {
39*34c96659SHeinrich Schuchardt 				efi_st_error("Duplicate device tree\n");
40*34c96659SHeinrich Schuchardt 				return EFI_ST_FAILURE;
41*34c96659SHeinrich Schuchardt 			}
42*34c96659SHeinrich Schuchardt 			fdt_addr = (uintptr_t)systable->tables[i].table;
43*34c96659SHeinrich Schuchardt 		}
44*34c96659SHeinrich Schuchardt 	}
4534841303SHeinrich Schuchardt 	return EFI_ST_SUCCESS;
4634841303SHeinrich Schuchardt }
4734841303SHeinrich Schuchardt 
4834841303SHeinrich Schuchardt /**
4934841303SHeinrich Schuchardt  * find_in_memory_map() - check matching memory map entry exists
5034841303SHeinrich Schuchardt  *
5134841303SHeinrich Schuchardt  * @memory_map:		memory map
5234841303SHeinrich Schuchardt  * @desc_size:		number of memory map entries
5334841303SHeinrich Schuchardt  * @addr:		physical address to find in the map
5434841303SHeinrich Schuchardt  * @type:		expected memory type
5534841303SHeinrich Schuchardt  * Return:		EFI_ST_SUCCESS for success
5634841303SHeinrich Schuchardt  */
find_in_memory_map(efi_uintn_t map_size,struct efi_mem_desc * memory_map,efi_uintn_t desc_size,u64 addr,int memory_type)5734841303SHeinrich Schuchardt static int find_in_memory_map(efi_uintn_t map_size,
5834841303SHeinrich Schuchardt 			      struct efi_mem_desc *memory_map,
5934841303SHeinrich Schuchardt 			      efi_uintn_t desc_size,
6034841303SHeinrich Schuchardt 			      u64 addr, int memory_type)
6134841303SHeinrich Schuchardt {
6234841303SHeinrich Schuchardt 	efi_uintn_t i;
6334841303SHeinrich Schuchardt 	bool found = false;
6434841303SHeinrich Schuchardt 
6534841303SHeinrich Schuchardt 	for (i = 0; map_size; ++i, map_size -= desc_size) {
6634841303SHeinrich Schuchardt 		struct efi_mem_desc *entry = &memory_map[i];
6734841303SHeinrich Schuchardt 
6834841303SHeinrich Schuchardt 		if (addr >= entry->physical_start &&
6934841303SHeinrich Schuchardt 		    addr < entry->physical_start +
7034841303SHeinrich Schuchardt 			    (entry->num_pages << EFI_PAGE_SHIFT)) {
7134841303SHeinrich Schuchardt 			if (found) {
7234841303SHeinrich Schuchardt 				efi_st_error("Duplicate memory map entry\n");
7334841303SHeinrich Schuchardt 				return EFI_ST_FAILURE;
7434841303SHeinrich Schuchardt 			}
7534841303SHeinrich Schuchardt 			found = true;
7634841303SHeinrich Schuchardt 			if (memory_type != entry->type) {
7734841303SHeinrich Schuchardt 				efi_st_error
7834841303SHeinrich Schuchardt 					("Wrong memory type %d, expected %d\n",
7934841303SHeinrich Schuchardt 					 entry->type, memory_type);
8034841303SHeinrich Schuchardt 				return EFI_ST_FAILURE;
8134841303SHeinrich Schuchardt 			}
8234841303SHeinrich Schuchardt 		}
8334841303SHeinrich Schuchardt 	}
8434841303SHeinrich Schuchardt 	if (!found) {
8534841303SHeinrich Schuchardt 		efi_st_error("Missing memory map entry\n");
8634841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
8734841303SHeinrich Schuchardt 	}
8834841303SHeinrich Schuchardt 	return EFI_ST_SUCCESS;
8934841303SHeinrich Schuchardt }
9034841303SHeinrich Schuchardt 
9134841303SHeinrich Schuchardt /*
9234841303SHeinrich Schuchardt  * execute() - execute unit test
9334841303SHeinrich Schuchardt  *
9434841303SHeinrich Schuchardt  * Return:	EFI_ST_SUCCESS for success
9534841303SHeinrich Schuchardt  */
execute(void)9634841303SHeinrich Schuchardt static int execute(void)
9734841303SHeinrich Schuchardt {
9834841303SHeinrich Schuchardt 	u64 p1;
9934841303SHeinrich Schuchardt 	u64 p2;
10034841303SHeinrich Schuchardt 	efi_uintn_t map_size = 0;
10134841303SHeinrich Schuchardt 	efi_uintn_t map_key;
10234841303SHeinrich Schuchardt 	efi_uintn_t desc_size;
10334841303SHeinrich Schuchardt 	u32 desc_version;
10434841303SHeinrich Schuchardt 	struct efi_mem_desc *memory_map;
10534841303SHeinrich Schuchardt 	efi_status_t ret;
10634841303SHeinrich Schuchardt 
10734841303SHeinrich Schuchardt 	/* Allocate two page ranges with different memory type */
10834841303SHeinrich Schuchardt 	ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
10934841303SHeinrich Schuchardt 				       EFI_RUNTIME_SERVICES_CODE,
11034841303SHeinrich Schuchardt 				       EFI_ST_NUM_PAGES, &p1);
11134841303SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
11234841303SHeinrich Schuchardt 		efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
11334841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
11434841303SHeinrich Schuchardt 	}
11534841303SHeinrich Schuchardt 	ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
11634841303SHeinrich Schuchardt 				       EFI_RUNTIME_SERVICES_DATA,
11734841303SHeinrich Schuchardt 				       EFI_ST_NUM_PAGES, &p2);
11834841303SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
11934841303SHeinrich Schuchardt 		efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
12034841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
12134841303SHeinrich Schuchardt 	}
12234841303SHeinrich Schuchardt 
12334841303SHeinrich Schuchardt 	/* Load memory map */
12434841303SHeinrich Schuchardt 	ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
12534841303SHeinrich Schuchardt 				       &desc_version);
12634841303SHeinrich Schuchardt 	if (ret != EFI_BUFFER_TOO_SMALL) {
12734841303SHeinrich Schuchardt 		efi_st_error
12834841303SHeinrich Schuchardt 			("GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
12934841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
13034841303SHeinrich Schuchardt 	}
13134841303SHeinrich Schuchardt 	/* Allocate extra space for newly allocated memory */
13234841303SHeinrich Schuchardt 	map_size += sizeof(struct efi_mem_desc);
13334841303SHeinrich Schuchardt 	ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
13434841303SHeinrich Schuchardt 				      (void **)&memory_map);
13534841303SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
13634841303SHeinrich Schuchardt 		efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
13734841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
13834841303SHeinrich Schuchardt 	}
13934841303SHeinrich Schuchardt 	ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
14034841303SHeinrich Schuchardt 				       &desc_size, &desc_version);
14134841303SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
14234841303SHeinrich Schuchardt 		efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
14334841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
14434841303SHeinrich Schuchardt 	}
14534841303SHeinrich Schuchardt 
14634841303SHeinrich Schuchardt 	/* Check memory map entries */
14734841303SHeinrich Schuchardt 	if (find_in_memory_map(map_size, memory_map, desc_size, p1,
14834841303SHeinrich Schuchardt 			       EFI_RUNTIME_SERVICES_CODE) != EFI_ST_SUCCESS)
14934841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
15034841303SHeinrich Schuchardt 	if (find_in_memory_map(map_size, memory_map, desc_size, p2,
15134841303SHeinrich Schuchardt 			       EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS)
15234841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
15334841303SHeinrich Schuchardt 
15434841303SHeinrich Schuchardt 	/* Free memory */
15534841303SHeinrich Schuchardt 	ret = boottime->free_pages(p1, EFI_ST_NUM_PAGES);
15634841303SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
15734841303SHeinrich Schuchardt 		efi_st_error("FreePages did not return EFI_SUCCESS\n");
15834841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
15934841303SHeinrich Schuchardt 	}
16034841303SHeinrich Schuchardt 	ret = boottime->free_pages(p2, EFI_ST_NUM_PAGES);
16134841303SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
16234841303SHeinrich Schuchardt 		efi_st_error("FreePages did not return EFI_SUCCESS\n");
16334841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
16434841303SHeinrich Schuchardt 	}
16534841303SHeinrich Schuchardt 	ret = boottime->free_pool(memory_map);
16634841303SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
16734841303SHeinrich Schuchardt 		efi_st_error("FreePool did not return EFI_SUCCESS\n");
16834841303SHeinrich Schuchardt 		return EFI_ST_FAILURE;
16934841303SHeinrich Schuchardt 	}
17034841303SHeinrich Schuchardt 
171*34c96659SHeinrich Schuchardt 	/* Check memory reservation for the device tree */
172*34c96659SHeinrich Schuchardt 	if (fdt_addr &&
173*34c96659SHeinrich Schuchardt 	    find_in_memory_map(map_size, memory_map, desc_size, fdt_addr,
174*34c96659SHeinrich Schuchardt 			       EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS) {
175*34c96659SHeinrich Schuchardt 		efi_st_error
176*34c96659SHeinrich Schuchardt 			("Device tree not marked as runtime services data\n");
177*34c96659SHeinrich Schuchardt 		return EFI_ST_FAILURE;
178*34c96659SHeinrich Schuchardt 	}
17934841303SHeinrich Schuchardt 	return EFI_ST_SUCCESS;
18034841303SHeinrich Schuchardt }
18134841303SHeinrich Schuchardt 
18234841303SHeinrich Schuchardt EFI_UNIT_TEST(memory) = {
18334841303SHeinrich Schuchardt 	.name = "memory",
18434841303SHeinrich Schuchardt 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
18534841303SHeinrich Schuchardt 	.setup = setup,
18634841303SHeinrich Schuchardt 	.execute = execute,
18734841303SHeinrich Schuchardt };
188