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 *	from: FreeBSD: src/sys/boot/sparc64/loader/metadata.c,v 1.6
27 */
28
29#include <stand.h>
30#include <sys/param.h>
31#include <sys/linker.h>
32#include <sys/boot.h>
33#include <sys/reboot.h>
34#if defined(LOADER_FDT_SUPPORT)
35#include <fdt_platform.h>
36#endif
37
38#ifdef __arm__
39#include <machine/elf.h>
40#endif
41#include <machine/metadata.h>
42
43#include "bootstrap.h"
44#include "modinfo.h"
45
46#ifdef LOADER_GELI_SUPPORT
47#include "geliboot.h"
48#endif
49
50static int
51md_getboothowto(char *kargs)
52{
53    int		howto;
54
55    /* Parse kargs */
56    howto = boot_parse_cmdline(kargs);
57    howto |= boot_env_to_howto();
58    if (!strcmp(getenv("console"), "comconsole"))
59	howto |= RB_SERIAL;
60    if (!strcmp(getenv("console"), "nullconsole"))
61	howto |= RB_MUTE;
62    return(howto);
63}
64
65/*
66 * Load the information expected by a kernel.
67 *
68 * - The 'boothowto' argument is constructed
69 * - The 'bootdev' argument is constructed
70 * - The kernel environment is copied into kernel space.
71 * - Module metadata are formatted and placed in kernel space.
72 */
73static int
74md_load_dual(char *args, vm_offset_t *modulep, vm_offset_t *dtb, int kern64)
75{
76    struct preloaded_file	*kfp;
77    struct preloaded_file	*xp;
78    struct file_metadata	*md;
79    vm_offset_t			kernend;
80    vm_offset_t			addr;
81    vm_offset_t			envp;
82#if defined(LOADER_FDT_SUPPORT)
83    vm_offset_t			fdtp;
84#endif
85    vm_offset_t			size;
86    uint64_t			scratch64;
87    char			*rootdevname;
88    int				howto;
89#ifdef __arm__
90    vm_offset_t			vaddr;
91    int				i;
92
93	/*
94	 * These metadata addreses must be converted for kernel after
95	 * relocation.
96	 */
97    uint32_t			mdt[] = {
98	    MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
99	    MODINFOMD_ENVP,
100#if defined(LOADER_FDT_SUPPORT)
101	    MODINFOMD_DTBP
102#endif
103    };
104#endif
105
106    howto = md_getboothowto(args);
107
108    /*
109     * Allow the environment variable 'rootdev' to override the supplied
110     * device. This should perhaps go to MI code and/or have $rootdev
111     * tested/set by MI code before launching the kernel.
112     */
113    rootdevname = getenv("rootdev");
114    if (rootdevname == NULL)
115	rootdevname = getenv("currdev");
116    /* Try reading the /etc/fstab file to select the root device */
117    getrootmount(rootdevname);
118
119    /* Find the last module in the chain */
120    addr = 0;
121    for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
122	if (addr < (xp->f_addr + xp->f_size))
123	    addr = xp->f_addr + xp->f_size;
124    }
125    /* Pad to a page boundary */
126    addr = roundup(addr, PAGE_SIZE);
127
128    /* Copy our environment */
129    envp = addr;
130    addr = md_copyenv(addr);
131
132    /* Pad to a page boundary */
133    addr = roundup(addr, PAGE_SIZE);
134
135#if defined(LOADER_FDT_SUPPORT)
136    /* Copy out FDT */
137    fdtp = 0;
138#if defined(__powerpc__)
139    if (getenv("usefdt") != NULL)
140#endif
141    {
142	size = fdt_copy(addr);
143	fdtp = addr;
144	addr = roundup(addr + size, PAGE_SIZE);
145    }
146#endif
147
148    kernend = 0;
149    kfp = file_findfile(NULL, kern64 ? "elf64 kernel" : "elf32 kernel");
150    if (kfp == NULL)
151	kfp = file_findfile(NULL, "elf kernel");
152    if (kfp == NULL)
153	panic("can't find kernel file");
154    file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
155    if (kern64) {
156	scratch64 = envp;
157	file_addmetadata(kfp, MODINFOMD_ENVP, sizeof scratch64, &scratch64);
158#if defined(LOADER_FDT_SUPPORT)
159	if (fdtp != 0) {
160	    scratch64 = fdtp;
161	    file_addmetadata(kfp, MODINFOMD_DTBP, sizeof scratch64, &scratch64);
162	}
163#endif
164	scratch64 = kernend;
165	file_addmetadata(kfp, MODINFOMD_KERNEND,
166		sizeof scratch64, &scratch64);
167    } else {
168	file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
169#if defined(LOADER_FDT_SUPPORT)
170	if (fdtp != 0)
171	    file_addmetadata(kfp, MODINFOMD_DTBP, sizeof fdtp, &fdtp);
172#endif
173	file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
174    }
175#ifdef LOADER_GELI_SUPPORT
176    geli_export_key_metadata(kfp);
177#endif
178
179    *modulep = addr;
180    size = md_copymodules(0, kern64);
181    kernend = roundup(addr + size, PAGE_SIZE);
182
183    md = file_findmetadata(kfp, MODINFOMD_KERNEND);
184    if (kern64) {
185	scratch64 = kernend;
186	bcopy(&scratch64, md->md_data, sizeof scratch64);
187    } else {
188	bcopy(&kernend, md->md_data, sizeof kernend);
189    }
190
191#ifdef __arm__
192    /* Convert addresses to the final VA */
193    *modulep -= __elfN(relocation_offset);
194
195    /* Do relocation fixup on metadata of each module. */
196    for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
197        for (i = 0; i < nitems(mdt); i++) {
198            md = file_findmetadata(xp, mdt[i]);
199                if (md) {
200                    bcopy(md->md_data, &vaddr, sizeof vaddr);
201                    vaddr -= __elfN(relocation_offset);
202                    bcopy(&vaddr, md->md_data, sizeof vaddr);
203                }
204            }
205    }
206#endif
207
208    (void)md_copymodules(addr, kern64);
209#if defined(LOADER_FDT_SUPPORT)
210    if (dtb != NULL)
211	*dtb = fdtp;
212#endif
213
214    return(0);
215}
216
217int
218md_load(char *args, vm_offset_t *modulep, vm_offset_t *dtb)
219{
220    return (md_load_dual(args, modulep, dtb, 0));
221}
222
223#if defined(__powerpc__)
224int
225md_load64(char *args, vm_offset_t *modulep, vm_offset_t *dtb)
226{
227    return (md_load_dual(args, modulep, dtb, 1));
228}
229#endif
230