gptboot.c revision 242562
1/*- 2 * Copyright (c) 1998 Robert Nordier 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are freely 6 * permitted provided that the above copyright notice and this 7 * paragraph and the following disclaimer are duplicated in all 8 * such forms. 9 * 10 * This software is provided "AS IS" and without any express or 11 * implied warranties, including, without limitation, the implied 12 * warranties of merchantability and fitness for a particular 13 * purpose. 14 */ 15 16#include <sys/cdefs.h> 17__FBSDID("$FreeBSD: stable/9/sys/boot/i386/gptboot/gptboot.c 242562 2012-11-04 13:37:33Z avg $"); 18 19#include <sys/param.h> 20#include <sys/gpt.h> 21#include <sys/dirent.h> 22#include <sys/reboot.h> 23 24#include <machine/bootinfo.h> 25#include <machine/elf.h> 26#include <machine/psl.h> 27 28#include <stdarg.h> 29 30#include <a.out.h> 31 32#include <btxv86.h> 33 34#include "lib.h" 35#include "rbx.h" 36#include "drv.h" 37#include "util.h" 38#include "cons.h" 39#include "gpt.h" 40 41#define PATH_DOTCONFIG "/boot.config" 42#define PATH_CONFIG "/boot/config" 43#define PATH_BOOT3 "/boot/loader" 44#define PATH_KERNEL "/boot/kernel/kernel" 45 46#define ARGS 0x900 47#define NOPT 14 48#define NDEV 3 49#define MEM_BASE 0x12 50#define MEM_EXT 0x15 51 52#define DRV_HARD 0x80 53#define DRV_MASK 0x7f 54 55#define TYPE_AD 0 56#define TYPE_DA 1 57#define TYPE_MAXHARD TYPE_DA 58#define TYPE_FD 2 59 60extern uint32_t _end; 61 62static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; 63static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 64static const unsigned char flags[NOPT] = { 65 RBX_DUAL, 66 RBX_SERIAL, 67 RBX_ASKNAME, 68 RBX_CDROM, 69 RBX_CONFIG, 70 RBX_KDB, 71 RBX_GDB, 72 RBX_MUTE, 73 RBX_NOINTR, 74 RBX_PAUSE, 75 RBX_QUIET, 76 RBX_DFLTROOT, 77 RBX_SINGLE, 78 RBX_VERBOSE 79}; 80uint32_t opts; 81 82static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 83static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 84 85static struct dsk dsk; 86static char kname[1024]; 87static int comspeed = SIOSPD; 88static struct bootinfo bootinfo; 89 90void exit(int); 91static void load(void); 92static int parse(char *, int *); 93static int xfsread(ino_t, void *, size_t); 94static int dskread(void *, daddr_t, unsigned); 95static uint32_t memsize(void); 96 97#include "ufsread.c" 98 99static inline int 100xfsread(ino_t inode, void *buf, size_t nbyte) 101{ 102 103 if ((size_t)fsread(inode, buf, nbyte) != nbyte) { 104 printf("Invalid %s\n", "format"); 105 return (-1); 106 } 107 return (0); 108} 109 110static inline uint32_t 111memsize(void) 112{ 113 114 v86.addr = MEM_EXT; 115 v86.eax = 0x8800; 116 v86int(); 117 return (v86.eax); 118} 119 120static int 121gptinit(void) 122{ 123 124 if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) { 125 printf("%s: unable to load GPT\n", BOOTPROG); 126 return (-1); 127 } 128 if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) { 129 printf("%s: no UFS partition was found\n", BOOTPROG); 130 return (-1); 131 } 132 dsk_meta = 0; 133 return (0); 134} 135 136int 137main(void) 138{ 139 char cmd[512], cmdtmp[512]; 140 int autoboot, dskupdated; 141 ino_t ino; 142 143 dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 144 v86.ctl = V86_FLAGS; 145 v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 146 dsk.drive = *(uint8_t *)PTOV(ARGS); 147 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 148 dsk.unit = dsk.drive & DRV_MASK; 149 dsk.part = -1; 150 dsk.start = 0; 151 bootinfo.bi_version = BOOTINFO_VERSION; 152 bootinfo.bi_size = sizeof(bootinfo); 153 bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ 154 bootinfo.bi_extmem = memsize(); 155 bootinfo.bi_memsizes_valid++; 156 157 /* Process configuration file */ 158 159 if (gptinit() != 0) 160 return (-1); 161 162 autoboot = 1; 163 *cmd = '\0'; 164 165 for (;;) { 166 *kname = '\0'; 167 if ((ino = lookup(PATH_CONFIG)) || 168 (ino = lookup(PATH_DOTCONFIG))) 169 fsread(ino, cmd, sizeof(cmd)); 170 171 if (*cmd != '\0') { 172 memcpy(cmdtmp, cmd, sizeof(cmdtmp)); 173 if (parse(cmdtmp, &dskupdated)) 174 break; 175 if (dskupdated && gptinit() != 0) 176 break; 177 if (!OPT_CHECK(RBX_QUIET)) 178 printf("%s: %s", PATH_CONFIG, cmd); 179 *cmd = '\0'; 180 } 181 182 if (autoboot && keyhit(3)) { 183 if (*kname == '\0') 184 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 185 break; 186 } 187 autoboot = 0; 188 189 /* 190 * Try to exec stage 3 boot loader. If interrupted by a 191 * keypress, or in case of failure, try to load a kernel 192 * directly instead. 193 */ 194 if (*kname != '\0') 195 load(); 196 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 197 load(); 198 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 199 load(); 200 gptbootfailed(&dsk); 201 if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1) 202 break; 203 dsk_meta = 0; 204 } 205 206 /* Present the user with the boot2 prompt. */ 207 208 for (;;) { 209 if (!OPT_CHECK(RBX_QUIET)) { 210 printf("\nFreeBSD/x86 boot\n" 211 "Default: %u:%s(%up%u)%s\n" 212 "boot: ", 213 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 214 dsk.part, kname); 215 } 216 if (ioctrl & IO_SERIAL) 217 sio_flush(); 218 *cmd = '\0'; 219 if (keyhit(0)) 220 getstr(cmd, sizeof(cmd)); 221 else if (!OPT_CHECK(RBX_QUIET)) 222 putchar('\n'); 223 if (parse(cmd, &dskupdated)) { 224 putchar('\a'); 225 continue; 226 } 227 if (dskupdated && gptinit() != 0) 228 continue; 229 load(); 230 } 231 /* NOTREACHED */ 232} 233 234/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 235void 236exit(int x) 237{ 238} 239 240static void 241load(void) 242{ 243 union { 244 struct exec ex; 245 Elf32_Ehdr eh; 246 } hdr; 247 static Elf32_Phdr ep[2]; 248 static Elf32_Shdr es[2]; 249 caddr_t p; 250 ino_t ino; 251 uint32_t addr, x; 252 int fmt, i, j; 253 254 if (!(ino = lookup(kname))) { 255 if (!ls) { 256 printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, 257 kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 258 dsk.part); 259 } 260 return; 261 } 262 if (xfsread(ino, &hdr, sizeof(hdr))) 263 return; 264 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 265 fmt = 0; 266 else if (IS_ELF(hdr.eh)) 267 fmt = 1; 268 else { 269 printf("Invalid %s\n", "format"); 270 return; 271 } 272 if (fmt == 0) { 273 addr = hdr.ex.a_entry & 0xffffff; 274 p = PTOV(addr); 275 fs_off = PAGE_SIZE; 276 if (xfsread(ino, p, hdr.ex.a_text)) 277 return; 278 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 279 if (xfsread(ino, p, hdr.ex.a_data)) 280 return; 281 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 282 bootinfo.bi_symtab = VTOP(p); 283 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 284 p += sizeof(hdr.ex.a_syms); 285 if (hdr.ex.a_syms) { 286 if (xfsread(ino, p, hdr.ex.a_syms)) 287 return; 288 p += hdr.ex.a_syms; 289 if (xfsread(ino, p, sizeof(int))) 290 return; 291 x = *(uint32_t *)p; 292 p += sizeof(int); 293 x -= sizeof(int); 294 if (xfsread(ino, p, x)) 295 return; 296 p += x; 297 } 298 } else { 299 fs_off = hdr.eh.e_phoff; 300 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 301 if (xfsread(ino, ep + j, sizeof(ep[0]))) 302 return; 303 if (ep[j].p_type == PT_LOAD) 304 j++; 305 } 306 for (i = 0; i < 2; i++) { 307 p = PTOV(ep[i].p_paddr & 0xffffff); 308 fs_off = ep[i].p_offset; 309 if (xfsread(ino, p, ep[i].p_filesz)) 310 return; 311 } 312 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 313 bootinfo.bi_symtab = VTOP(p); 314 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 315 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 316 (hdr.eh.e_shstrndx + 1); 317 if (xfsread(ino, &es, sizeof(es))) 318 return; 319 for (i = 0; i < 2; i++) { 320 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 321 p += sizeof(es[i].sh_size); 322 fs_off = es[i].sh_offset; 323 if (xfsread(ino, p, es[i].sh_size)) 324 return; 325 p += es[i].sh_size; 326 } 327 } 328 addr = hdr.eh.e_entry & 0xffffff; 329 } 330 bootinfo.bi_esymtab = VTOP(p); 331 bootinfo.bi_kernelname = VTOP(kname); 332 bootinfo.bi_bios_dev = dsk.drive; 333 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 334 MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff), 335 0, 0, 0, VTOP(&bootinfo)); 336} 337 338static int 339parse(char *cmdstr, int *dskupdated) 340{ 341 char *arg = cmdstr; 342 char *ep, *p, *q; 343 const char *cp; 344 unsigned int drv; 345 int c, i, j; 346 347 *dskupdated = 0; 348 while ((c = *arg++)) { 349 if (c == ' ' || c == '\t' || c == '\n') 350 continue; 351 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 352 ep = p; 353 if (*p) 354 *p++ = 0; 355 if (c == '-') { 356 while ((c = *arg++)) { 357 if (c == 'P') { 358 if (*(uint8_t *)PTOV(0x496) & 0x10) { 359 cp = "yes"; 360 } else { 361 opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); 362 cp = "no"; 363 } 364 printf("Keyboard: %s\n", cp); 365 continue; 366 } else if (c == 'S') { 367 j = 0; 368 while ((unsigned int)(i = *arg++ - '0') <= 9) 369 j = j * 10 + i; 370 if (j > 0 && i == -'0') { 371 comspeed = j; 372 break; 373 } 374 /* Fall through to error below ('S' not in optstr[]). */ 375 } 376 for (i = 0; c != optstr[i]; i++) 377 if (i == NOPT - 1) 378 return -1; 379 opts ^= OPT_SET(flags[i]); 380 } 381 ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 382 OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; 383 if (ioctrl & IO_SERIAL) { 384 if (sio_init(115200 / comspeed) != 0) 385 ioctrl &= ~IO_SERIAL; 386 } 387 } else { 388 for (q = arg--; *q && *q != '('; q++); 389 if (*q) { 390 drv = -1; 391 if (arg[1] == ':') { 392 drv = *arg - '0'; 393 if (drv > 9) 394 return (-1); 395 arg += 2; 396 } 397 if (q - arg != 2) 398 return -1; 399 for (i = 0; arg[0] != dev_nm[i][0] || 400 arg[1] != dev_nm[i][1]; i++) 401 if (i == NDEV - 1) 402 return -1; 403 dsk.type = i; 404 arg += 3; 405 dsk.unit = *arg - '0'; 406 if (arg[1] != 'p' || dsk.unit > 9) 407 return -1; 408 arg += 2; 409 dsk.part = *arg - '0'; 410 if (dsk.part < 1 || dsk.part > 9) 411 return -1; 412 arg++; 413 if (arg[0] != ')') 414 return -1; 415 arg++; 416 if (drv == -1) 417 drv = dsk.unit; 418 dsk.drive = (dsk.type <= TYPE_MAXHARD 419 ? DRV_HARD : 0) + drv; 420 *dskupdated = 1; 421 } 422 if ((i = ep - arg)) { 423 if ((size_t)i >= sizeof(kname)) 424 return -1; 425 memcpy(kname, arg, i + 1); 426 } 427 } 428 arg = p; 429 } 430 return 0; 431} 432 433static int 434dskread(void *buf, daddr_t lba, unsigned nblk) 435{ 436 437 return drvread(&dsk, buf, lba + dsk.start, nblk); 438} 439