1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * Copyright (c) 2006 Marcel Moolenaar 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD$"); 30 31#include <stand.h> 32#include <string.h> 33#include <sys/param.h> 34#include <sys/reboot.h> 35#include <sys/linker.h> 36 37#include <efi.h> 38#include <efilib.h> 39 40#include "bootstrap.h" 41#include "libi386.h" 42#include <machine/bootinfo.h> 43 44/* 45 * Return a 'boothowto' value corresponding to the kernel arguments in 46 * (kargs) and any relevant environment variables. 47 */ 48static struct 49{ 50 const char *ev; 51 int mask; 52} howto_names[] = { 53 { "boot_askname", RB_ASKNAME}, 54 { "boot_cdrom", RB_CDROM}, 55 { "boot_ddb", RB_KDB}, 56 { "boot_dfltroot", RB_DFLTROOT}, 57 { "boot_gdb", RB_GDB}, 58 { "boot_multicons", RB_MULTIPLE}, 59 { "boot_mute", RB_MUTE}, 60 { "boot_pause", RB_PAUSE}, 61 { "boot_serial", RB_SERIAL}, 62 { "boot_single", RB_SINGLE}, 63 { "boot_verbose", RB_VERBOSE}, 64 { NULL, 0} 65}; 66 67static const char howto_switches[] = "aCdrgDmphsv"; 68static int howto_masks[] = { 69 RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE, 70 RB_MUTE, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE 71}; 72 73int 74bi_getboothowto(char *kargs) 75{ 76 const char *sw; 77 char *opts; 78 int howto, i; 79 80 howto = 0; 81 82 /* Get the boot options from the environment first. */ 83 for (i = 0; howto_names[i].ev != NULL; i++) { 84 if (getenv(howto_names[i].ev) != NULL) 85 howto |= howto_names[i].mask; 86 } 87 88 /* Parse kargs */ 89 if (kargs == NULL) 90 return (howto); 91 92 opts = strchr(kargs, '-'); 93 while (opts != NULL) { 94 while (*(++opts) != '\0') { 95 sw = strchr(howto_switches, *opts); 96 if (sw == NULL) 97 break; 98 howto |= howto_masks[sw - howto_switches]; 99 } 100 opts = strchr(opts, '-'); 101 } 102 103 return (howto); 104} 105 106/* 107 * Copy the environment into the load area starting at (addr). 108 * Each variable is formatted as <name>=<value>, with a single nul 109 * separating each variable, and a double nul terminating the environment. 110 */ 111vm_offset_t 112bi_copyenv(vm_offset_t start) 113{ 114 struct env_var *ep; 115 vm_offset_t addr, last; 116 size_t len; 117 118 addr = last = start; 119 120 /* Traverse the environment. */ 121 for (ep = environ; ep != NULL; ep = ep->ev_next) { 122 len = strlen(ep->ev_name); 123 if (i386_copyin(ep->ev_name, addr, len) != len) 124 break; 125 addr += len; 126 if (i386_copyin("=", addr, 1) != 1) 127 break; 128 addr++; 129 if (ep->ev_value != NULL) { 130 len = strlen(ep->ev_value); 131 if (i386_copyin(ep->ev_value, addr, len) != len) 132 break; 133 addr += len; 134 } 135 if (i386_copyin("", addr, 1) != 1) 136 break; 137 last = ++addr; 138 } 139 140 if (i386_copyin("", last++, 1) != 1) 141 last = start; 142 return(last); 143} 144 145/* 146 * Copy module-related data into the load area, where it can be 147 * used as a directory for loaded modules. 148 * 149 * Module data is presented in a self-describing format. Each datum 150 * is preceded by a 32-bit identifier and a 32-bit size field. 151 * 152 * Currently, the following data are saved: 153 * 154 * MOD_NAME (variable) module name (string) 155 * MOD_TYPE (variable) module type (string) 156 * MOD_ARGS (variable) module parameters (string) 157 * MOD_ADDR sizeof(vm_offset_t) module load address 158 * MOD_SIZE sizeof(size_t) module size 159 * MOD_METADATA (variable) type-specific metadata 160 */ 161#define COPY32(v, a) { \ 162 u_int32_t x = (v); \ 163 i386_copyin(&x, a, sizeof(x)); \ 164 a += sizeof(x); \ 165} 166 167#define MOD_STR(t, a, s) { \ 168 COPY32(t, a); \ 169 COPY32(strlen(s) + 1, a); \ 170 i386_copyin(s, a, strlen(s) + 1); \ 171 a += roundup(strlen(s) + 1, sizeof(u_int64_t));\ 172} 173 174#define MOD_NAME(a, s) MOD_STR(MODINFO_NAME, a, s) 175#define MOD_TYPE(a, s) MOD_STR(MODINFO_TYPE, a, s) 176#define MOD_ARGS(a, s) MOD_STR(MODINFO_ARGS, a, s) 177 178#define MOD_VAR(t, a, s) { \ 179 COPY32(t, a); \ 180 COPY32(sizeof(s), a); \ 181 i386_copyin(&s, a, sizeof(s)); \ 182 a += roundup(sizeof(s), sizeof(u_int64_t)); \ 183} 184 185#define MOD_ADDR(a, s) MOD_VAR(MODINFO_ADDR, a, s) 186#define MOD_SIZE(a, s) MOD_VAR(MODINFO_SIZE, a, s) 187 188#define MOD_METADATA(a, mm) { \ 189 COPY32(MODINFO_METADATA | mm->md_type, a); \ 190 COPY32(mm->md_size, a); \ 191 i386_copyin(mm->md_data, a, mm->md_size); \ 192 a += roundup(mm->md_size, sizeof(u_int64_t));\ 193} 194 195#define MOD_END(a) { \ 196 COPY32(MODINFO_END, a); \ 197 COPY32(0, a); \ 198} 199 200vm_offset_t 201bi_copymodules(vm_offset_t addr) 202{ 203 struct preloaded_file *fp; 204 struct file_metadata *md; 205 206 /* Start with the first module on the list, should be the kernel. */ 207 for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { 208 /* The name field must come first. */ 209 MOD_NAME(addr, fp->f_name); 210 MOD_TYPE(addr, fp->f_type); 211 if (fp->f_args) 212 MOD_ARGS(addr, fp->f_args); 213 MOD_ADDR(addr, fp->f_addr); 214 MOD_SIZE(addr, fp->f_size); 215 for (md = fp->f_metadata; md != NULL; md = md->md_next) { 216 if (!(md->md_type & MODINFOMD_NOCOPY)) 217 MOD_METADATA(addr, md); 218 } 219 } 220 MOD_END(addr); 221 return(addr); 222} 223 224/* 225 * Load the information expected by the kernel. 226 * 227 * - The kernel environment is copied into kernel space. 228 * - Module metadata are formatted and placed in kernel space. 229 */ 230int 231bi_load(struct preloaded_file *fp, uint64_t *bi_addr) 232{ 233 struct bootinfo bi; 234 struct preloaded_file *xp; 235 struct file_metadata *md; 236 struct devdesc *rootdev; 237 char *rootdevname; 238 vm_offset_t addr, ssym, esym; 239 240 bzero(&bi, sizeof(struct bootinfo)); 241 bi.bi_version = 1; 242// bi.bi_boothowto = bi_getboothowto(fp->f_args); 243 244 /* 245 * Allow the environment variable 'rootdev' to override the supplied 246 * device. This should perhaps go to MI code and/or have $rootdev 247 * tested/set by MI code before launching the kernel. 248 */ 249 rootdevname = getenv("rootdev"); 250 i386_getdev((void**)&rootdev, rootdevname, NULL); 251 if (rootdev != NULL) { 252 /* Try reading /etc/fstab to select the root device. */ 253 getrootmount(i386_fmtdev(rootdev)); 254 free(rootdev); 255 } 256 257 md = file_findmetadata(fp, MODINFOMD_SSYM); 258 ssym = (md != NULL) ? *((vm_offset_t *)&(md->md_data)) : 0; 259 md = file_findmetadata(fp, MODINFOMD_ESYM); 260 esym = (md != NULL) ? *((vm_offset_t *)&(md->md_data)) : 0; 261 if (ssym != 0 && esym != 0) { 262 bi.bi_symtab = ssym; 263 bi.bi_esymtab = esym; 264 } 265 266 /* Find the last module in the chain. */ 267 addr = 0; 268 for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { 269 if (addr < (xp->f_addr + xp->f_size)) 270 addr = xp->f_addr + xp->f_size; 271 } 272 273 addr = (addr + 15) & ~15; 274 275 /* Copy module list and metadata. */ 276 bi.bi_modulep = addr; 277 addr = bi_copymodules(addr); 278 if (addr <= bi.bi_modulep) { 279 addr = bi.bi_modulep; 280 bi.bi_modulep = 0; 281 } 282 283 addr = (addr + 15) & ~15; 284 285 /* Copy our environment. */ 286 bi.bi_envp = addr; 287 addr = bi_copyenv(addr); 288 if (addr <= bi.bi_envp) { 289 addr = bi.bi_envp; 290 bi.bi_envp = 0; 291 } 292 293 addr = (addr + PAGE_MASK) & ~PAGE_MASK; 294 bi.bi_kernend = addr; 295 296 return (ldr_bootinfo(&bi, bi_addr)); 297} 298