1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * efi_selftest_memory
4  *
5  * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
6  *
7  * This unit test checks the following boottime services:
8  * AllocatePages, FreePages, GetMemoryMap
9  *
10  * The memory type used for the device tree is checked.
11  */
12 
13 #include <efi_selftest.h>
14 
15 #define EFI_ST_NUM_PAGES 8
16 
17 static const efi_guid_t fdt_guid = EFI_FDT_GUID;
18 static struct efi_boot_services *boottime;
19 static u64 fdt_addr;
20 
21 /**
22  * setup() - setup unit test
23  *
24  * @handle:	handle of the loaded image
25  * @systable:	system table
26  * Return:	EFI_ST_SUCCESS for success
27  */
setup(const efi_handle_t handle,const struct efi_system_table * systable)28 static int setup(const efi_handle_t handle,
29 		 const struct efi_system_table *systable)
30 {
31 	size_t i;
32 
33 	boottime = systable->boottime;
34 
35 	for (i = 0; i < systable->nr_tables; ++i) {
36 		if (!memcmp(&systable->tables[i].guid, &fdt_guid,
37 			    sizeof(efi_guid_t))) {
38 			if (fdt_addr) {
39 				efi_st_error("Duplicate device tree\n");
40 				return EFI_ST_FAILURE;
41 			}
42 			fdt_addr = (uintptr_t)systable->tables[i].table;
43 		}
44 	}
45 	return EFI_ST_SUCCESS;
46 }
47 
48 /**
49  * find_in_memory_map() - check matching memory map entry exists
50  *
51  * @memory_map:		memory map
52  * @desc_size:		number of memory map entries
53  * @addr:		physical address to find in the map
54  * @type:		expected memory type
55  * Return:		EFI_ST_SUCCESS for success
56  */
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)57 static int find_in_memory_map(efi_uintn_t map_size,
58 			      struct efi_mem_desc *memory_map,
59 			      efi_uintn_t desc_size,
60 			      u64 addr, int memory_type)
61 {
62 	efi_uintn_t i;
63 	bool found = false;
64 
65 	for (i = 0; map_size; ++i, map_size -= desc_size) {
66 		struct efi_mem_desc *entry = &memory_map[i];
67 
68 		if (entry->physical_start != entry->virtual_start) {
69 			efi_st_error("Physical and virtual addresses do not match\n");
70 			return EFI_ST_FAILURE;
71 		}
72 
73 		if (addr >= entry->physical_start &&
74 		    addr < entry->physical_start +
75 			    (entry->num_pages << EFI_PAGE_SHIFT)) {
76 			if (found) {
77 				efi_st_error("Duplicate memory map entry\n");
78 				return EFI_ST_FAILURE;
79 			}
80 			found = true;
81 			if (memory_type != entry->type) {
82 				efi_st_error
83 					("Wrong memory type %d, expected %d\n",
84 					 entry->type, memory_type);
85 				return EFI_ST_FAILURE;
86 			}
87 		}
88 	}
89 	if (!found) {
90 		efi_st_error("Missing memory map entry\n");
91 		return EFI_ST_FAILURE;
92 	}
93 	return EFI_ST_SUCCESS;
94 }
95 
96 /*
97  * execute() - execute unit test
98  *
99  * Return:	EFI_ST_SUCCESS for success
100  */
execute(void)101 static int execute(void)
102 {
103 	u64 p1;
104 	u64 p2;
105 	efi_uintn_t map_size = 0;
106 	efi_uintn_t map_key;
107 	efi_uintn_t desc_size;
108 	u32 desc_version;
109 	struct efi_mem_desc *memory_map;
110 	efi_status_t ret;
111 
112 	/* Allocate two page ranges with different memory type */
113 	ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
114 				       EFI_RUNTIME_SERVICES_CODE,
115 				       EFI_ST_NUM_PAGES, &p1);
116 	if (ret != EFI_SUCCESS) {
117 		efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
118 		return EFI_ST_FAILURE;
119 	}
120 	ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
121 				       EFI_RUNTIME_SERVICES_DATA,
122 				       EFI_ST_NUM_PAGES, &p2);
123 	if (ret != EFI_SUCCESS) {
124 		efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
125 		return EFI_ST_FAILURE;
126 	}
127 
128 	/* Load memory map */
129 	ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
130 				       &desc_version);
131 	if (ret != EFI_BUFFER_TOO_SMALL) {
132 		efi_st_error
133 			("GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
134 		return EFI_ST_FAILURE;
135 	}
136 	/* Allocate extra space for newly allocated memory */
137 	map_size += sizeof(struct efi_mem_desc);
138 	ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
139 				      (void **)&memory_map);
140 	if (ret != EFI_SUCCESS) {
141 		efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
142 		return EFI_ST_FAILURE;
143 	}
144 	ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
145 				       &desc_size, &desc_version);
146 	if (ret != EFI_SUCCESS) {
147 		efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
148 		return EFI_ST_FAILURE;
149 	}
150 
151 	/* Check memory map entries */
152 	if (find_in_memory_map(map_size, memory_map, desc_size, p1,
153 			       EFI_RUNTIME_SERVICES_CODE) != EFI_ST_SUCCESS)
154 		return EFI_ST_FAILURE;
155 	if (find_in_memory_map(map_size, memory_map, desc_size, p2,
156 			       EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS)
157 		return EFI_ST_FAILURE;
158 
159 	/* Free memory */
160 	ret = boottime->free_pages(p1, EFI_ST_NUM_PAGES);
161 	if (ret != EFI_SUCCESS) {
162 		efi_st_error("FreePages did not return EFI_SUCCESS\n");
163 		return EFI_ST_FAILURE;
164 	}
165 	ret = boottime->free_pages(p2, EFI_ST_NUM_PAGES);
166 	if (ret != EFI_SUCCESS) {
167 		efi_st_error("FreePages did not return EFI_SUCCESS\n");
168 		return EFI_ST_FAILURE;
169 	}
170 	ret = boottime->free_pool(memory_map);
171 	if (ret != EFI_SUCCESS) {
172 		efi_st_error("FreePool did not return EFI_SUCCESS\n");
173 		return EFI_ST_FAILURE;
174 	}
175 
176 	/* Check memory reservation for the device tree */
177 	if (fdt_addr &&
178 	    find_in_memory_map(map_size, memory_map, desc_size, fdt_addr,
179 			       EFI_ACPI_RECLAIM_MEMORY) != EFI_ST_SUCCESS) {
180 		efi_st_error
181 			("Device tree not marked as ACPI reclaim memory\n");
182 		return EFI_ST_FAILURE;
183 	}
184 	return EFI_ST_SUCCESS;
185 }
186 
187 EFI_UNIT_TEST(memory) = {
188 	.name = "memory",
189 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
190 	.setup = setup,
191 	.execute = execute,
192 };
193