1 #include <dragonstub/dragonstub.h>
2 #include <dragonstub/linux/math.h>
3 #include <dragonstub/linux/align.h>
4 #include <dragonstub/minmax.h>
5
6 /**
7 * efi_get_memory_map() - get memory map
8 * @map: pointer to memory map pointer to which to assign the
9 * newly allocated memory map
10 * @install_cfg_tbl: whether or not to install the boot memory map as a
11 * configuration table
12 *
13 * Retrieve the UEFI memory map. The allocated memory leaves room for
14 * up to EFI_MMAP_NR_SLACK_SLOTS additional memory map entries.
15 *
16 * Return: status code
17 */
efi_get_memory_map(struct efi_boot_memmap ** map,bool install_cfg_tbl)18 efi_status_t efi_get_memory_map(struct efi_boot_memmap **map,
19 bool install_cfg_tbl)
20 {
21 int memtype = install_cfg_tbl ? EfiACPIReclaimMemory : EfiLoaderData;
22 efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID;
23 struct efi_boot_memmap *m, tmp;
24 efi_status_t status;
25 unsigned long size;
26
27 tmp.map_size = 0;
28 status = efi_bs_call(GetMemoryMap, &tmp.map_size, NULL, &tmp.map_key,
29 &tmp.desc_size, &tmp.desc_ver);
30 if (status != EFI_BUFFER_TOO_SMALL)
31 return EFI_LOAD_ERROR;
32
33 size = tmp.map_size + tmp.desc_size * EFI_MMAP_NR_SLACK_SLOTS;
34 status = efi_bs_call(AllocatePool, memtype, sizeof(*m) + size,
35 (void **)&m);
36 if (status != EFI_SUCCESS)
37 return status;
38
39 if (install_cfg_tbl) {
40 /*
41 * Installing a configuration table might allocate memory, and
42 * this may modify the memory map. This means we should install
43 * the configuration table first, and re-install or delete it
44 * as needed.
45 */
46 status = efi_bs_call(InstallConfigurationTable, &tbl_guid, m);
47 if (status != EFI_SUCCESS)
48 goto free_map;
49 }
50
51 m->buff_size = m->map_size = size;
52 status = efi_bs_call(GetMemoryMap, &m->map_size, m->map, &m->map_key,
53 &m->desc_size, &m->desc_ver);
54 if (status != EFI_SUCCESS)
55 goto uninstall_table;
56
57 *map = m;
58 return EFI_SUCCESS;
59
60 uninstall_table:
61 if (install_cfg_tbl)
62 efi_bs_call(InstallConfigurationTable, &tbl_guid, NULL);
63 free_map:
64 efi_bs_call(FreePool, m);
65 return status;
66 }
67
68 /**
69 * efi_allocate_pages() - Allocate memory pages
70 * @size: minimum number of bytes to allocate
71 * @addr: On return the address of the first allocated page. The first
72 * allocated page has alignment EFI_ALLOC_ALIGN which is an
73 * architecture dependent multiple of the page size.
74 * @max: the address that the last allocated memory page shall not
75 * exceed
76 *
77 * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
78 * to EFI_ALLOC_ALIGN. The last allocated page will not exceed the address
79 * given by @max.
80 *
81 * Return: status code
82 */
efi_allocate_pages(unsigned long size,unsigned long * addr,unsigned long max)83 efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
84 unsigned long max)
85 {
86 efi_physical_addr_t alloc_addr;
87 efi_status_t status;
88
89 max = min(max, EFI_ALLOC_LIMIT);
90
91 // if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE)
92 // return efi_allocate_pages_aligned(
93 // size, addr, max, EFI_ALLOC_ALIGN, EfiLoaderData);
94
95 alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
96 status = efi_bs_call(AllocatePages, EFI_ALLOCATE_MAX_ADDRESS,
97 EfiLoaderData, DIV_ROUND_UP(size, EFI_PAGE_SIZE),
98 &alloc_addr);
99 if (status != EFI_SUCCESS)
100 return status;
101
102 *addr = alloc_addr;
103 return EFI_SUCCESS;
104 }
105
106 /**
107 * efi_allocate_pages_exact() - Allocate memory pages at a specific address
108 * @size: minimum number of bytes to allocate
109 * @addr: The address of the first allocated page.
110 *
111 * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
112 * to EFI_ALLOC_ALIGN.
113 *
114 * Return: status code
115 */
efi_allocate_pages_exact(unsigned long size,unsigned long addr)116 efi_status_t efi_allocate_pages_exact(unsigned long size, unsigned long addr)
117 {
118 efi_status_t status;
119
120 u64 addr_rounded = addr & ~(EFI_ALLOC_ALIGN - 1);
121 size += addr - addr_rounded;
122
123 u32 pagecount = DIV_ROUND_UP(size, EFI_PAGE_SIZE);
124 efi_debug(
125 "efi_allocate_pages_exact: size=%d, addr=%p, addr_rounded=%p, pagecount=%d\n",
126 size, addr, addr_rounded, pagecount);
127
128 status = efi_bs_call(AllocatePages, AllocateAddress, EfiLoaderData,
129 pagecount, (EFI_PHYSICAL_ADDRESS *)&addr);
130
131 // status = efi_bs_call(AllocatePages, AllocateAddress, EfiLoaderData,
132 // DIV_ROUND_UP(size, EFI_PAGE_SIZE),
133 // (EFI_PHYSICAL_ADDRESS *)&addr);
134 if (status != EFI_SUCCESS)
135 return status;
136
137 return EFI_SUCCESS;
138 }
139
140 /**
141 * efi_free() - free memory pages
142 * @size: size of the memory area to free in bytes
143 * @addr: start of the memory area to free (must be EFI_PAGE_SIZE
144 * aligned)
145 *
146 * @size is rounded up to a multiple of EFI_ALLOC_ALIGN which is an
147 * architecture specific multiple of EFI_PAGE_SIZE. So this function should
148 * only be used to return pages allocated with efi_allocate_pages() or
149 * efi_low_alloc_above().
150 */
efi_free(unsigned long size,unsigned long addr)151 void efi_free(unsigned long size, unsigned long addr)
152 {
153 unsigned long nr_pages;
154
155 if (!size)
156 return;
157
158 nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
159 efi_bs_call(FreePages, addr, nr_pages);
160 }
161