xref: /DragonStub/apps/stub.c (revision 823f04931913f01ee1fc0dc0c7876156ad150388)
1 #include "dragonstub/printk.h"
2 #include "efidef.h"
3 #include <efi.h>
4 #include <efilib.h>
5 #include <elf.h>
6 #include <dragonstub/dragonstub.h>
7 #include <dragonstub/elfloader.h>
8 #include <dragonstub/linux/math.h>
9 #include <dragonstub/linux/align.h>
10 
11 /*
12  * This is the base address at which to start allocating virtual memory ranges
13  * for UEFI Runtime Services.
14  *
15  * For ARM/ARM64:
16  * This is in the low TTBR0 range so that we can use
17  * any allocation we choose, and eliminate the risk of a conflict after kexec.
18  * The value chosen is the largest non-zero power of 2 suitable for this purpose
19  * both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can
20  * be mapped efficiently.
21  * Since 32-bit ARM could potentially execute with a 1G/3G user/kernel split,
22  * map everything below 1 GB. (512 MB is a reasonable upper bound for the
23  * entire footprint of the UEFI runtime services memory regions)
24  *
25  * For RISC-V:
26  * There is no specific reason for which, this address (512MB) can't be used
27  * EFI runtime virtual address for RISC-V. It also helps to use EFI runtime
28  * services on both RV32/RV64. Keep the same runtime virtual address for RISC-V
29  * as well to minimize the code churn.
30  */
31 #define EFI_RT_VIRTUAL_BASE SZ_512M
32 
33 /*
34  * Some architectures map the EFI regions into the kernel's linear map using a
35  * fixed offset.
36  */
37 #ifndef EFI_RT_VIRTUAL_OFFSET
38 #define EFI_RT_VIRTUAL_OFFSET 0
39 #endif
40 
41 extern void _image_end(void);
42 
43 static u64 image_base = 0;
44 static u64 image_size = 0;
45 static u64 image_end = 0;
46 
47 static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
48 static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0);
49 
efi_handle_cmdline(EFI_LOADED_IMAGE * image,char ** cmdline_ptr)50 EFI_STATUS efi_handle_cmdline(EFI_LOADED_IMAGE *image, char **cmdline_ptr)
51 {
52 	int cmdline_size = 0;
53 	EFI_STATUS status;
54 	char *cmdline;
55 
56 	/*
57 	 * Get the command line from EFI, using the LOADED_IMAGE
58 	 * protocol. We are going to copy the command line into the
59 	 * device tree, so this can be allocated anywhere.
60 	 */
61 	cmdline = efi_convert_cmdline(image, &cmdline_size);
62 	if (!cmdline) {
63 		efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
64 		return EFI_OUT_OF_RESOURCES;
65 	}
66 
67 	// 	if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) ||
68 	// 	    IS_ENABLED(CONFIG_CMDLINE_FORCE) ||
69 	// 	    cmdline_size == 0) {
70 	// 		status = efi_parse_options(CONFIG_CMDLINE);
71 	// 		if (status != EFI_SUCCESS) {
72 	// 			efi_err("Failed to parse options\n");
73 	// 			goto fail_free_cmdline;
74 	// 		}
75 	// 	}
76 
77 	// if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) {
78 	if (cmdline_size > 0) {
79 		status = efi_parse_options(cmdline);
80 		if (status != EFI_SUCCESS) {
81 			efi_err("Failed to parse options\n");
82 			goto fail_free_cmdline;
83 		}
84 	}
85 
86 	*cmdline_ptr = cmdline;
87 	return EFI_SUCCESS;
88 
89 fail_free_cmdline:
90 	efi_bs_call(FreePool, cmdline_ptr);
91 	return status;
92 }
93 
init_efi_program_info(efi_loaded_image_t * loaded_image)94 static efi_status_t init_efi_program_info(efi_loaded_image_t *loaded_image)
95 {
96 	image_base = (u64)loaded_image->ImageBase;
97 	image_size = loaded_image->ImageSize;
98 	image_end = (u64)_image_end;
99 	efi_info("DragonStub loaded at 0x%p\n", image_base);
100 	efi_info("DragonStub + payload size: 0x%p\n", image_size);
101 	efi_info("DragonStub end addr: 0x%p\n", image_end);
102 	return EFI_SUCCESS;
103 }
104 
105 /// @brief payload_info的构造函数
payload_info_new(u64 payload_addr,u64 payload_size)106 static struct payload_info payload_info_new(u64 payload_addr, u64 payload_size)
107 {
108 	struct payload_info info = { .payload_addr = payload_addr,
109 				     .payload_size = payload_size,
110 				     .loaded_paddr = 0,
111 				     .loaded_size = 0,
112 				     .kernel_entry = 0 };
113 	return info;
114 }
find_elf(struct payload_info * info)115 static efi_status_t find_elf(struct payload_info *info)
116 {
117 	extern __weak void _binary_payload_start(void);
118 	extern __weak void _binary_payload_end(void);
119 	extern __weak void _binary_payload_size(void);
120 	u64 payload_start = (u64)_binary_payload_start;
121 	u64 payload_end = (u64)_binary_payload_end;
122 
123 	u64 payload_size = payload_end - payload_start;
124 
125 	efi_info("payload_addr: %p\n", payload_start);
126 	efi_info("payload_end: %p\n", payload_end);
127 	efi_info("payload_size: %p\n", payload_size);
128 
129 	if (payload_start == 0 || payload_end <= payload_start + 4 ||
130 	    payload_size == 0) {
131 		return EFI_NOT_FOUND;
132 	}
133 
134 	efi_info("Checking payload's ELF header...\n");
135 	bool found = elf_check((void *)payload_start, payload_size);
136 
137 	if (found) {
138 		info->payload_addr = payload_start;
139 		info->payload_size = payload_size;
140 		efi_info("Found payload ELF header\n");
141 		return EFI_SUCCESS;
142 	}
143 
144 	return EFI_NOT_FOUND;
145 }
146 
147 /// @brief 寻找要加载的内核负载
148 /// @param handle efi_handle
149 /// @param image efi_loaded_image_t
150 /// @param ret_info 返回的负载信息
151 /// @return
find_payload(efi_handle_t handle,efi_loaded_image_t * loaded_image,struct payload_info * ret_info)152 efi_status_t find_payload(efi_handle_t handle, efi_loaded_image_t *loaded_image,
153 			  struct payload_info *ret_info)
154 {
155 	efi_info("Try to find payload to boot\n");
156 	efi_status_t status = init_efi_program_info(loaded_image);
157 	if (status != EFI_SUCCESS) {
158 		efi_err("Failed to init efi program info\n");
159 		return status;
160 	}
161 
162 	struct payload_info info = payload_info_new(0, 0);
163 
164 	status = find_elf(&info);
165 	if (status != EFI_SUCCESS) {
166 		efi_err("Payload not found: Did you forget to add the payload by setting PAYLOAD_ELF at compile time?\n"
167 			"Or the payload is not an ELF file?\n");
168 		return status;
169 	}
170 
171 	*ret_info = info;
172 	return EFI_SUCCESS;
173 }
174 
175 /*
176  * efi_allocate_virtmap() - create a pool allocation for the virtmap
177  *
178  * Create an allocation that is of sufficient size to hold all the memory
179  * descriptors that will be passed to SetVirtualAddressMap() to inform the
180  * firmware about the virtual mapping that will be used under the OS to call
181  * into the firmware.
182  */
efi_alloc_virtmap(efi_memory_desc_t ** virtmap,unsigned long * desc_size,u32 * desc_ver)183 efi_status_t efi_alloc_virtmap(efi_memory_desc_t **virtmap,
184 			       unsigned long *desc_size, u32 *desc_ver)
185 {
186 	unsigned long size, mmap_key;
187 	efi_status_t status;
188 
189 	/*
190 	 * Use the size of the current memory map as an upper bound for the
191 	 * size of the buffer we need to pass to SetVirtualAddressMap() to
192 	 * cover all EFI_MEMORY_RUNTIME regions.
193 	 */
194 	size = 0;
195 	status = efi_bs_call(GetMemoryMap, &size, NULL, &mmap_key, desc_size,
196 			     desc_ver);
197 	if (status != EFI_BUFFER_TOO_SMALL)
198 		return EFI_LOAD_ERROR;
199 
200 	return efi_bs_call(AllocatePool, EfiLoaderData, size, (void **)virtmap);
201 }
202 
203 /*
204  * efi_get_virtmap() - create a virtual mapping for the EFI memory map
205  *
206  * This function populates the virt_addr fields of all memory region descriptors
207  * in @memory_map whose EFI_MEMORY_RUNTIME attribute is set. Those descriptors
208  * are also copied to @runtime_map, and their total count is returned in @count.
209  */
efi_get_virtmap(efi_memory_desc_t * memory_map,unsigned long map_size,unsigned long desc_size,efi_memory_desc_t * runtime_map,int * count)210 void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
211 		     unsigned long desc_size, efi_memory_desc_t *runtime_map,
212 		     int *count)
213 {
214 	u64 efi_virt_base = virtmap_base;
215 	efi_memory_desc_t *in, *out = runtime_map;
216 	int l;
217 
218 	*count = 0;
219 
220 	for (l = 0; l < map_size; l += desc_size) {
221 		u64 paddr, size;
222 
223 		in = (void *)memory_map + l;
224 		if (!(in->Attribute & EFI_MEMORY_RUNTIME))
225 			continue;
226 
227 		paddr = in->PhysicalStart;
228 		size = in->NumberOfPages * EFI_PAGE_SIZE;
229 
230 		in->VirtualStart = in->PhysicalStart + EFI_RT_VIRTUAL_OFFSET;
231 		if (efi_novamap) {
232 			continue;
233 		}
234 
235 		/*
236 		 * Make the mapping compatible with 64k pages: this allows
237 		 * a 4k page size kernel to kexec a 64k page size kernel and
238 		 * vice versa.
239 		 */
240 		if (!flat_va_mapping) {
241 			paddr = round_down(in->PhysicalStart, SZ_64K);
242 			size += in->PhysicalStart - paddr;
243 
244 			/*
245 			 * Avoid wasting memory on PTEs by choosing a virtual
246 			 * base that is compatible with section mappings if this
247 			 * region has the appropriate size and physical
248 			 * alignment. (Sections are 2 MB on 4k granule kernels)
249 			 */
250 			if (IS_ALIGNED(in->PhysicalStart, SZ_2M) &&
251 			    size >= SZ_2M)
252 				efi_virt_base = round_up(efi_virt_base, SZ_2M);
253 			else
254 				efi_virt_base = round_up(efi_virt_base, SZ_64K);
255 
256 			in->VirtualStart += efi_virt_base - paddr;
257 			efi_virt_base += size;
258 		}
259 
260 		memcpy(out, in, desc_size);
261 		out = (void *)out + desc_size;
262 		++*count;
263 	}
264 }
265 
266 /// @brief 设置内存保留表
267 /// @param
install_memreserve_table(void)268 static void install_memreserve_table(void)
269 {
270 	struct linux_efi_memreserve *rsv;
271 	efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID;
272 	efi_status_t status;
273 
274 	status = efi_bs_call(AllocatePool, EfiLoaderData, sizeof(*rsv),
275 			     (void **)&rsv);
276 	if (status != EFI_SUCCESS) {
277 		efi_err("Failed to allocate memreserve entry!\n");
278 		return;
279 	}
280 
281 	rsv->next = 0;
282 	rsv->size = 0;
283 	rsv->count = 0;
284 
285 	status = efi_bs_call(InstallConfigurationTable, &memreserve_table_guid,
286 			     rsv);
287 	if (status != EFI_SUCCESS)
288 		efi_err("Failed to install memreserve config table!\n");
289 }
290 
get_supported_rt_services(void)291 static u32 get_supported_rt_services(void)
292 {
293 	const efi_rt_properties_table_t *rt_prop_table;
294 	u32 supported = EFI_RT_SUPPORTED_ALL;
295 
296 	rt_prop_table = get_efi_config_table(EFI_RT_PROPERTIES_TABLE_GUID);
297 	if (rt_prop_table)
298 		supported &= rt_prop_table->runtime_services_supported;
299 
300 	return supported;
301 }
302 
efi_stub_common(efi_handle_t handle,efi_loaded_image_t * loaded_image,struct payload_info * payload_info,char * cmdline_ptr)303 efi_status_t efi_stub_common(efi_handle_t handle,
304 			     efi_loaded_image_t *loaded_image,
305 			     struct payload_info *payload_info,
306 			     char *cmdline_ptr)
307 {
308 	struct screen_info *si;
309 	efi_status_t status;
310 
311 	status = check_platform_features();
312 	if (status != EFI_SUCCESS)
313 		return status;
314 
315 	// si = setup_graphics();
316 
317 	// efi_retrieve_tpm2_eventlog();
318 
319 	// /* Ask the firmware to clear memory on unclean shutdown */
320 	// efi_enable_reset_attack_mitigation();
321 
322 	// efi_load_initrd(image, ULONG_MAX, efi_get_max_initrd_addr(image_addr),
323 	// 		NULL);
324 
325 	// efi_random_get_seed();
326 
327 	/* force efi_novamap if SetVirtualAddressMap() is unsupported */
328 	efi_novamap |= !(get_supported_rt_services() &
329 			 EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP);
330 
331 	install_memreserve_table();
332 	efi_info("Memreserve table installed\n");
333 	efi_info("Booting DragonOS kernel...\n");
334 	status = efi_boot_kernel(handle, loaded_image, payload_info,
335 				 cmdline_ptr);
336 
337 	// free_screen_info(si);
338 	return status;
339 }
340