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