efi_selftest: sharpen ConvertPointer() test
[platform/kernel/u-boot.git] / lib / efi_selftest / efi_selftest_set_virtual_address_map.c
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  */
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  */
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  */
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 };