xref: /freebsd/stand/efi/loader/arch/amd64/multiboot2.c (revision adda2797eb2a29487fe26640a9c990fea7e6585d)
1*adda2797SRoger Pau Monné /*-
2*adda2797SRoger Pau Monné  * Copyright (c) 2021 Roger Pau Monné <royger@FreeBSD.org>
3*adda2797SRoger Pau Monné  * All rights reserved.
4*adda2797SRoger Pau Monné  *
5*adda2797SRoger Pau Monné  * Redistribution and use in source and binary forms, with or without
6*adda2797SRoger Pau Monné  * modification, are permitted provided that the following conditions
7*adda2797SRoger Pau Monné  * are met:
8*adda2797SRoger Pau Monné  * 1. Redistributions of source code must retain the above copyright
9*adda2797SRoger Pau Monné  *    notice, this list of conditions and the following disclaimer.
10*adda2797SRoger Pau Monné  * 2. Redistributions in binary form must reproduce the above copyright
11*adda2797SRoger Pau Monné  *    notice, this list of conditions and the following disclaimer in the
12*adda2797SRoger Pau Monné  *    documentation and/or other materials provided with the distribution.
13*adda2797SRoger Pau Monné  *
14*adda2797SRoger Pau Monné  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*adda2797SRoger Pau Monné  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*adda2797SRoger Pau Monné  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*adda2797SRoger Pau Monné  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*adda2797SRoger Pau Monné  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*adda2797SRoger Pau Monné  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*adda2797SRoger Pau Monné  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*adda2797SRoger Pau Monné  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*adda2797SRoger Pau Monné  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*adda2797SRoger Pau Monné  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*adda2797SRoger Pau Monné  * SUCH DAMAGE.
25*adda2797SRoger Pau Monné  */
26*adda2797SRoger Pau Monné 
27*adda2797SRoger Pau Monné /*
28*adda2797SRoger Pau Monné  * This multiboot2 implementation only implements a subset of the full
29*adda2797SRoger Pau Monné  * multiboot2 specification in order to be able to boot Xen and a
30*adda2797SRoger Pau Monné  * FreeBSD Dom0. Trying to use it to boot other multiboot2 compliant
31*adda2797SRoger Pau Monné  * kernels will most surely fail.
32*adda2797SRoger Pau Monné  *
33*adda2797SRoger Pau Monné  * The full multiboot specification can be found here:
34*adda2797SRoger Pau Monné  * https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html
35*adda2797SRoger Pau Monné  */
36*adda2797SRoger Pau Monné 
37*adda2797SRoger Pau Monné #include <sys/cdefs.h>
38*adda2797SRoger Pau Monné 
39*adda2797SRoger Pau Monné #include <sys/param.h>
40*adda2797SRoger Pau Monné #include <sys/exec.h>
41*adda2797SRoger Pau Monné #include <sys/linker.h>
42*adda2797SRoger Pau Monné #include <sys/module.h>
43*adda2797SRoger Pau Monné #include <sys/stdint.h>
44*adda2797SRoger Pau Monné #define _MACHINE_ELF_WANT_32BIT
45*adda2797SRoger Pau Monné #include <machine/elf.h>
46*adda2797SRoger Pau Monné #include <machine/metadata.h>
47*adda2797SRoger Pau Monné #include <string.h>
48*adda2797SRoger Pau Monné #include <stand.h>
49*adda2797SRoger Pau Monné 
50*adda2797SRoger Pau Monné #include <efi.h>
51*adda2797SRoger Pau Monné #include <efilib.h>
52*adda2797SRoger Pau Monné 
53*adda2797SRoger Pau Monné #include "bootstrap.h"
54*adda2797SRoger Pau Monné #include "multiboot2.h"
55*adda2797SRoger Pau Monné #include "loader_efi.h"
56*adda2797SRoger Pau Monné 
57*adda2797SRoger Pau Monné extern int elf32_loadfile_raw(char *filename, uint64_t dest,
58*adda2797SRoger Pau Monné     struct preloaded_file **result, int multiboot);
59*adda2797SRoger Pau Monné extern int elf64_load_modmetadata(struct preloaded_file *fp, uint64_t dest);
60*adda2797SRoger Pau Monné extern int elf64_obj_loadfile(char *filename, uint64_t dest,
61*adda2797SRoger Pau Monné     struct preloaded_file **result);
62*adda2797SRoger Pau Monné extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp,
63*adda2797SRoger Pau Monné     bool exit_bs);
64*adda2797SRoger Pau Monné 
65*adda2797SRoger Pau Monné extern void multiboot2_exec(void *entry, uint64_t multiboot_info,
66*adda2797SRoger Pau Monné     uint64_t stack);
67*adda2797SRoger Pau Monné 
68*adda2797SRoger Pau Monné /*
69*adda2797SRoger Pau Monné  * Multiboot2 header information to pass between the loading and the exec
70*adda2797SRoger Pau Monné  * functions.
71*adda2797SRoger Pau Monné  */
72*adda2797SRoger Pau Monné struct mb2hdr {
73*adda2797SRoger Pau Monné 	uint32_t efi64_entry;
74*adda2797SRoger Pau Monné };
75*adda2797SRoger Pau Monné 
76*adda2797SRoger Pau Monné static int
77*adda2797SRoger Pau Monné loadfile(char *filename, uint64_t dest, struct preloaded_file **result)
78*adda2797SRoger Pau Monné {
79*adda2797SRoger Pau Monné 	unsigned int		 i;
80*adda2797SRoger Pau Monné 	int			 error, fd;
81*adda2797SRoger Pau Monné 	void			*header_search = NULL;
82*adda2797SRoger Pau Monné 	void			*multiboot = NULL;
83*adda2797SRoger Pau Monné 	ssize_t			 search_size;
84*adda2797SRoger Pau Monné 	struct multiboot_header	*header;
85*adda2797SRoger Pau Monné 	char			*cmdline;
86*adda2797SRoger Pau Monné 	struct mb2hdr		 hdr;
87*adda2797SRoger Pau Monné 	bool			 keep_bs = false;
88*adda2797SRoger Pau Monné 
89*adda2797SRoger Pau Monné 	/*
90*adda2797SRoger Pau Monné 	 * Read MULTIBOOT_SEARCH size in order to search for the
91*adda2797SRoger Pau Monné 	 * multiboot magic header.
92*adda2797SRoger Pau Monné 	 */
93*adda2797SRoger Pau Monné 	if (filename == NULL)
94*adda2797SRoger Pau Monné 		return (EFTYPE);
95*adda2797SRoger Pau Monné 	if ((fd = open(filename, O_RDONLY)) == -1)
96*adda2797SRoger Pau Monné 		return (errno);
97*adda2797SRoger Pau Monné 	header_search = malloc(MULTIBOOT_SEARCH);
98*adda2797SRoger Pau Monné 	if (header_search == NULL) {
99*adda2797SRoger Pau Monné 		error = ENOMEM;
100*adda2797SRoger Pau Monné 		goto out;
101*adda2797SRoger Pau Monné 	}
102*adda2797SRoger Pau Monné 	search_size = read(fd, header_search, MULTIBOOT_SEARCH);
103*adda2797SRoger Pau Monné 
104*adda2797SRoger Pau Monné 	for (i = 0; i < search_size; i += MULTIBOOT_HEADER_ALIGN) {
105*adda2797SRoger Pau Monné 		header = header_search + i;
106*adda2797SRoger Pau Monné 		if (header->magic == MULTIBOOT2_HEADER_MAGIC)
107*adda2797SRoger Pau Monné 			break;
108*adda2797SRoger Pau Monné 	}
109*adda2797SRoger Pau Monné 
110*adda2797SRoger Pau Monné 	if (i >= MULTIBOOT_SEARCH) {
111*adda2797SRoger Pau Monné 		error = EFTYPE;
112*adda2797SRoger Pau Monné 		goto out;
113*adda2797SRoger Pau Monné 	}
114*adda2797SRoger Pau Monné 
115*adda2797SRoger Pau Monné 	/* Valid multiboot header has been found, validate checksum */
116*adda2797SRoger Pau Monné 	if (header->magic + header->architecture + header->header_length +
117*adda2797SRoger Pau Monné 	    header->checksum != 0) {
118*adda2797SRoger Pau Monné 		printf("Multiboot checksum failed, magic: %#x architecture: %#x"
119*adda2797SRoger Pau Monné 		    "header_length %#x checksum: %#x\n",
120*adda2797SRoger Pau Monné 		    header->magic, header->architecture, header->header_length,
121*adda2797SRoger Pau Monné 		    header->checksum);
122*adda2797SRoger Pau Monné 		error = EFTYPE;
123*adda2797SRoger Pau Monné 		goto out;
124*adda2797SRoger Pau Monné 	}
125*adda2797SRoger Pau Monné 
126*adda2797SRoger Pau Monné 	if (header->architecture != MULTIBOOT2_ARCHITECTURE_I386) {
127*adda2797SRoger Pau Monné 		printf("Unsupported architecture: %#x\n",
128*adda2797SRoger Pau Monné 		    header->architecture);
129*adda2797SRoger Pau Monné 		error = EFTYPE;
130*adda2797SRoger Pau Monné 		goto out;
131*adda2797SRoger Pau Monné 	}
132*adda2797SRoger Pau Monné 
133*adda2797SRoger Pau Monné 	multiboot = malloc(header->header_length - sizeof(*header));
134*adda2797SRoger Pau Monné 	error = lseek(fd, i + sizeof(*header), SEEK_SET);
135*adda2797SRoger Pau Monné 	if (error != i + sizeof(*header)) {
136*adda2797SRoger Pau Monné 		printf("Unable to set file pointer to header location: %d\n",
137*adda2797SRoger Pau Monné 		    error);
138*adda2797SRoger Pau Monné 		goto out;
139*adda2797SRoger Pau Monné 	}
140*adda2797SRoger Pau Monné 	search_size = read(fd, multiboot,
141*adda2797SRoger Pau Monné 	    header->header_length - sizeof(*header));
142*adda2797SRoger Pau Monné 
143*adda2797SRoger Pau Monné 	bzero(&hdr, sizeof(hdr));
144*adda2797SRoger Pau Monné 	for (i = 0; i < search_size; ) {
145*adda2797SRoger Pau Monné 		struct multiboot_header_tag *tag;
146*adda2797SRoger Pau Monné 		struct multiboot_header_tag_entry_address *entry;
147*adda2797SRoger Pau Monné 		struct multiboot_header_tag_information_request *req;
148*adda2797SRoger Pau Monné 		unsigned int j;
149*adda2797SRoger Pau Monné 
150*adda2797SRoger Pau Monné 		tag = multiboot + i;
151*adda2797SRoger Pau Monné 
152*adda2797SRoger Pau Monné 		switch(tag->type) {
153*adda2797SRoger Pau Monné 		case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST:
154*adda2797SRoger Pau Monné 			req = (void *)tag;
155*adda2797SRoger Pau Monné 			for (j = 0;
156*adda2797SRoger Pau Monné 			    j < (tag->size - sizeof(*tag)) / sizeof(uint32_t);
157*adda2797SRoger Pau Monné 			    j++) {
158*adda2797SRoger Pau Monné 				switch (req->requests[j]) {
159*adda2797SRoger Pau Monné 				case MULTIBOOT_TAG_TYPE_MMAP:
160*adda2797SRoger Pau Monné 				case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
161*adda2797SRoger Pau Monné 					/* Only applicable to BIOS. */
162*adda2797SRoger Pau Monné 					break;
163*adda2797SRoger Pau Monné 
164*adda2797SRoger Pau Monné 				case MULTIBOOT_TAG_TYPE_EFI_BS:
165*adda2797SRoger Pau Monné 				case MULTIBOOT_TAG_TYPE_EFI64:
166*adda2797SRoger Pau Monné 				case MULTIBOOT_TAG_TYPE_EFI64_IH:
167*adda2797SRoger Pau Monné 					/* Tags unconditionally added. */
168*adda2797SRoger Pau Monné 					break;
169*adda2797SRoger Pau Monné 
170*adda2797SRoger Pau Monné 				default:
171*adda2797SRoger Pau Monné 					if (req->flags &
172*adda2797SRoger Pau Monné 					    MULTIBOOT_HEADER_TAG_OPTIONAL)
173*adda2797SRoger Pau Monné 						break;
174*adda2797SRoger Pau Monné 
175*adda2797SRoger Pau Monné 					printf(
176*adda2797SRoger Pau Monné 				"Unknown non-optional information request %u\n",
177*adda2797SRoger Pau Monné 					    req->requests[j]);
178*adda2797SRoger Pau Monné 					error = EINVAL;
179*adda2797SRoger Pau Monné 					goto out;
180*adda2797SRoger Pau Monné 				}
181*adda2797SRoger Pau Monné 			}
182*adda2797SRoger Pau Monné 			break;
183*adda2797SRoger Pau Monné 
184*adda2797SRoger Pau Monné 		case MULTIBOOT_HEADER_TAG_EFI_BS:
185*adda2797SRoger Pau Monné 			/* Never shut down BS. */
186*adda2797SRoger Pau Monné 			keep_bs = true;
187*adda2797SRoger Pau Monné 			break;
188*adda2797SRoger Pau Monné 
189*adda2797SRoger Pau Monné 		case MULTIBOOT_HEADER_TAG_MODULE_ALIGN:
190*adda2797SRoger Pau Monné 			/* We will align modules by default already. */
191*adda2797SRoger Pau Monné 		case MULTIBOOT_HEADER_TAG_END:
192*adda2797SRoger Pau Monné 			break;
193*adda2797SRoger Pau Monné 
194*adda2797SRoger Pau Monné 		case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64:
195*adda2797SRoger Pau Monné 			entry = (void *)tag;
196*adda2797SRoger Pau Monné 			hdr.efi64_entry = entry->entry_addr;
197*adda2797SRoger Pau Monné 			break;
198*adda2797SRoger Pau Monné 
199*adda2797SRoger Pau Monné 		default:
200*adda2797SRoger Pau Monné 			if (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL)
201*adda2797SRoger Pau Monné 				break;
202*adda2797SRoger Pau Monné 			printf("Unknown header tag %#x not optional\n",
203*adda2797SRoger Pau Monné 			    tag->type);
204*adda2797SRoger Pau Monné 			error = EINVAL;
205*adda2797SRoger Pau Monné 			goto out;
206*adda2797SRoger Pau Monné 		}
207*adda2797SRoger Pau Monné 
208*adda2797SRoger Pau Monné 		i += roundup2(tag->size, MULTIBOOT_TAG_ALIGN);
209*adda2797SRoger Pau Monné 		if (tag->type == MULTIBOOT_HEADER_TAG_END)
210*adda2797SRoger Pau Monné 			break;
211*adda2797SRoger Pau Monné 	}
212*adda2797SRoger Pau Monné 
213*adda2797SRoger Pau Monné 	if (hdr.efi64_entry == 0) {
214*adda2797SRoger Pau Monné 		printf("No EFI64 entry address provided\n");
215*adda2797SRoger Pau Monné 		error = EINVAL;
216*adda2797SRoger Pau Monné 		goto out;
217*adda2797SRoger Pau Monné 	}
218*adda2797SRoger Pau Monné 	if (!keep_bs) {
219*adda2797SRoger Pau Monné 		printf("Unable to boot MB2 with BS exited\n");
220*adda2797SRoger Pau Monné 		error = EINVAL;
221*adda2797SRoger Pau Monné 		goto out;
222*adda2797SRoger Pau Monné 	}
223*adda2797SRoger Pau Monné 
224*adda2797SRoger Pau Monné 	error = elf32_loadfile_raw(filename, dest, result, 1);
225*adda2797SRoger Pau Monné 	if (error != 0) {
226*adda2797SRoger Pau Monné 		printf(
227*adda2797SRoger Pau Monné 	"elf32_loadfile_raw failed: %d unable to load multiboot kernel\n",
228*adda2797SRoger Pau Monné 		    error);
229*adda2797SRoger Pau Monné 		goto out;
230*adda2797SRoger Pau Monné 	}
231*adda2797SRoger Pau Monné 
232*adda2797SRoger Pau Monné 	file_addmetadata(*result, MODINFOMD_NOCOPY | MODINFOMD_MB2HDR,
233*adda2797SRoger Pau Monné 	    sizeof(hdr), &hdr);
234*adda2797SRoger Pau Monné 
235*adda2797SRoger Pau Monné 	/*
236*adda2797SRoger Pau Monné 	 * f_addr is already aligned to PAGE_SIZE, make sure
237*adda2797SRoger Pau Monné 	 * f_size it's also aligned so when the modules are loaded
238*adda2797SRoger Pau Monné 	 * they are aligned to PAGE_SIZE.
239*adda2797SRoger Pau Monné 	 */
240*adda2797SRoger Pau Monné 	(*result)->f_size = roundup((*result)->f_size, PAGE_SIZE);
241*adda2797SRoger Pau Monné 
242*adda2797SRoger Pau Monné out:
243*adda2797SRoger Pau Monné 	if (header_search != NULL)
244*adda2797SRoger Pau Monné 		free(header_search);
245*adda2797SRoger Pau Monné 	if (multiboot != NULL)
246*adda2797SRoger Pau Monné 		free(multiboot);
247*adda2797SRoger Pau Monné 	close(fd);
248*adda2797SRoger Pau Monné 	return (error);
249*adda2797SRoger Pau Monné }
250*adda2797SRoger Pau Monné 
251*adda2797SRoger Pau Monné static unsigned int add_string(void *buf, unsigned int type, const char *str)
252*adda2797SRoger Pau Monné {
253*adda2797SRoger Pau Monné 	struct multiboot_tag *tag;
254*adda2797SRoger Pau Monné 
255*adda2797SRoger Pau Monné 	tag = buf;
256*adda2797SRoger Pau Monné 	tag->type = type;
257*adda2797SRoger Pau Monné 	tag->size = sizeof(*tag) + strlen(str) + 1;
258*adda2797SRoger Pau Monné 	strcpy(buf + sizeof(*tag), str);
259*adda2797SRoger Pau Monné 	return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN));
260*adda2797SRoger Pau Monné }
261*adda2797SRoger Pau Monné 
262*adda2797SRoger Pau Monné static unsigned int add_efi(void *buf)
263*adda2797SRoger Pau Monné {
264*adda2797SRoger Pau Monné 	struct multiboot_tag *bs;
265*adda2797SRoger Pau Monné 	struct multiboot_tag_efi64 *efi64;
266*adda2797SRoger Pau Monné 	struct multiboot_tag_efi64_ih *ih;
267*adda2797SRoger Pau Monné 	unsigned int len;
268*adda2797SRoger Pau Monné 
269*adda2797SRoger Pau Monné 	len = 0;
270*adda2797SRoger Pau Monné 	bs = buf;
271*adda2797SRoger Pau Monné 	bs->type = MULTIBOOT_TAG_TYPE_EFI_BS;
272*adda2797SRoger Pau Monné 	bs->size = sizeof(*bs);
273*adda2797SRoger Pau Monné 	len += roundup2(bs->size, MULTIBOOT_TAG_ALIGN);
274*adda2797SRoger Pau Monné 
275*adda2797SRoger Pau Monné 	efi64 = buf + len;
276*adda2797SRoger Pau Monné 	efi64->type = MULTIBOOT_TAG_TYPE_EFI64;
277*adda2797SRoger Pau Monné 	efi64->size = sizeof(*efi64);
278*adda2797SRoger Pau Monné 	efi64->pointer = (uintptr_t)ST;
279*adda2797SRoger Pau Monné 	len += roundup2(efi64->size, MULTIBOOT_TAG_ALIGN);
280*adda2797SRoger Pau Monné 
281*adda2797SRoger Pau Monné 	ih = buf + len;
282*adda2797SRoger Pau Monné 	ih->type = MULTIBOOT_TAG_TYPE_EFI64_IH;
283*adda2797SRoger Pau Monné 	ih->size = sizeof(*ih);
284*adda2797SRoger Pau Monné 	ih->pointer = (uintptr_t)IH;
285*adda2797SRoger Pau Monné 
286*adda2797SRoger Pau Monné 	return (len + roundup2(ih->size, MULTIBOOT_TAG_ALIGN));
287*adda2797SRoger Pau Monné }
288*adda2797SRoger Pau Monné 
289*adda2797SRoger Pau Monné static unsigned int add_module(void *buf, vm_offset_t start, vm_offset_t end,
290*adda2797SRoger Pau Monné     const char *cmdline)
291*adda2797SRoger Pau Monné {
292*adda2797SRoger Pau Monné 	struct multiboot_tag_module *mod;
293*adda2797SRoger Pau Monné 
294*adda2797SRoger Pau Monné 	mod = buf;
295*adda2797SRoger Pau Monné 	mod->type = MULTIBOOT_TAG_TYPE_MODULE;
296*adda2797SRoger Pau Monné 	mod->size = sizeof(*mod);
297*adda2797SRoger Pau Monné 	mod->mod_start = start;
298*adda2797SRoger Pau Monné 	mod->mod_end = end;
299*adda2797SRoger Pau Monné 	if (cmdline != NULL)
300*adda2797SRoger Pau Monné 	{
301*adda2797SRoger Pau Monné 		strcpy(buf + sizeof(*mod), cmdline);
302*adda2797SRoger Pau Monné 		mod->size += strlen(cmdline) + 1;
303*adda2797SRoger Pau Monné 	}
304*adda2797SRoger Pau Monné 
305*adda2797SRoger Pau Monné 	return (roundup2(mod->size, MULTIBOOT_TAG_ALIGN));
306*adda2797SRoger Pau Monné }
307*adda2797SRoger Pau Monné 
308*adda2797SRoger Pau Monné static unsigned int add_end(void *buf)
309*adda2797SRoger Pau Monné {
310*adda2797SRoger Pau Monné 	struct multiboot_tag *tag;
311*adda2797SRoger Pau Monné 
312*adda2797SRoger Pau Monné 	tag = buf;
313*adda2797SRoger Pau Monné 	tag->type = MULTIBOOT_TAG_TYPE_END;
314*adda2797SRoger Pau Monné 	tag->size = sizeof(*tag);
315*adda2797SRoger Pau Monné 
316*adda2797SRoger Pau Monné 	return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN));
317*adda2797SRoger Pau Monné }
318*adda2797SRoger Pau Monné 
319*adda2797SRoger Pau Monné static int
320*adda2797SRoger Pau Monné exec(struct preloaded_file *fp)
321*adda2797SRoger Pau Monné {
322*adda2797SRoger Pau Monné 	EFI_PHYSICAL_ADDRESS		 addr = 0;
323*adda2797SRoger Pau Monné 	EFI_PHYSICAL_ADDRESS		 stack = 0;
324*adda2797SRoger Pau Monné 	EFI_STATUS			 status;
325*adda2797SRoger Pau Monné 	void				*multiboot_space;
326*adda2797SRoger Pau Monné 	vm_offset_t			 modulep, kernend, kern_base,
327*adda2797SRoger Pau Monné 					 payload_base;
328*adda2797SRoger Pau Monné 	char				*cmdline = NULL;
329*adda2797SRoger Pau Monné 	size_t				 len;
330*adda2797SRoger Pau Monné 	int				 error;
331*adda2797SRoger Pau Monné 	uint32_t			*total_size;
332*adda2797SRoger Pau Monné 	struct file_metadata		*md;
333*adda2797SRoger Pau Monné 	struct xen_header		 header;
334*adda2797SRoger Pau Monné 	struct mb2hdr			*hdr;
335*adda2797SRoger Pau Monné 
336*adda2797SRoger Pau Monné 
337*adda2797SRoger Pau Monné 	CTASSERT(sizeof(header) <= PAGE_SIZE);
338*adda2797SRoger Pau Monné 
339*adda2797SRoger Pau Monné 	if ((md = file_findmetadata(fp,
340*adda2797SRoger Pau Monné 	    MODINFOMD_NOCOPY | MODINFOMD_MB2HDR)) == NULL) {
341*adda2797SRoger Pau Monné 		printf("Missing Multiboot2 EFI64 entry point\n");
342*adda2797SRoger Pau Monné 		return(EFTYPE);
343*adda2797SRoger Pau Monné 	}
344*adda2797SRoger Pau Monné 	hdr = (void *)&md->md_data;
345*adda2797SRoger Pau Monné 
346*adda2797SRoger Pau Monné 	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
347*adda2797SRoger Pau Monné 	    EFI_SIZE_TO_PAGES(PAGE_SIZE), &addr);
348*adda2797SRoger Pau Monné 	if (EFI_ERROR(status)) {
349*adda2797SRoger Pau Monné 		printf("Failed to allocate pages for multiboot2 header: %lu\n",
350*adda2797SRoger Pau Monné 		    EFI_ERROR_CODE(status));
351*adda2797SRoger Pau Monné 		error = ENOMEM;
352*adda2797SRoger Pau Monné 		goto error;
353*adda2797SRoger Pau Monné 	}
354*adda2797SRoger Pau Monné 	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
355*adda2797SRoger Pau Monné 	    EFI_SIZE_TO_PAGES(128 * 1024), &stack);
356*adda2797SRoger Pau Monné 	if (EFI_ERROR(status)) {
357*adda2797SRoger Pau Monné 		printf("Failed to allocate pages for Xen stack: %lu\n",
358*adda2797SRoger Pau Monné 		    EFI_ERROR_CODE(status));
359*adda2797SRoger Pau Monné 		error = ENOMEM;
360*adda2797SRoger Pau Monné 		goto error;
361*adda2797SRoger Pau Monné 	}
362*adda2797SRoger Pau Monné 
363*adda2797SRoger Pau Monné 	/*
364*adda2797SRoger Pau Monné 	 * Scratch space to build the multiboot2 header. Reserve the start of
365*adda2797SRoger Pau Monné 	 * the space to place the header with the size, which we don't know
366*adda2797SRoger Pau Monné 	 * yet.
367*adda2797SRoger Pau Monné 	 */
368*adda2797SRoger Pau Monné 	multiboot_space = (void *)(uintptr_t)(addr + sizeof(uint32_t) * 2);
369*adda2797SRoger Pau Monné 
370*adda2797SRoger Pau Monné 	/*
371*adda2797SRoger Pau Monné 	 * Don't pass the memory size found by the bootloader, the memory
372*adda2797SRoger Pau Monné 	 * available to Dom0 will be lower than that.
373*adda2797SRoger Pau Monné 	 */
374*adda2797SRoger Pau Monné 	unsetenv("smbios.memory.enabled");
375*adda2797SRoger Pau Monné 
376*adda2797SRoger Pau Monné 	/* Set the Xen command line. */
377*adda2797SRoger Pau Monné 	if (fp->f_args == NULL) {
378*adda2797SRoger Pau Monné 		/* Add the Xen command line if it is set. */
379*adda2797SRoger Pau Monné 		cmdline = getenv("xen_cmdline");
380*adda2797SRoger Pau Monné 		if (cmdline != NULL) {
381*adda2797SRoger Pau Monné 			fp->f_args = strdup(cmdline);
382*adda2797SRoger Pau Monné 			if (fp->f_args == NULL) {
383*adda2797SRoger Pau Monné 				error = ENOMEM;
384*adda2797SRoger Pau Monné 				goto error;
385*adda2797SRoger Pau Monné 			}
386*adda2797SRoger Pau Monné 		}
387*adda2797SRoger Pau Monné 	}
388*adda2797SRoger Pau Monné 	if (fp->f_args != NULL) {
389*adda2797SRoger Pau Monné 		len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1;
390*adda2797SRoger Pau Monné 		cmdline = malloc(len);
391*adda2797SRoger Pau Monné 		if (cmdline == NULL) {
392*adda2797SRoger Pau Monné 			error = ENOMEM;
393*adda2797SRoger Pau Monné 			goto error;
394*adda2797SRoger Pau Monné 		}
395*adda2797SRoger Pau Monné 		snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args);
396*adda2797SRoger Pau Monné 		multiboot_space += add_string(multiboot_space,
397*adda2797SRoger Pau Monné 		    MULTIBOOT_TAG_TYPE_CMDLINE, cmdline);
398*adda2797SRoger Pau Monné 		free(cmdline);
399*adda2797SRoger Pau Monné 	}
400*adda2797SRoger Pau Monné 
401*adda2797SRoger Pau Monné 	multiboot_space += add_string(multiboot_space,
402*adda2797SRoger Pau Monné 	    MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME, "FreeBSD Loader");
403*adda2797SRoger Pau Monné 	multiboot_space += add_efi(multiboot_space);
404*adda2797SRoger Pau Monné 
405*adda2797SRoger Pau Monné 	/*
406*adda2797SRoger Pau Monné 	 * Prepare the multiboot module list, Xen assumes the first
407*adda2797SRoger Pau Monné 	 * module is the Dom0 kernel, and the second one is the initramfs.
408*adda2797SRoger Pau Monné 	 * This is not optimal for FreeBSD, that doesn't have a initramfs
409*adda2797SRoger Pau Monné 	 * but instead loads modules dynamically and creates the metadata
410*adda2797SRoger Pau Monné 	 * info on-the-fly.
411*adda2797SRoger Pau Monné 	 *
412*adda2797SRoger Pau Monné 	 * As expected, the first multiboot module is going to be the
413*adda2797SRoger Pau Monné 	 * FreeBSD kernel loaded as a raw file. The second module is going
414*adda2797SRoger Pau Monné 	 * to contain the metadata info and the loaded modules.
415*adda2797SRoger Pau Monné 	 *
416*adda2797SRoger Pau Monné 	 * There's a small header prefixed in the second module that contains
417*adda2797SRoger Pau Monné 	 * some information required to calculate the relocated address of
418*adda2797SRoger Pau Monné 	 * modulep based on the original offset of modulep from the start of
419*adda2797SRoger Pau Monné 	 * the module address. Note other fields might be added to this header
420*adda2797SRoger Pau Monné 	 * if required.
421*adda2797SRoger Pau Monné 	 *
422*adda2797SRoger Pau Monné 	 * Native layout:
423*adda2797SRoger Pau Monné 	 *           fp->f_addr + fp->f_size
424*adda2797SRoger Pau Monné 	 * +---------+----------------+------------+
425*adda2797SRoger Pau Monné 	 * |         |                |            |
426*adda2797SRoger Pau Monné 	 * | Kernel  |    Modules     |  Metadata  |
427*adda2797SRoger Pau Monné 	 * |         |                |            |
428*adda2797SRoger Pau Monné 	 * +---------+----------------+------------+
429*adda2797SRoger Pau Monné 	 * fp->f_addr                 modulep      kernend
430*adda2797SRoger Pau Monné 	 *
431*adda2797SRoger Pau Monné 	 * Xen dom0 layout:
432*adda2797SRoger Pau Monné 	 * fp->f_addr             fp->f_addr + fp->f_size
433*adda2797SRoger Pau Monné 	 * +---------+------------+----------------+------------+
434*adda2797SRoger Pau Monné 	 * |         |            |                |            |
435*adda2797SRoger Pau Monné 	 * | Kernel  | xen_header |    Modules     |  Metadata  |
436*adda2797SRoger Pau Monné 	 * |         |            |                |            |
437*adda2797SRoger Pau Monné 	 * +---------+------------+----------------+------------+
438*adda2797SRoger Pau Monné 	 * 	                                   modulep      kernend
439*adda2797SRoger Pau Monné 	 * \________/\__________________________________________/
440*adda2797SRoger Pau Monné 	 *  module 0                 module 1
441*adda2797SRoger Pau Monné 	 */
442*adda2797SRoger Pau Monné 
443*adda2797SRoger Pau Monné 	fp = file_findfile(NULL, "elf kernel");
444*adda2797SRoger Pau Monné 	if (fp == NULL) {
445*adda2797SRoger Pau Monné 		printf("No FreeBSD kernel provided, aborting\n");
446*adda2797SRoger Pau Monné 		error = EINVAL;
447*adda2797SRoger Pau Monné 		goto error;
448*adda2797SRoger Pau Monné 	}
449*adda2797SRoger Pau Monné 
450*adda2797SRoger Pau Monné 	error = bi_load(fp->f_args, &modulep, &kernend, false);
451*adda2797SRoger Pau Monné 	if (error != 0)
452*adda2797SRoger Pau Monné 		goto error;
453*adda2797SRoger Pau Monné 
454*adda2797SRoger Pau Monné 	/*
455*adda2797SRoger Pau Monné 	 * Note that the Xen kernel requires to be started with BootServices
456*adda2797SRoger Pau Monné 	 * enabled, and hence we cannot use efi_copy_finish to relocate the
457*adda2797SRoger Pau Monné 	 * loaded data from the staging area to the expected loaded addresses.
458*adda2797SRoger Pau Monné 	 * This is fine because the Xen kernel is relocatable, so it can boot
459*adda2797SRoger Pau Monné 	 * fine straight from the staging area. We use efi_translate to get the
460*adda2797SRoger Pau Monné 	 * staging addresses where the kernels and metadata are currently
461*adda2797SRoger Pau Monné 	 * loaded.
462*adda2797SRoger Pau Monné 	 */
463*adda2797SRoger Pau Monné 	kern_base = (uintptr_t)efi_translate(fp->f_addr);
464*adda2797SRoger Pau Monné 	payload_base = kern_base + fp->f_size - PAGE_SIZE;
465*adda2797SRoger Pau Monné 	multiboot_space += add_module(multiboot_space, kern_base, payload_base,
466*adda2797SRoger Pau Monné 	    NULL);
467*adda2797SRoger Pau Monné 	multiboot_space += add_module(multiboot_space, payload_base,
468*adda2797SRoger Pau Monné 	    (uintptr_t)efi_translate(kernend), "header");
469*adda2797SRoger Pau Monné 
470*adda2797SRoger Pau Monné 	header.flags = XENHEADER_HAS_MODULEP_OFFSET;
471*adda2797SRoger Pau Monné 	header.modulep_offset = modulep - (fp->f_addr + fp->f_size - PAGE_SIZE);
472*adda2797SRoger Pau Monné 	archsw.arch_copyin(&header, fp->f_addr + fp->f_size - PAGE_SIZE,
473*adda2797SRoger Pau Monné 	    sizeof(header));
474*adda2797SRoger Pau Monné 
475*adda2797SRoger Pau Monné 	multiboot_space += add_end(multiboot_space);
476*adda2797SRoger Pau Monné 	total_size = (uint32_t *)(uintptr_t)(addr);
477*adda2797SRoger Pau Monné 	*total_size = (uintptr_t)multiboot_space - addr;
478*adda2797SRoger Pau Monné 
479*adda2797SRoger Pau Monné 	if (*total_size > PAGE_SIZE)
480*adda2797SRoger Pau Monné 		panic("Multiboot header exceeds fixed size");
481*adda2797SRoger Pau Monné 
482*adda2797SRoger Pau Monné 	efi_time_fini();
483*adda2797SRoger Pau Monné 	dev_cleanup();
484*adda2797SRoger Pau Monné 	multiboot2_exec(efi_translate(hdr->efi64_entry), addr,
485*adda2797SRoger Pau Monné 	    stack + 128 * 1024);
486*adda2797SRoger Pau Monné 
487*adda2797SRoger Pau Monné 	panic("exec returned");
488*adda2797SRoger Pau Monné 
489*adda2797SRoger Pau Monné error:
490*adda2797SRoger Pau Monné 	if (addr)
491*adda2797SRoger Pau Monné 		BS->FreePages(addr, EFI_SIZE_TO_PAGES(PAGE_SIZE));
492*adda2797SRoger Pau Monné 	if (stack)
493*adda2797SRoger Pau Monné 		BS->FreePages(stack, EFI_SIZE_TO_PAGES(128 * 1024));
494*adda2797SRoger Pau Monné 	return (error);
495*adda2797SRoger Pau Monné }
496*adda2797SRoger Pau Monné 
497*adda2797SRoger Pau Monné static int
498*adda2797SRoger Pau Monné obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result)
499*adda2797SRoger Pau Monné {
500*adda2797SRoger Pau Monné 	struct preloaded_file	*mfp, *kfp, *rfp;
501*adda2797SRoger Pau Monné 	struct kernel_module	*kmp;
502*adda2797SRoger Pau Monné 	int			 error;
503*adda2797SRoger Pau Monné 
504*adda2797SRoger Pau Monné 	/* See if there's a multiboot kernel loaded */
505*adda2797SRoger Pau Monné 	mfp = file_findfile(NULL, "elf multiboot kernel");
506*adda2797SRoger Pau Monné 	if (mfp == NULL)
507*adda2797SRoger Pau Monné 		return (EFTYPE);
508*adda2797SRoger Pau Monné 
509*adda2797SRoger Pau Monné 	/*
510*adda2797SRoger Pau Monné 	 * We have a multiboot kernel loaded, see if there's a FreeBSD
511*adda2797SRoger Pau Monné 	 * kernel loaded also.
512*adda2797SRoger Pau Monné 	 */
513*adda2797SRoger Pau Monné 	kfp = file_findfile(NULL, "elf kernel");
514*adda2797SRoger Pau Monné 	if (kfp == NULL) {
515*adda2797SRoger Pau Monné 		/*
516*adda2797SRoger Pau Monné 		 * No kernel loaded, this must be it. The kernel has to
517*adda2797SRoger Pau Monné 		 * be loaded as a raw file, it will be processed by
518*adda2797SRoger Pau Monné 		 * Xen and correctly loaded as an ELF file.
519*adda2797SRoger Pau Monné 		 */
520*adda2797SRoger Pau Monné 		rfp = file_loadraw(filename, "elf kernel", 0);
521*adda2797SRoger Pau Monné 		if (rfp == NULL) {
522*adda2797SRoger Pau Monné 			printf(
523*adda2797SRoger Pau Monné 			"Unable to load %s as a multiboot payload kernel\n",
524*adda2797SRoger Pau Monné 			filename);
525*adda2797SRoger Pau Monné 			return (EINVAL);
526*adda2797SRoger Pau Monné 		}
527*adda2797SRoger Pau Monné 
528*adda2797SRoger Pau Monné 		/* Load kernel metadata... */
529*adda2797SRoger Pau Monné 		setenv("kernelname", filename, 1);
530*adda2797SRoger Pau Monné 		error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size);
531*adda2797SRoger Pau Monné 		if (error) {
532*adda2797SRoger Pau Monné 			printf("Unable to load kernel %s metadata error: %d\n",
533*adda2797SRoger Pau Monné 			    rfp->f_name, error);
534*adda2797SRoger Pau Monné 			return (EINVAL);
535*adda2797SRoger Pau Monné 		}
536*adda2797SRoger Pau Monné 
537*adda2797SRoger Pau Monné 
538*adda2797SRoger Pau Monné 		/*
539*adda2797SRoger Pau Monné 		 * Reserve one page at the end of the kernel to place some
540*adda2797SRoger Pau Monné 		 * metadata in order to cope for Xen relocating the modules and
541*adda2797SRoger Pau Monné 		 * the metadata information.
542*adda2797SRoger Pau Monné 		 */
543*adda2797SRoger Pau Monné 		rfp->f_size = roundup(rfp->f_size, PAGE_SIZE);
544*adda2797SRoger Pau Monné 		rfp->f_size += PAGE_SIZE;
545*adda2797SRoger Pau Monné 		*result = rfp;
546*adda2797SRoger Pau Monné 	} else {
547*adda2797SRoger Pau Monné 		/* The rest should be loaded as regular modules */
548*adda2797SRoger Pau Monné 		error = elf64_obj_loadfile(filename, dest, result);
549*adda2797SRoger Pau Monné 		if (error != 0) {
550*adda2797SRoger Pau Monné 			printf("Unable to load %s as an object file, error: %d",
551*adda2797SRoger Pau Monné 			    filename, error);
552*adda2797SRoger Pau Monné 			return (error);
553*adda2797SRoger Pau Monné 		}
554*adda2797SRoger Pau Monné 	}
555*adda2797SRoger Pau Monné 
556*adda2797SRoger Pau Monné 	return (0);
557*adda2797SRoger Pau Monné }
558*adda2797SRoger Pau Monné 
559*adda2797SRoger Pau Monné static int
560*adda2797SRoger Pau Monné obj_exec(struct preloaded_file *fp)
561*adda2797SRoger Pau Monné {
562*adda2797SRoger Pau Monné 
563*adda2797SRoger Pau Monné 	return (EFTYPE);
564*adda2797SRoger Pau Monné }
565*adda2797SRoger Pau Monné 
566*adda2797SRoger Pau Monné struct file_format multiboot2 = { loadfile, exec };
567*adda2797SRoger Pau Monné struct file_format multiboot2_obj = { obj_loadfile, obj_exec };
568