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