1277215Sroyger/*-
2277215Sroyger * Copyright (c) 2014 Roger Pau Monn�� <royger@FreeBSD.org>
3277215Sroyger * All rights reserved.
4277215Sroyger *
5277215Sroyger * Redistribution and use in source and binary forms, with or without
6277215Sroyger * modification, are permitted provided that the following conditions
7277215Sroyger * are met:
8277215Sroyger * 1. Redistributions of source code must retain the above copyright
9277215Sroyger *    notice, this list of conditions and the following disclaimer.
10277215Sroyger * 2. Redistributions in binary form must reproduce the above copyright
11277215Sroyger *    notice, this list of conditions and the following disclaimer in the
12277215Sroyger *    documentation and/or other materials provided with the distribution.
13277215Sroyger *
14277215Sroyger * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15277215Sroyger * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16277215Sroyger * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17277215Sroyger * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18277215Sroyger * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19277215Sroyger * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20277215Sroyger * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21277215Sroyger * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22277215Sroyger * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23277215Sroyger * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24277215Sroyger * SUCH DAMAGE.
25277215Sroyger */
26277215Sroyger
27277215Sroyger/*
28277215Sroyger * This multiboot implementation only implements a subset of the full
29277215Sroyger * multiboot specification in order to be able to boot Xen and a
30277215Sroyger * FreeBSD Dom0. Trying to use it to boot other multiboot compliant
31277215Sroyger * kernels will most surely fail.
32277215Sroyger *
33277215Sroyger * The full multiboot specification can be found here:
34277215Sroyger * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
35277215Sroyger */
36277215Sroyger
37277215Sroyger#include <sys/cdefs.h>
38277215Sroyger__FBSDID("$FreeBSD$");
39277215Sroyger
40277215Sroyger#include <sys/param.h>
41277215Sroyger#include <sys/exec.h>
42277215Sroyger#include <sys/linker.h>
43277215Sroyger#include <sys/module.h>
44277215Sroyger#include <sys/stdint.h>
45277215Sroyger#define _MACHINE_ELF_WANT_32BIT
46277215Sroyger#include <machine/elf.h>
47277215Sroyger#include <string.h>
48277215Sroyger#include <stand.h>
49277215Sroyger
50277215Sroyger#include "bootstrap.h"
51277215Sroyger#include "multiboot.h"
52277215Sroyger#include "../i386/libi386/libi386.h"
53277215Sroyger#include "../i386/btx/lib/btxv86.h"
54277215Sroyger
55277215Sroyger#define MULTIBOOT_SUPPORTED_FLAGS \
56277215Sroyger				(MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO)
57277215Sroyger#define NUM_MODULES		2
58277215Sroyger#define METADATA_FIXED_SIZE	(PAGE_SIZE*4)
59277215Sroyger#define METADATA_MODULE_SIZE	PAGE_SIZE
60277215Sroyger
61277215Sroyger#define METADATA_RESV_SIZE(mod_num) \
62277215Sroyger	roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE)
63277215Sroyger
64277215Sroygerextern int elf32_loadfile_raw(char *filename, u_int64_t dest,
65277215Sroyger    struct preloaded_file **result, int multiboot);
66277215Sroygerextern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest);
67277215Sroygerextern int elf64_obj_loadfile(char *filename, u_int64_t dest,
68277215Sroyger    struct preloaded_file **result);
69277215Sroyger
70277215Sroygerstatic int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **);
71277215Sroygerstatic int multiboot_exec(struct preloaded_file *);
72277215Sroyger
73277215Sroygerstatic int multiboot_obj_loadfile(char *, u_int64_t, struct preloaded_file **);
74277215Sroygerstatic int multiboot_obj_exec(struct preloaded_file *fp);
75277215Sroyger
76277215Sroygerstruct file_format multiboot = { multiboot_loadfile, multiboot_exec };
77277215Sroygerstruct file_format multiboot_obj =
78277215Sroyger    { multiboot_obj_loadfile, multiboot_obj_exec };
79277215Sroyger
80277215Sroygerextern void multiboot_tramp();
81277215Sroyger
82277215Sroygerstatic const char mbl_name[] = "FreeBSD Loader";
83277215Sroyger
84277215Sroygerstatic int
85277215Sroygernum_modules(struct preloaded_file *kfp)
86277215Sroyger{
87277215Sroyger	struct kernel_module	*kmp;
88277215Sroyger	int			 mod_num = 0;
89277215Sroyger
90277215Sroyger	for (kmp = kfp->f_modules; kmp != NULL; kmp = kmp->m_next)
91277215Sroyger		mod_num++;
92277215Sroyger
93277215Sroyger	return (mod_num);
94277215Sroyger}
95277215Sroyger
96277215Sroygerstatic vm_offset_t
97277215Sroygermax_addr(void)
98277215Sroyger{
99277215Sroyger	struct preloaded_file	*fp;
100277215Sroyger	vm_offset_t		 addr = 0;
101277215Sroyger
102277215Sroyger	for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
103277215Sroyger		if (addr < (fp->f_addr + fp->f_size))
104277215Sroyger			addr = fp->f_addr + fp->f_size;
105277215Sroyger	}
106277215Sroyger
107277215Sroyger	return (addr);
108277215Sroyger}
109277215Sroyger
110277215Sroygerstatic int
111277215Sroygermultiboot_loadfile(char *filename, u_int64_t dest,
112277215Sroyger    struct preloaded_file **result)
113277215Sroyger{
114277215Sroyger	uint32_t		*magic;
115277215Sroyger	int			 i, error;
116277215Sroyger	caddr_t			 header_search;
117277215Sroyger	ssize_t			 search_size;
118277215Sroyger	int			 fd;
119277215Sroyger	struct multiboot_header	*header;
120277215Sroyger	char			*cmdline;
121277215Sroyger
122277215Sroyger	/*
123277215Sroyger	 * Read MULTIBOOT_SEARCH size in order to search for the
124277215Sroyger	 * multiboot magic header.
125277215Sroyger	 */
126277215Sroyger	if (filename == NULL)
127277215Sroyger		return (EFTYPE);
128277215Sroyger	if ((fd = open(filename, O_RDONLY)) == -1)
129277215Sroyger		return (errno);
130277215Sroyger	header_search = malloc(MULTIBOOT_SEARCH);
131277215Sroyger	if (header_search == NULL) {
132277215Sroyger		close(fd);
133277215Sroyger		return (ENOMEM);
134277215Sroyger	}
135277215Sroyger	search_size = read(fd, header_search, MULTIBOOT_SEARCH);
136277215Sroyger	magic = (uint32_t *)header_search;
137277215Sroyger
138277215Sroyger	header = NULL;
139277215Sroyger	for (i = 0; i < (search_size / sizeof(uint32_t)); i++) {
140277215Sroyger		if (magic[i] == MULTIBOOT_HEADER_MAGIC) {
141277215Sroyger			header = (struct multiboot_header *)&magic[i];
142277215Sroyger			break;
143277215Sroyger		}
144277215Sroyger	}
145277215Sroyger
146277215Sroyger	if (header == NULL) {
147277215Sroyger		error = EFTYPE;
148277215Sroyger		goto out;
149277215Sroyger	}
150277215Sroyger
151277215Sroyger	/* Valid multiboot header has been found, validate checksum */
152277215Sroyger	if (header->magic + header->flags + header->checksum != 0) {
153277215Sroyger		printf(
154277215Sroyger	"Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n",
155277215Sroyger	header->magic, header->flags, header->checksum);
156277215Sroyger		error = EFTYPE;
157277215Sroyger		goto out;
158277215Sroyger	}
159277215Sroyger
160277215Sroyger	if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) {
161277215Sroyger		printf("Unsupported multiboot flags found: 0x%x\n",
162277215Sroyger		    header->flags);
163277215Sroyger		error = EFTYPE;
164277215Sroyger		goto out;
165277215Sroyger	}
166277215Sroyger
167277215Sroyger	error = elf32_loadfile_raw(filename, dest, result, 1);
168277215Sroyger	if (error != 0) {
169277215Sroyger		printf(
170277215Sroyger	"elf32_loadfile_raw failed: %d unable to load multiboot kernel\n",
171277215Sroyger	error);
172277215Sroyger		goto out;
173277215Sroyger	}
174277215Sroyger
175277215Sroyger	/*
176277215Sroyger	 * f_addr is already aligned to PAGE_SIZE, make sure
177277215Sroyger	 * f_size it's also aligned so when the modules are loaded
178277215Sroyger	 * they are aligned to PAGE_SIZE.
179277215Sroyger	 */
180277215Sroyger	(*result)->f_size = roundup((*result)->f_size, PAGE_SIZE);
181277215Sroyger
182277215Sroygerout:
183277215Sroyger	free(header_search);
184277215Sroyger	close(fd);
185277215Sroyger	return (error);
186277215Sroyger}
187277215Sroyger
188277215Sroygerstatic int
189277215Sroygermultiboot_exec(struct preloaded_file *fp)
190277215Sroyger{
191277215Sroyger	vm_offset_t			 module_start, last_addr, metadata_size;
192277215Sroyger	vm_offset_t			 modulep, kernend, entry;
193277215Sroyger	struct file_metadata		*md;
194277215Sroyger	Elf_Ehdr			*ehdr;
195277215Sroyger	struct multiboot_info		*mb_info = NULL;
196277215Sroyger	struct multiboot_mod_list	*mb_mod = NULL;
197277215Sroyger	char				*cmdline = NULL;
198277215Sroyger	size_t				 len;
199277215Sroyger	int				 error, mod_num;
200277215Sroyger
201277215Sroyger	/*
202277215Sroyger	 * Don't pass the memory size found by the bootloader, the memory
203277215Sroyger	 * available to Dom0 will be lower than that.
204277215Sroyger	 */
205277215Sroyger	unsetenv("smbios.memory.enabled");
206277215Sroyger
207277215Sroyger	/* Allocate the multiboot struct and fill the basic details. */
208277215Sroyger	mb_info = malloc(sizeof(struct multiboot_info));
209277215Sroyger	if (mb_info == NULL) {
210277215Sroyger		error = ENOMEM;
211277215Sroyger		goto error;
212277215Sroyger	}
213277215Sroyger	bzero(mb_info, sizeof(struct multiboot_info));
214277215Sroyger	mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME;
215277215Sroyger	mb_info->mem_lower = bios_basemem / 1024;
216277215Sroyger	mb_info->mem_upper = bios_extmem / 1024;
217277215Sroyger	mb_info->boot_loader_name = VTOP(mbl_name);
218277215Sroyger
219277215Sroyger	/* Set the Xen command line. */
220277215Sroyger	if (fp->f_args == NULL) {
221277215Sroyger		/* Add the Xen command line if it is set. */
222277215Sroyger		cmdline = getenv("xen_cmdline");
223277215Sroyger		if (cmdline != NULL) {
224277215Sroyger			fp->f_args = strdup(cmdline);
225277215Sroyger			if (fp->f_args == NULL) {
226277215Sroyger				error = ENOMEM;
227277215Sroyger				goto error;
228277215Sroyger			}
229277215Sroyger		}
230277215Sroyger	}
231277215Sroyger	if (fp->f_args != NULL) {
232277215Sroyger		len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1;
233277215Sroyger		cmdline = malloc(len);
234277215Sroyger		if (cmdline == NULL) {
235277215Sroyger			error = ENOMEM;
236277215Sroyger			goto error;
237277215Sroyger		}
238277215Sroyger		snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args);
239277215Sroyger		mb_info->cmdline = VTOP(cmdline);
240277215Sroyger		mb_info->flags |= MULTIBOOT_INFO_CMDLINE;
241277215Sroyger	}
242277215Sroyger
243277215Sroyger	/* Find the entry point of the Xen kernel and save it for later */
244277215Sroyger	if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
245277215Sroyger		printf("Unable to find %s entry point\n", fp->f_name);
246294417Sroyger		error = EINVAL;
247277215Sroyger		goto error;
248277215Sroyger	}
249277215Sroyger	ehdr = (Elf_Ehdr *)&(md->md_data);
250277215Sroyger	entry = ehdr->e_entry & 0xffffff;
251277215Sroyger
252277215Sroyger	/*
253277215Sroyger	 * Prepare the multiboot module list, Xen assumes the first
254277215Sroyger	 * module is the Dom0 kernel, and the second one is the initramfs.
255277215Sroyger	 * This is not optimal for FreeBSD, that doesn't have a initramfs
256277215Sroyger	 * but instead loads modules dynamically and creates the metadata
257277215Sroyger	 * info on-the-fly.
258277215Sroyger	 *
259277215Sroyger	 * As expected, the first multiboot module is going to be the
260277215Sroyger	 * FreeBSD kernel loaded as a raw file. The second module is going
261277215Sroyger	 * to contain the metadata info and the loaded modules.
262277215Sroyger	 *
263277215Sroyger	 * On native FreeBSD loads all the modules and then places the
264277215Sroyger	 * metadata info at the end, but this is painful when running on Xen,
265277215Sroyger	 * because it relocates the second multiboot module wherever it
266277215Sroyger	 * likes. In order to workaround this limitation the metadata
267277215Sroyger	 * information is placed at the start of the second module and
268277215Sroyger	 * the original modulep value is saved together with the other
269277215Sroyger	 * metadata, so we can relocate everything.
270277215Sroyger	 */
271277215Sroyger	fp = file_findfile(NULL, "elf kernel");
272277215Sroyger	if (fp == NULL) {
273277215Sroyger		printf("No FreeBSD kernel provided, aborting\n");
274294417Sroyger		error = EINVAL;
275277215Sroyger		goto error;
276277215Sroyger	}
277294417Sroyger
278277215Sroyger	mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES);
279294417Sroyger	if (mb_mod == NULL) {
280294417Sroyger		error = ENOMEM;
281294417Sroyger		goto error;
282294417Sroyger	}
283277215Sroyger
284294417Sroyger	bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES);
285294417Sroyger
286277215Sroyger	/*
287277215Sroyger	 * Calculate how much memory is needed for the metatdata. We did
288277215Sroyger	 * an approximation of the maximum size when loading the kernel,
289277215Sroyger	 * but now we know the exact size, so we can release some of this
290277215Sroyger	 * preallocated memory if not needed.
291277215Sroyger	 */
292277215Sroyger	last_addr = roundup(max_addr(), PAGE_SIZE);
293277215Sroyger	mod_num = num_modules(fp);
294277215Sroyger
295277215Sroyger	/*
296277215Sroyger	 * Place the metadata after the last used address in order to
297277215Sroyger	 * calculate it's size, this will not be used.
298277215Sroyger	 */
299277215Sroyger	error = bi_load64(fp->f_args, last_addr, &modulep, &kernend, 0);
300277215Sroyger	if (error != 0) {
301277215Sroyger		printf("bi_load64 failed: %d\n", error);
302277215Sroyger		goto error;
303277215Sroyger	}
304277215Sroyger	metadata_size = roundup(kernend - last_addr, PAGE_SIZE);
305277215Sroyger
306277215Sroyger	/* Check that the size is not greater than what we have reserved */
307277215Sroyger	if (metadata_size > METADATA_RESV_SIZE(mod_num)) {
308277215Sroyger		printf("Required memory for metadata is greater than reserved "
309277215Sroyger		    "space, please increase METADATA_FIXED_SIZE and "
310277215Sroyger		    "METADATA_MODULE_SIZE and rebuild the loader\n");
311277215Sroyger		error = ENOMEM;
312277215Sroyger		goto error;
313277215Sroyger	}
314277215Sroyger
315277215Sroyger	/*
316277215Sroyger	 * This is the position where the second multiboot module
317277215Sroyger	 * will be placed.
318277215Sroyger	 */
319277215Sroyger	module_start = fp->f_addr + fp->f_size - metadata_size;
320277215Sroyger
321277215Sroyger	error = bi_load64(fp->f_args, module_start, &modulep, &kernend, 0);
322277215Sroyger	if (error != 0) {
323277215Sroyger		printf("bi_load64 failed: %d\n", error);
324277215Sroyger		goto error;
325277215Sroyger	}
326277215Sroyger
327277215Sroyger	mb_mod[0].mod_start = fp->f_addr;
328277215Sroyger	mb_mod[0].mod_end = fp->f_addr + fp->f_size;
329277215Sroyger	mb_mod[0].mod_end -= METADATA_RESV_SIZE(mod_num);
330277215Sroyger
331277215Sroyger	mb_mod[1].mod_start = module_start;
332277215Sroyger	mb_mod[1].mod_end = last_addr;
333277215Sroyger
334277215Sroyger	mb_info->mods_count = NUM_MODULES;
335277215Sroyger	mb_info->mods_addr = VTOP(mb_mod);
336277215Sroyger	mb_info->flags |= MULTIBOOT_INFO_MODS;
337277215Sroyger
338277215Sroyger	dev_cleanup();
339277215Sroyger	__exec((void *)VTOP(multiboot_tramp), (void *)entry,
340277215Sroyger	    (void *)VTOP(mb_info));
341277215Sroyger
342277215Sroyger	panic("exec returned");
343277215Sroyger
344277215Sroygererror:
345277215Sroyger	if (mb_mod)
346277215Sroyger		free(mb_mod);
347277215Sroyger	if (mb_info)
348277215Sroyger		free(mb_info);
349277215Sroyger	if (cmdline)
350277215Sroyger		free(cmdline);
351277215Sroyger	return (error);
352277215Sroyger}
353277215Sroyger
354277215Sroygerstatic int
355277215Sroygermultiboot_obj_loadfile(char *filename, u_int64_t dest,
356277215Sroyger    struct preloaded_file **result)
357277215Sroyger{
358277215Sroyger	struct preloaded_file	*mfp, *kfp, *rfp;
359277215Sroyger	struct kernel_module	*kmp;
360277215Sroyger	int			 error, mod_num;
361277215Sroyger
362277215Sroyger	/* See if there's a multiboot kernel loaded */
363277215Sroyger	mfp = file_findfile(NULL, "elf multiboot kernel");
364277215Sroyger	if (mfp == NULL)
365277215Sroyger		return (EFTYPE);
366277215Sroyger
367277215Sroyger	/*
368277215Sroyger	 * We have a multiboot kernel loaded, see if there's a FreeBSD
369277215Sroyger	 * kernel loaded also.
370277215Sroyger	 */
371277215Sroyger	kfp = file_findfile(NULL, "elf kernel");
372277215Sroyger	if (kfp == NULL) {
373277215Sroyger		/*
374277215Sroyger		 * No kernel loaded, this must be it. The kernel has to
375277215Sroyger		 * be loaded as a raw file, it will be processed by
376277215Sroyger		 * Xen and correctly loaded as an ELF file.
377277215Sroyger		 */
378277215Sroyger		rfp = file_loadraw(filename, "elf kernel", 0);
379277215Sroyger		if (rfp == NULL) {
380277215Sroyger			printf(
381277215Sroyger			"Unable to load %s as a multiboot payload kernel\n",
382277215Sroyger			filename);
383294417Sroyger			return (EINVAL);
384277215Sroyger		}
385277215Sroyger
386277215Sroyger		/* Load kernel metadata... */
387277215Sroyger		setenv("kernelname", filename, 1);
388277215Sroyger		error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size);
389277215Sroyger		if (error) {
390277215Sroyger			printf("Unable to load kernel %s metadata error: %d\n",
391277215Sroyger			    rfp->f_name, error);
392294417Sroyger			return (EINVAL);
393277215Sroyger		}
394277215Sroyger
395277215Sroyger		/*
396277215Sroyger		 * Save space at the end of the kernel in order to place
397277215Sroyger		 * the metadata information. We do an approximation of the
398277215Sroyger		 * max metadata size, this is not optimal but it's probably
399277215Sroyger		 * the best we can do at this point. Once all modules are
400277215Sroyger		 * loaded and the size of the metadata is known this
401277215Sroyger		 * space will be recovered if not used.
402277215Sroyger		 */
403277215Sroyger		mod_num = num_modules(rfp);
404277215Sroyger		rfp->f_size = roundup(rfp->f_size, PAGE_SIZE);
405277215Sroyger		rfp->f_size += METADATA_RESV_SIZE(mod_num);
406277215Sroyger		*result = rfp;
407277215Sroyger	} else {
408277215Sroyger		/* The rest should be loaded as regular modules */
409277215Sroyger		error = elf64_obj_loadfile(filename, dest, result);
410277215Sroyger		if (error != 0) {
411277215Sroyger			printf("Unable to load %s as an object file, error: %d",
412277215Sroyger			    filename, error);
413277215Sroyger			return (error);
414277215Sroyger		}
415277215Sroyger	}
416277215Sroyger
417277215Sroyger	return (0);
418277215Sroyger}
419277215Sroyger
420277215Sroygerstatic int
421277215Sroygermultiboot_obj_exec(struct preloaded_file *fp)
422277215Sroyger{
423277215Sroyger
424277215Sroyger	return (EFTYPE);
425277215Sroyger}
426