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 runtime 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  */
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 (!efi_st_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  */
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 (addr >= entry->physical_start &&
69 		    addr < entry->physical_start +
70 			    (entry->num_pages << EFI_PAGE_SHIFT)) {
71 			if (found) {
72 				efi_st_error("Duplicate memory map entry\n");
73 				return EFI_ST_FAILURE;
74 			}
75 			found = true;
76 			if (memory_type != entry->type) {
77 				efi_st_error
78 					("Wrong memory type %d, expected %d\n",
79 					 entry->type, memory_type);
80 				return EFI_ST_FAILURE;
81 			}
82 		}
83 	}
84 	if (!found) {
85 		efi_st_error("Missing memory map entry\n");
86 		return EFI_ST_FAILURE;
87 	}
88 	return EFI_ST_SUCCESS;
89 }
90 
91 /*
92  * execute() - execute unit test
93  *
94  * Return:	EFI_ST_SUCCESS for success
95  */
96 static int execute(void)
97 {
98 	u64 p1;
99 	u64 p2;
100 	efi_uintn_t map_size = 0;
101 	efi_uintn_t map_key;
102 	efi_uintn_t desc_size;
103 	u32 desc_version;
104 	struct efi_mem_desc *memory_map;
105 	efi_status_t ret;
106 
107 	/* Allocate two page ranges with different memory type */
108 	ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
109 				       EFI_RUNTIME_SERVICES_CODE,
110 				       EFI_ST_NUM_PAGES, &p1);
111 	if (ret != EFI_SUCCESS) {
112 		efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
113 		return EFI_ST_FAILURE;
114 	}
115 	ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
116 				       EFI_RUNTIME_SERVICES_DATA,
117 				       EFI_ST_NUM_PAGES, &p2);
118 	if (ret != EFI_SUCCESS) {
119 		efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
120 		return EFI_ST_FAILURE;
121 	}
122 
123 	/* Load memory map */
124 	ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
125 				       &desc_version);
126 	if (ret != EFI_BUFFER_TOO_SMALL) {
127 		efi_st_error
128 			("GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
129 		return EFI_ST_FAILURE;
130 	}
131 	/* Allocate extra space for newly allocated memory */
132 	map_size += sizeof(struct efi_mem_desc);
133 	ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
134 				      (void **)&memory_map);
135 	if (ret != EFI_SUCCESS) {
136 		efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
137 		return EFI_ST_FAILURE;
138 	}
139 	ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
140 				       &desc_size, &desc_version);
141 	if (ret != EFI_SUCCESS) {
142 		efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
143 		return EFI_ST_FAILURE;
144 	}
145 
146 	/* Check memory map entries */
147 	if (find_in_memory_map(map_size, memory_map, desc_size, p1,
148 			       EFI_RUNTIME_SERVICES_CODE) != EFI_ST_SUCCESS)
149 		return EFI_ST_FAILURE;
150 	if (find_in_memory_map(map_size, memory_map, desc_size, p2,
151 			       EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS)
152 		return EFI_ST_FAILURE;
153 
154 	/* Free memory */
155 	ret = boottime->free_pages(p1, EFI_ST_NUM_PAGES);
156 	if (ret != EFI_SUCCESS) {
157 		efi_st_error("FreePages did not return EFI_SUCCESS\n");
158 		return EFI_ST_FAILURE;
159 	}
160 	ret = boottime->free_pages(p2, 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_pool(memory_map);
166 	if (ret != EFI_SUCCESS) {
167 		efi_st_error("FreePool did not return EFI_SUCCESS\n");
168 		return EFI_ST_FAILURE;
169 	}
170 
171 	/* Check memory reservation for the device tree */
172 	if (fdt_addr &&
173 	    find_in_memory_map(map_size, memory_map, desc_size, fdt_addr,
174 			       EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS) {
175 		efi_st_error
176 			("Device tree not marked as runtime services data\n");
177 		return EFI_ST_FAILURE;
178 	}
179 	return EFI_ST_SUCCESS;
180 }
181 
182 EFI_UNIT_TEST(memory) = {
183 	.name = "memory",
184 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
185 	.setup = setup,
186 	.execute = execute,
187 };
188