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