efi_selftest_set_virtual_address_map.c 5.6 KB

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