1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * efi_selftest_set_virtual_address_map.c
4  *
5  * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
6  *
7  * This test checks the notification of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
8  * and the following services: SetVirtualAddressMap, ConvertPointer.
9  */
10 
11 #include <efi_selftest.h>
12 
13 static const struct efi_boot_services *boottime;
14 static const struct efi_runtime_services *runtime;
15 static struct efi_event *event;
16 static struct efi_mem_desc *memory_map;
17 static efi_uintn_t map_size;
18 static efi_uintn_t desc_size;
19 static u32 desc_version;
20 static u64 page1;
21 static u64 page2;
22 static u32 notify_call_count;
23 static bool convert_pointer_failed;
24 
25 /**
26  * notify() - notification function
27  *
28  * This function is called when the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event
29  * occurs. The correct output of ConvertPointer() is checked.
30  *
31  * @event	notified event
32  * @context	pointer to the notification count
33  */
notify(struct efi_event * event,void * context)34 static void EFIAPI notify(struct efi_event *event, void *context)
35 {
36 	void *addr;
37 	efi_status_t ret;
38 
39 	++notify_call_count;
40 
41 	addr = (void *)(uintptr_t)page1;
42 	ret = runtime->convert_pointer(0, &addr);
43 	if (ret != EFI_SUCCESS) {
44 		efi_st_error("ConvertPointer failed\n");
45 		convert_pointer_failed = true;
46 		return;
47 	}
48 	if ((uintptr_t)addr != page1 + EFI_PAGE_SIZE) {
49 		efi_st_error("ConvertPointer wrong address\n");
50 		convert_pointer_failed = true;
51 		return;
52 	}
53 
54 	addr = (void *)(uintptr_t)page2;
55 	ret = runtime->convert_pointer(0, &addr);
56 	if (ret != EFI_SUCCESS) {
57 		efi_st_error("ConvertPointer failed\n");
58 		convert_pointer_failed = true;
59 		return;
60 	}
61 	if ((uintptr_t)addr != page2 + 2 * EFI_PAGE_SIZE) {
62 		efi_st_error("ConvertPointer wrong address\n");
63 		convert_pointer_failed = true;
64 	}
65 }
66 
67 /**
68  * setup() - setup unit test
69  *
70  * The memory map is read. Boottime only entries are deleted. Two entries for
71  * newly allocated pages are added. For these virtual addresses deviating from
72  * the physical addresses are set.
73  *
74  * @handle:	handle of the loaded image
75  * @systable:	system table
76  * @return:	EFI_ST_SUCCESS for success
77  */
setup(const efi_handle_t handle,const struct efi_system_table * systable)78 static int setup(const efi_handle_t handle,
79 		 const struct efi_system_table *systable)
80 {
81 	efi_uintn_t map_key;
82 	efi_status_t ret;
83 	struct efi_mem_desc *end, *pos1, *pos2;
84 
85 	boottime = systable->boottime;
86 	runtime = systable->runtime;
87 
88 	ret = boottime->create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
89 				     TPL_CALLBACK, notify, NULL,
90 				     &event);
91 	if (ret != EFI_SUCCESS) {
92 		efi_st_error("could not create event\n");
93 		return EFI_ST_FAILURE;
94 	}
95 
96 	ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
97 				       &desc_version);
98 	if (ret != EFI_BUFFER_TOO_SMALL) {
99 		efi_st_error(
100 			"GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
101 		return EFI_ST_FAILURE;
102 	}
103 	/* Allocate extra space for newly allocated memory */
104 	map_size += 3 * sizeof(struct efi_mem_desc);
105 	ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
106 				      (void **)&memory_map);
107 	if (ret != EFI_SUCCESS) {
108 		efi_st_error("AllocatePool failed\n");
109 		return EFI_ST_FAILURE;
110 	}
111 	ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
112 				       &desc_size, &desc_version);
113 	if (ret != EFI_SUCCESS) {
114 		efi_st_error("GetMemoryMap failed\n");
115 		return EFI_ST_FAILURE;
116 	}
117 	ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
118 				       EFI_BOOT_SERVICES_DATA, 2, &page1);
119 	if (ret != EFI_SUCCESS) {
120 		efi_st_error("AllocatePages failed\n");
121 		return EFI_ST_FAILURE;
122 	}
123 	ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
124 				       EFI_BOOT_SERVICES_DATA, 3, &page2);
125 	if (ret != EFI_SUCCESS) {
126 		efi_st_error("AllocatePages failed\n");
127 		return EFI_ST_FAILURE;
128 	}
129 	/* Remove entries not relevant for runtime from map */
130 	end = (struct efi_mem_desc *)((u8 *)memory_map + map_size);
131 	for (pos1 = memory_map, pos2 = memory_map;
132 	     pos2 < end; ++pos2) {
133 		switch (pos2->type) {
134 		case EFI_LOADER_CODE:
135 		case EFI_LOADER_DATA:
136 		case EFI_BOOT_SERVICES_CODE:
137 		case EFI_BOOT_SERVICES_DATA:
138 		case EFI_CONVENTIONAL_MEMORY:
139 			continue;
140 		}
141 		memcpy(pos1, pos2, desc_size);
142 		++pos1;
143 	}
144 
145 	/*
146 	 * Add entries with virtual addresses deviating from the physical
147 	 * addresses. By choosing virtual address ranges within the allocated
148 	 * physical pages address space collisions are avoided.
149 	 */
150 	pos1->type = EFI_RUNTIME_SERVICES_DATA;
151 	pos1->reserved = 0;
152 	pos1->physical_start = page1;
153 	pos1->virtual_start = page1 + EFI_PAGE_SIZE;
154 	pos1->num_pages = 1;
155 	pos1->attribute = EFI_MEMORY_RUNTIME;
156 	++pos1;
157 
158 	pos1->type = EFI_RUNTIME_SERVICES_DATA;
159 	pos1->reserved = 0;
160 	pos1->physical_start = page2;
161 	pos1->virtual_start = page2 + 2 * EFI_PAGE_SIZE;
162 	pos1->num_pages = 1;
163 	pos1->attribute = EFI_MEMORY_RUNTIME;
164 	++pos1;
165 
166 	map_size = (u8 *)pos1 - (u8 *)memory_map;
167 
168 	return EFI_ST_SUCCESS;
169 }
170 
171 /**
172  * execute() - execute unit test
173  *
174  * SetVirtualAddressMap() is called with the memory map prepared in setup().
175  *
176  * The triggering of the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event is checked via
177  * the call count of the notification function.
178  *
179  * @return:	EFI_ST_SUCCESS for success
180  */
execute(void)181 static int execute(void)
182 {
183 	efi_status_t ret;
184 
185 	ret = runtime->set_virtual_address_map(map_size, desc_size,
186 					       desc_version, memory_map);
187 	if (ret != EFI_SUCCESS) {
188 		efi_st_error("SetVirtualAddressMap failed\n");
189 		return EFI_ST_FAILURE;
190 	}
191 	if (notify_call_count != 1) {
192 		efi_st_error("EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE triggered %d times\n",
193 			     notify_call_count);
194 		return EFI_ST_FAILURE;
195 	}
196 	if (convert_pointer_failed)
197 		return EFI_ST_FAILURE;
198 
199 	return EFI_ST_SUCCESS;
200 }
201 
202 EFI_UNIT_TEST(virtaddrmap) = {
203 	.name = "virtual address map",
204 	.phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
205 	.setup = setup,
206 	.execute = execute,
207 };
208