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