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
17static const efi_guid_t fdt_guid = EFI_FDT_GUID;
18static struct efi_boot_services *boottime;
19static 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 */
28static 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 */
57static 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 */
101static 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
187EFI_UNIT_TEST(memory) = {
188	.name = "memory",
189	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
190	.setup = setup,
191	.execute = execute,
192};
193