xref: /DragonStub/apps/mem.c (revision 823f04931913f01ee1fc0dc0c7876156ad150388)
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