1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <stand.h>
28#include <sys/param.h>
29#include <sys/reboot.h>
30#include <sys/linker.h>
31#include <i386/include/bootinfo.h>
32
33#include "bootstrap.h"
34#include "modinfo.h"
35#include "libuserboot.h"
36
37#ifdef LOADER_GELI_SUPPORT
38#include "geliboot.h"
39#endif
40
41static struct bootinfo  bi;
42
43/*
44 * Load the information expected by an i386 kernel.
45 *
46 * - The 'boothowto' argument is constructed
47 * - The 'bootdev' argument is constructed
48 * - The 'bootinfo' struct is constructed, and copied into the kernel space.
49 * - The kernel environment is copied into kernel space.
50 * - Module metadata are formatted and placed in kernel space.
51 */
52int
53bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernendp)
54{
55    struct preloaded_file	*xp, *kfp;
56    struct devdesc		*rootdev;
57    struct file_metadata	*md;
58    vm_offset_t			addr;
59    vm_offset_t			kernend;
60    vm_offset_t			envp;
61    vm_offset_t			size;
62    vm_offset_t			ssym, esym;
63    char			*rootdevname;
64    int				bootdevnr, howto;
65    char			*kernelname;
66    const char			*kernelpath;
67    uint64_t			lowmem, highmem;
68
69    howto = bi_getboothowto(args);
70
71    /*
72     * Allow the environment variable 'rootdev' to override the supplied device
73     * This should perhaps go to MI code and/or have $rootdev tested/set by
74     * MI code before launching the kernel.
75     */
76    rootdevname = getenv("rootdev");
77    userboot_getdev((void **)(&rootdev), rootdevname, NULL);
78    if (rootdev == NULL) {		/* bad $rootdev/$currdev */
79	printf("can't determine root device\n");
80	return(EINVAL);
81    }
82
83    /* Try reading the /etc/fstab file to select the root device */
84    getrootmount(devformat(rootdev));
85
86    bootdevnr = 0;
87#if 0
88    if (bootdevnr == -1) {
89	printf("root device %s invalid\n", devformat(rootdev));
90	return (EINVAL);
91    }
92#endif
93    free(rootdev);
94
95    /* find the last module in the chain */
96    addr = 0;
97    for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
98	if (addr < (xp->f_addr + xp->f_size))
99	    addr = xp->f_addr + xp->f_size;
100    }
101    /* pad to a page boundary */
102    addr = roundup(addr, PAGE_SIZE);
103
104    /* copy our environment */
105    envp = addr;
106    addr = md_copyenv(addr);
107
108    /* pad to a page boundary */
109    addr = roundup(addr, PAGE_SIZE);
110
111    kfp = file_findfile(NULL, "elf kernel");
112    if (kfp == NULL)
113      kfp = file_findfile(NULL, "elf32 kernel");
114    if (kfp == NULL)
115	panic("can't find kernel file");
116    kernend = 0;	/* fill it in later */
117    file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
118    file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
119    file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
120    bios_addsmapdata(kfp);
121#ifdef LOADER_GELI_SUPPORT
122    geli_export_key_metadata(kfp);
123#endif
124
125    /* Figure out the size and location of the metadata */
126    *modulep = addr;
127    size = md_copymodules(0, false);
128    kernend = roundup(addr + size, PAGE_SIZE);
129    *kernendp = kernend;
130
131    /* patch MODINFOMD_KERNEND */
132    md = file_findmetadata(kfp, MODINFOMD_KERNEND);
133    bcopy(&kernend, md->md_data, sizeof kernend);
134
135    /* copy module list and metadata */
136    (void)md_copymodules(addr, false);
137
138    ssym = esym = 0;
139    md = file_findmetadata(kfp, MODINFOMD_SSYM);
140    if (md != NULL)
141	ssym = *((vm_offset_t *)&(md->md_data));
142    md = file_findmetadata(kfp, MODINFOMD_ESYM);
143    if (md != NULL)
144	esym = *((vm_offset_t *)&(md->md_data));
145    if (ssym == 0 || esym == 0)
146	ssym = esym = 0;		/* sanity */
147
148    /* legacy bootinfo structure */
149    kernelname = getenv("kernelname");
150    userboot_getdev(NULL, kernelname, &kernelpath);
151    bi.bi_version = BOOTINFO_VERSION;
152    bi.bi_size = sizeof(bi);
153    CALLBACK(getmem, &lowmem, &highmem);
154    bi.bi_memsizes_valid = 1;
155    bi.bi_basemem = 640;
156    bi.bi_extmem = (lowmem - 0x100000) / 1024;
157    bi.bi_envp = envp;
158    bi.bi_modulep = *modulep;
159    bi.bi_kernend = kernend;
160    bi.bi_symtab = ssym;       /* XXX this is only the primary kernel symtab */
161    bi.bi_esymtab = esym;
162
163    /*
164     * Copy the legacy bootinfo and kernel name to the guest at 0x2000
165     */
166    bi.bi_kernelname = 0x2000 + sizeof(bi);
167    CALLBACK(copyin, &bi, 0x2000, sizeof(bi));
168    CALLBACK(copyin, kernelname, 0x2000 + sizeof(bi), strlen(kernelname) + 1);
169
170    /* legacy boot arguments */
171    *howtop = howto | RB_BOOTINFO;
172    *bootdevp = bootdevnr;
173    *bip = 0x2000;
174
175    return(0);
176}
177