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/param.h> 17 #include <sys/gpt.h> 18 #include <sys/dirent.h> 19 #include <sys/reboot.h> 20 21 #include <machine/bootinfo.h> 22 #include <machine/elf.h> 23 #include <machine/pc/bios.h> 24 #include <machine/psl.h> 25 26 #include <stdarg.h> 27 28 #include <a.out.h> 29 30 #include <btxv86.h> 31 32 #include "stand.h" 33 34 #include "bootargs.h" 35 #include "lib.h" 36 #include "rbx.h" 37 #include "drv.h" 38 #include "cons.h" 39 #include "gpt.h" 40 #include "paths.h" 41 42 #define ARGS 0x900 43 #define NOPT 14 44 #define NDEV 3 45 #define MEM_BASE 0x12 46 #define MEM_EXT 0x15 47 48 #define DRV_HARD 0x80 49 #define DRV_MASK 0x7f 50 51 #define TYPE_AD 0 52 #define TYPE_DA 1 53 #define TYPE_MAXHARD TYPE_DA 54 #define TYPE_FD 2 55 56 extern uint32_t _end; 57 58 static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 59 static const unsigned char flags[NOPT] = { 60 RBX_DUAL, 61 RBX_SERIAL, 62 RBX_ASKNAME, 63 RBX_CDROM, 64 RBX_CONFIG, 65 RBX_KDB, 66 RBX_GDB, 67 RBX_MUTE, 68 RBX_NOINTR, 69 RBX_PAUSE, 70 RBX_QUIET, 71 RBX_DFLTROOT, 72 RBX_SINGLE, 73 RBX_VERBOSE 74 }; 75 uint32_t opts; 76 77 static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 78 static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 79 80 static struct dsk dsk; 81 static char kname[1024]; 82 static int comspeed = SIOSPD; 83 static struct bootinfo bootinfo; 84 85 static vm_offset_t high_heap_base; 86 static uint32_t bios_basemem, bios_extmem, high_heap_size; 87 88 static struct bios_smap smap; 89 90 /* 91 * The minimum amount of memory to reserve in bios_extmem for the heap. 92 */ 93 #define HEAP_MIN (3 * 1024 * 1024) 94 95 static char *heap_next; 96 static char *heap_end; 97 98 int main(void); 99 100 static void load(void); 101 static int parse_cmds(char *, int *); 102 103 static uint8_t ls, dsk_meta; 104 static uint32_t fs_off; 105 106 #include "cd9660read.c" 107 108 static inline int 109 xfsread(uint64_t inode, void *buf, size_t nbyte) 110 { 111 112 if ((size_t)cd9660_fsread(inode, buf, nbyte) != nbyte) { 113 printf("Invalid %s\n", "format"); 114 return (-1); 115 } 116 return (0); 117 } 118 119 static void 120 bios_getmem(void) 121 { 122 uint64_t size; 123 124 /* Parse system memory map */ 125 v86.ebx = 0; 126 do { 127 v86.ctl = V86_FLAGS; 128 v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/ 129 v86.eax = 0xe820; 130 v86.ecx = sizeof(struct bios_smap); 131 v86.edx = SMAP_SIG; 132 v86.es = VTOPSEG(&smap); 133 v86.edi = VTOPOFF(&smap); 134 v86int(); 135 if ((v86.efl & 1) || (v86.eax != SMAP_SIG)) 136 break; 137 /* look for a low-memory segment that's large enough */ 138 if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && 139 (smap.length >= (512 * 1024))) 140 bios_basemem = smap.length; 141 /* look for the first segment in 'extended' memory */ 142 if ((smap.type == SMAP_TYPE_MEMORY) && 143 (smap.base == 0x100000)) { 144 bios_extmem = smap.length; 145 } 146 147 /* 148 * Look for the largest segment in 'extended' memory beyond 149 * 1MB but below 4GB. 150 */ 151 if ((smap.type == SMAP_TYPE_MEMORY) && 152 (smap.base > 0x100000) && (smap.base < 0x100000000ull)) { 153 size = smap.length; 154 155 /* 156 * If this segment crosses the 4GB boundary, 157 * truncate it. 158 */ 159 if (smap.base + size > 0x100000000ull) 160 size = 0x100000000ull - smap.base; 161 162 if (size > high_heap_size) { 163 high_heap_size = size; 164 high_heap_base = smap.base; 165 } 166 } 167 } while (v86.ebx != 0); 168 169 /* Fall back to the old compatibility function for base memory */ 170 if (bios_basemem == 0) { 171 v86.ctl = 0; 172 v86.addr = 0x12; /* int 0x12 */ 173 v86int(); 174 175 bios_basemem = (v86.eax & 0xffff) * 1024; 176 } 177 178 /* 179 * Fall back through several compatibility functions for extended 180 * memory 181 */ 182 if (bios_extmem == 0) { 183 v86.ctl = V86_FLAGS; 184 v86.addr = 0x15; /* int 0x15 function 0xe801*/ 185 v86.eax = 0xe801; 186 v86int(); 187 if (!(v86.efl & 1)) { 188 bios_extmem = ((v86.ecx & 0xffff) + 189 ((v86.edx & 0xffff) * 64)) * 1024; 190 } 191 } 192 if (bios_extmem == 0) { 193 v86.ctl = 0; 194 v86.addr = 0x15; /* int 0x15 function 0x88*/ 195 v86.eax = 0x8800; 196 v86int(); 197 bios_extmem = (v86.eax & 0xffff) * 1024; 198 } 199 200 /* 201 * If we have extended memory and did not find a suitable heap 202 * region in the SMAP, use the last 3MB of 'extended' memory as a 203 * high heap candidate. 204 */ 205 if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { 206 high_heap_size = HEAP_MIN; 207 high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; 208 } 209 } 210 211 int 212 main(void) 213 { 214 char cmd[512], cmdtmp[512]; 215 ssize_t sz; 216 int autoboot, dskupdated; 217 uint64_t ino; 218 219 bios_getmem(); 220 221 if (high_heap_size > 0) { 222 heap_end = PTOV(high_heap_base + high_heap_size); 223 heap_next = PTOV(high_heap_base); 224 } else { 225 heap_next = (char *) 226 (roundup2(__base + (int32_t)&_end, 0x10000) - __base); 227 heap_end = (char *)PTOV(bios_basemem); 228 } 229 setheap(heap_next, heap_end); 230 231 v86.ctl = V86_FLAGS; 232 v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 233 dsk.drive = *(uint8_t *)PTOV(ARGS); 234 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 235 dsk.unit = dsk.drive & DRV_MASK; 236 dsk.part = -1; 237 dsk.start = 0; 238 bootinfo.bi_version = BOOTINFO_VERSION; 239 bootinfo.bi_size = sizeof(bootinfo); 240 bootinfo.bi_basemem = bios_basemem / 1024; 241 bootinfo.bi_extmem = bios_extmem / 1024; 242 bootinfo.bi_memsizes_valid++; 243 bootinfo.bi_bios_dev = dsk.drive; 244 245 autoboot = 1; 246 *cmd = '\0'; 247 248 for (;;) { 249 *kname = '\0'; 250 if ((ino = cd9660_lookup(PATH_CONFIG)) || 251 (ino = cd9660_lookup(PATH_DOTCONFIG))) { 252 sz = cd9660_fsread(ino, cmd, sizeof(cmd) - 1); 253 cmd[(sz < 0) ? 0 : sz] = '\0'; 254 } 255 if (*cmd != '\0') { 256 memcpy(cmdtmp, cmd, sizeof(cmdtmp)); 257 if (parse_cmds(cmdtmp, &dskupdated)) 258 break; 259 if (!OPT_CHECK(RBX_QUIET)) 260 printf("%s: %s", PATH_CONFIG, cmd); 261 *cmd = '\0'; 262 } 263 264 if (autoboot && keyhit(3)) { 265 if (*kname == '\0') 266 memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); 267 break; 268 } 269 autoboot = 0; 270 271 /* 272 * Try to exec stage 3 boot loader. If interrupted by a 273 * keypress, or in case of failure, try to load a kernel 274 * directly instead. 275 */ 276 if (*kname != '\0') 277 load(); 278 memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); 279 load(); 280 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 281 load(); 282 dsk_meta = 0; 283 } 284 285 /* Present the user with the boot2 prompt. */ 286 287 for (;;) { 288 if (!OPT_CHECK(RBX_QUIET)) { 289 printf("\nFreeBSD/x86 boot\n" 290 "Default: %u:%s(%up%u)%s\n" 291 "boot: ", 292 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 293 dsk.part, kname); 294 } 295 if (ioctrl & IO_SERIAL) 296 sio_flush(); 297 *cmd = '\0'; 298 if (keyhit(0)) 299 getstr(cmd, sizeof(cmd)); 300 else if (!OPT_CHECK(RBX_QUIET)) 301 putchar('\n'); 302 if (parse_cmds(cmd, &dskupdated)) { 303 putchar('\a'); 304 continue; 305 } 306 load(); 307 } 308 /* NOTREACHED */ 309 } 310 311 /* Needed so btxld can link us properly; do not remove. */ 312 void 313 exit(int x) 314 { 315 316 while (1); 317 __unreachable(); 318 } 319 320 static void 321 load(void) 322 { 323 union { 324 struct exec ex; 325 Elf32_Ehdr eh; 326 } hdr; 327 static Elf32_Phdr ep[2]; 328 static Elf32_Shdr es[2]; 329 caddr_t p; 330 uint64_t ino; 331 uint32_t addr, x; 332 int fmt, i, j; 333 334 if (!(ino = cd9660_lookup(kname))) { 335 if (!ls) { 336 printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, 337 kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], 338 dsk.unit, 339 dsk.part); 340 } 341 return; 342 } 343 if (xfsread(ino, &hdr, sizeof(hdr))) 344 return; 345 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 346 fmt = 0; 347 else if (IS_ELF(hdr.eh)) 348 fmt = 1; 349 else { 350 printf("Invalid %s\n", "format"); 351 return; 352 } 353 if (fmt == 0) { 354 addr = hdr.ex.a_entry & 0xffffff; 355 p = PTOV(addr); 356 fs_off = PAGE_SIZE; 357 if (xfsread(ino, p, hdr.ex.a_text)) 358 return; 359 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 360 if (xfsread(ino, p, hdr.ex.a_data)) 361 return; 362 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 363 bootinfo.bi_symtab = VTOP(p); 364 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 365 p += sizeof(hdr.ex.a_syms); 366 if (hdr.ex.a_syms) { 367 if (xfsread(ino, p, hdr.ex.a_syms)) 368 return; 369 p += hdr.ex.a_syms; 370 if (xfsread(ino, p, sizeof(int))) 371 return; 372 x = *(uint32_t *)p; 373 p += sizeof(int); 374 x -= sizeof(int); 375 if (xfsread(ino, p, x)) 376 return; 377 p += x; 378 } 379 } else { 380 fs_off = hdr.eh.e_phoff; 381 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 382 if (xfsread(ino, ep + j, sizeof(ep[0]))) 383 return; 384 if (ep[j].p_type == PT_LOAD) 385 j++; 386 } 387 for (i = 0; i < 2; i++) { 388 p = PTOV(ep[i].p_paddr & 0xffffff); 389 fs_off = ep[i].p_offset; 390 if (xfsread(ino, p, ep[i].p_filesz)) 391 return; 392 } 393 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 394 bootinfo.bi_symtab = VTOP(p); 395 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 396 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 397 (hdr.eh.e_shstrndx + 1); 398 if (xfsread(ino, &es, sizeof(es))) 399 return; 400 for (i = 0; i < 2; i++) { 401 memcpy(p, &es[i].sh_size, 402 sizeof(es[i].sh_size)); 403 p += sizeof(es[i].sh_size); 404 fs_off = es[i].sh_offset; 405 if (xfsread(ino, p, es[i].sh_size)) 406 return; 407 p += es[i].sh_size; 408 } 409 } 410 addr = hdr.eh.e_entry & 0xffffff; 411 } 412 bootinfo.bi_esymtab = VTOP(p); 413 bootinfo.bi_kernelname = VTOP(kname); 414 bootinfo.bi_bios_dev = dsk.drive; 415 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 416 MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.unit, 0), 417 0, 0, 0, VTOP(&bootinfo)); 418 } 419 420 static int 421 parse_cmds(char *cmdstr, int *dskupdated) 422 { 423 char *arg; 424 char *ep, *p, *q; 425 const char *cp; 426 unsigned int drv; 427 int c, i, j; 428 429 arg = cmdstr; 430 *dskupdated = 0; 431 while ((c = *arg++)) { 432 if (c == ' ' || c == '\t' || c == '\n') 433 continue; 434 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 435 ep = p; 436 if (*p) 437 *p++ = 0; 438 if (c == '-') { 439 while ((c = *arg++)) { 440 if (c == 'P') { 441 if (*(uint8_t *)PTOV(0x496) & 0x10) { 442 cp = "yes"; 443 } else { 444 opts |= OPT_SET(RBX_DUAL) | 445 OPT_SET(RBX_SERIAL); 446 cp = "no"; 447 } 448 printf("Keyboard: %s\n", cp); 449 continue; 450 } else if (c == 'S') { 451 j = 0; 452 while ((unsigned int)(i = *arg++ - '0') 453 <= 9) 454 j = j * 10 + i; 455 if (j > 0 && i == -'0') { 456 comspeed = j; 457 break; 458 } 459 /* 460 * Fall through to error below 461 * ('S' not in optstr[]). 462 */ 463 } 464 for (i = 0; c != optstr[i]; i++) 465 if (i == NOPT - 1) 466 return (-1); 467 opts ^= OPT_SET(flags[i]); 468 } 469 ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 470 OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; 471 if (ioctrl & IO_SERIAL) { 472 if (sio_init(115200 / comspeed) != 0) 473 ioctrl &= ~IO_SERIAL; 474 } 475 } else { 476 for (q = arg--; *q && *q != '('; q++); 477 if (*q) { 478 drv = -1; 479 if (arg[1] == ':') { 480 drv = *arg - '0'; 481 if (drv > 9) 482 return (-1); 483 arg += 2; 484 } 485 if (q - arg != 2) 486 return (-1); 487 for (i = 0; arg[0] != dev_nm[i][0] || 488 arg[1] != dev_nm[i][1]; i++) 489 if (i == NDEV - 1) 490 return (-1); 491 dsk.type = i; 492 arg += 3; 493 dsk.unit = *arg - '0'; 494 if (arg[1] != 'p' || dsk.unit > 9) 495 return (-1); 496 arg += 2; 497 dsk.part = *arg - '0'; 498 if (dsk.part < 1 || dsk.part > 9) 499 return (-1); 500 arg++; 501 if (arg[0] != ')') 502 return (-1); 503 arg++; 504 if (drv == -1) 505 drv = dsk.unit; 506 dsk.drive = (dsk.type <= TYPE_MAXHARD 507 ? DRV_HARD : 0) + drv; 508 *dskupdated = 1; 509 } 510 if ((i = ep - arg)) { 511 if ((size_t)i >= sizeof(kname)) 512 return (-1); 513 memcpy(kname, arg, i + 1); 514 } 515 } 516 arg = p; 517 } 518 return (0); 519 } 520