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 #include <sys/boot.h> 37 38 #include <efi.h> 39 #include <efilib.h> 40 41 #include "bootstrap.h" 42 #include "libi386.h" 43 #include <machine/bootinfo.h> 44 45 static const char howto_switches[] = "aCdrgDmphsv"; 46 static int howto_masks[] = { 47 RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE, 48 RB_MUTE, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE 49 }; 50 51 int 52 bi_getboothowto(char *kargs) 53 { 54 const char *sw; 55 char *opts; 56 int howto, i; 57 58 howto = 0; 59 60 /* Get the boot options from the environment first. */ 61 for (i = 0; howto_names[i].ev != NULL; i++) { 62 if (getenv(howto_names[i].ev) != NULL) 63 howto |= howto_names[i].mask; 64 } 65 66 /* Parse kargs */ 67 if (kargs == NULL) 68 return (howto); 69 70 opts = strchr(kargs, '-'); 71 while (opts != NULL) { 72 while (*(++opts) != '\0') { 73 sw = strchr(howto_switches, *opts); 74 if (sw == NULL) 75 break; 76 howto |= howto_masks[sw - howto_switches]; 77 } 78 opts = strchr(opts, '-'); 79 } 80 81 return (howto); 82 } 83 84 /* 85 * Copy the environment into the load area starting at (addr). 86 * Each variable is formatted as <name>=<value>, with a single nul 87 * separating each variable, and a double nul terminating the environment. 88 */ 89 vm_offset_t 90 bi_copyenv(vm_offset_t start) 91 { 92 struct env_var *ep; 93 vm_offset_t addr, last; 94 size_t len; 95 96 addr = last = start; 97 98 /* Traverse the environment. */ 99 for (ep = environ; ep != NULL; ep = ep->ev_next) { 100 len = strlen(ep->ev_name); 101 if (i386_copyin(ep->ev_name, addr, len) != len) 102 break; 103 addr += len; 104 if (i386_copyin("=", addr, 1) != 1) 105 break; 106 addr++; 107 if (ep->ev_value != NULL) { 108 len = strlen(ep->ev_value); 109 if (i386_copyin(ep->ev_value, addr, len) != len) 110 break; 111 addr += len; 112 } 113 if (i386_copyin("", addr, 1) != 1) 114 break; 115 last = ++addr; 116 } 117 118 if (i386_copyin("", last++, 1) != 1) 119 last = start; 120 return(last); 121 } 122 123 /* 124 * Copy module-related data into the load area, where it can be 125 * used as a directory for loaded modules. 126 * 127 * Module data is presented in a self-describing format. Each datum 128 * is preceded by a 32-bit identifier and a 32-bit size field. 129 * 130 * Currently, the following data are saved: 131 * 132 * MOD_NAME (variable) module name (string) 133 * MOD_TYPE (variable) module type (string) 134 * MOD_ARGS (variable) module parameters (string) 135 * MOD_ADDR sizeof(vm_offset_t) module load address 136 * MOD_SIZE sizeof(size_t) module size 137 * MOD_METADATA (variable) type-specific metadata 138 */ 139 #define COPY32(v, a) { \ 140 u_int32_t x = (v); \ 141 i386_copyin(&x, a, sizeof(x)); \ 142 a += sizeof(x); \ 143 } 144 145 #define MOD_STR(t, a, s) { \ 146 COPY32(t, a); \ 147 COPY32(strlen(s) + 1, a); \ 148 i386_copyin(s, a, strlen(s) + 1); \ 149 a += roundup(strlen(s) + 1, sizeof(u_int64_t));\ 150 } 151 152 #define MOD_NAME(a, s) MOD_STR(MODINFO_NAME, a, s) 153 #define MOD_TYPE(a, s) MOD_STR(MODINFO_TYPE, a, s) 154 #define MOD_ARGS(a, s) MOD_STR(MODINFO_ARGS, a, s) 155 156 #define MOD_VAR(t, a, s) { \ 157 COPY32(t, a); \ 158 COPY32(sizeof(s), a); \ 159 i386_copyin(&s, a, sizeof(s)); \ 160 a += roundup(sizeof(s), sizeof(u_int64_t)); \ 161 } 162 163 #define MOD_ADDR(a, s) MOD_VAR(MODINFO_ADDR, a, s) 164 #define MOD_SIZE(a, s) MOD_VAR(MODINFO_SIZE, a, s) 165 166 #define MOD_METADATA(a, mm) { \ 167 COPY32(MODINFO_METADATA | mm->md_type, a); \ 168 COPY32(mm->md_size, a); \ 169 i386_copyin(mm->md_data, a, mm->md_size); \ 170 a += roundup(mm->md_size, sizeof(u_int64_t));\ 171 } 172 173 #define MOD_END(a) { \ 174 COPY32(MODINFO_END, a); \ 175 COPY32(0, a); \ 176 } 177 178 vm_offset_t 179 bi_copymodules(vm_offset_t addr) 180 { 181 struct preloaded_file *fp; 182 struct file_metadata *md; 183 184 /* Start with the first module on the list, should be the kernel. */ 185 for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { 186 /* The name field must come first. */ 187 MOD_NAME(addr, fp->f_name); 188 MOD_TYPE(addr, fp->f_type); 189 if (fp->f_args) 190 MOD_ARGS(addr, fp->f_args); 191 MOD_ADDR(addr, fp->f_addr); 192 MOD_SIZE(addr, fp->f_size); 193 for (md = fp->f_metadata; md != NULL; md = md->md_next) { 194 if (!(md->md_type & MODINFOMD_NOCOPY)) 195 MOD_METADATA(addr, md); 196 } 197 } 198 MOD_END(addr); 199 return(addr); 200 } 201 202 /* 203 * Load the information expected by the kernel. 204 * 205 * - The kernel environment is copied into kernel space. 206 * - Module metadata are formatted and placed in kernel space. 207 */ 208 int 209 bi_load(struct preloaded_file *fp, uint64_t *bi_addr) 210 { 211 struct bootinfo bi; 212 struct preloaded_file *xp; 213 struct file_metadata *md; 214 struct devdesc *rootdev; 215 char *rootdevname; 216 vm_offset_t addr, ssym, esym; 217 218 bzero(&bi, sizeof(struct bootinfo)); 219 bi.bi_version = 1; 220 // bi.bi_boothowto = bi_getboothowto(fp->f_args); 221 222 /* 223 * Allow the environment variable 'rootdev' to override the supplied 224 * device. This should perhaps go to MI code and/or have $rootdev 225 * tested/set by MI code before launching the kernel. 226 */ 227 rootdevname = getenv("rootdev"); 228 i386_getdev((void**)&rootdev, rootdevname, NULL); 229 if (rootdev != NULL) { 230 /* Try reading /etc/fstab to select the root device. */ 231 getrootmount(i386_fmtdev(rootdev)); 232 free(rootdev); 233 } 234 235 md = file_findmetadata(fp, MODINFOMD_SSYM); 236 ssym = (md != NULL) ? *((vm_offset_t *)&(md->md_data)) : 0; 237 md = file_findmetadata(fp, MODINFOMD_ESYM); 238 esym = (md != NULL) ? *((vm_offset_t *)&(md->md_data)) : 0; 239 if (ssym != 0 && esym != 0) { 240 bi.bi_symtab = ssym; 241 bi.bi_esymtab = esym; 242 } 243 244 /* Find the last module in the chain. */ 245 addr = 0; 246 for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { 247 if (addr < (xp->f_addr + xp->f_size)) 248 addr = xp->f_addr + xp->f_size; 249 } 250 251 addr = (addr + 15) & ~15; 252 253 /* Copy module list and metadata. */ 254 bi.bi_modulep = addr; 255 addr = bi_copymodules(addr); 256 if (addr <= bi.bi_modulep) { 257 addr = bi.bi_modulep; 258 bi.bi_modulep = 0; 259 } 260 261 addr = (addr + 15) & ~15; 262 263 /* Copy our environment. */ 264 bi.bi_envp = addr; 265 addr = bi_copyenv(addr); 266 if (addr <= bi.bi_envp) { 267 addr = bi.bi_envp; 268 bi.bi_envp = 0; 269 } 270 271 addr = (addr + PAGE_MASK) & ~PAGE_MASK; 272 bi.bi_kernend = addr; 273 274 return (ldr_bootinfo(&bi, bi_addr)); 275 } 276