1249259Sdim/*- 2249259Sdim * Copyright (c) 1998 Robert Nordier 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms are freely 6249259Sdim * permitted provided that the above copyright notice and this 7249259Sdim * paragraph and the following disclaimer are duplicated in all 8249259Sdim * such forms. 9249259Sdim * 10249259Sdim * This software is provided "AS IS" and without any express or 11249259Sdim * implied warranties, including, without limitation, the implied 12249259Sdim * warranties of merchantability and fitness for a particular 13249259Sdim * purpose. 14249259Sdim */ 15249259Sdim 16360784Sdim#include <sys/cdefs.h> 17249259Sdim__FBSDID("$FreeBSD: stable/11/stand/i386/isoboot/isoboot.c 337816 2018-08-14 19:44:36Z kevans $"); 18249259Sdim 19249259Sdim#include <sys/param.h> 20276479Sdim#include <sys/gpt.h> 21249259Sdim#include <sys/dirent.h> 22321369Sdim#include <sys/reboot.h> 23276479Sdim 24249259Sdim#include <machine/bootinfo.h> 25249259Sdim#include <machine/elf.h> 26288943Sdim#include <machine/pc/bios.h> 27249259Sdim#include <machine/psl.h> 28249259Sdim 29288943Sdim#include <stdarg.h> 30276479Sdim 31249259Sdim#include <a.out.h> 32360784Sdim 33249259Sdim#include <btxv86.h> 34249259Sdim 35249259Sdim#include "stand.h" 36288943Sdim 37249259Sdim#include "bootargs.h" 38309124Sdim#include "lib.h" 39249259Sdim#include "rbx.h" 40249259Sdim#include "drv.h" 41341825Sdim#include "cons.h" 42341825Sdim#include "gpt.h" 43341825Sdim#include "paths.h" 44341825Sdim 45249259Sdim#define ARGS 0x900 46249259Sdim#define NOPT 14 47249259Sdim#define NDEV 3 48249259Sdim#define MEM_BASE 0x12 49249259Sdim#define MEM_EXT 0x15 50276479Sdim 51249259Sdim#define DRV_HARD 0x80 52249259Sdim#define DRV_MASK 0x7f 53249259Sdim 54288943Sdim#define TYPE_AD 0 55288943Sdim#define TYPE_DA 1 56288943Sdim#define TYPE_MAXHARD TYPE_DA 57327952Sdim#define TYPE_FD 2 58249259Sdim 59249259Sdimextern uint32_t _end; 60249259Sdim 61353358Sdimstatic const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 62353358Sdimstatic const unsigned char flags[NOPT] = { 63249259Sdim RBX_DUAL, 64249259Sdim RBX_SERIAL, 65249259Sdim RBX_ASKNAME, 66327952Sdim RBX_CDROM, 67249259Sdim RBX_CONFIG, 68249259Sdim RBX_KDB, 69321369Sdim RBX_GDB, 70309124Sdim RBX_MUTE, 71249259Sdim RBX_NOINTR, 72249259Sdim RBX_PAUSE, 73249259Sdim RBX_QUIET, 74249259Sdim RBX_DFLTROOT, 75249259Sdim RBX_SINGLE, 76249259Sdim RBX_VERBOSE 77280031Sdim}; 78280031Sdimuint32_t opts; 79249259Sdim 80249259Sdimstatic const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 81249259Sdimstatic const unsigned char dev_maj[NDEV] = {30, 4, 2}; 82249259Sdim 83288943Sdimstatic struct dsk dsk; 84288943Sdimstatic char kname[1024]; 85249259Sdimstatic int comspeed = SIOSPD; 86249259Sdimstatic struct bootinfo bootinfo; 87249259Sdim 88288943Sdimstatic vm_offset_t high_heap_base; 89288943Sdimstatic uint32_t bios_basemem, bios_extmem, high_heap_size; 90249259Sdim 91249259Sdimstatic struct bios_smap smap; 92249259Sdim 93249259Sdim/* 94249259Sdim * The minimum amount of memory to reserve in bios_extmem for the heap. 95249259Sdim */ 96280031Sdim#define HEAP_MIN (3 * 1024 * 1024) 97280031Sdim 98280031Sdimstatic char *heap_next; 99321369Sdimstatic char *heap_end; 100321369Sdim 101321369Sdimint main(void); 102321369Sdim 103321369Sdimstatic void load(void); 104321369Sdimstatic int parse_cmds(char *, int *); 105321369Sdim 106321369Sdimstatic uint8_t ls, dsk_meta; 107321369Sdimstatic uint32_t fs_off; 108321369Sdim 109321369Sdim#include "cd9660read.c" 110321369Sdim 111321369Sdimstatic inline int 112321369Sdimxfsread(uint64_t inode, void *buf, size_t nbyte) 113321369Sdim{ 114321369Sdim 115321369Sdim if ((size_t)cd9660_fsread(inode, buf, nbyte) != nbyte) { 116321369Sdim printf("Invalid %s\n", "format"); 117321369Sdim return (-1); 118321369Sdim } 119321369Sdim return (0); 120321369Sdim} 121321369Sdim 122321369Sdimstatic void 123321369Sdimbios_getmem(void) 124321369Sdim{ 125280031Sdim uint64_t size; 126280031Sdim 127280031Sdim /* Parse system memory map */ 128249259Sdim v86.ebx = 0; 129280031Sdim do { 130249259Sdim v86.ctl = V86_FLAGS; 131249259Sdim v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/ 132249259Sdim v86.eax = 0xe820; 133344779Sdim v86.ecx = sizeof(struct bios_smap); 134249259Sdim v86.edx = SMAP_SIG; 135249259Sdim v86.es = VTOPSEG(&smap); 136249259Sdim v86.edi = VTOPOFF(&smap); 137344779Sdim v86int(); 138249259Sdim if ((v86.efl & 1) || (v86.eax != SMAP_SIG)) 139249259Sdim break; 140249259Sdim /* look for a low-memory segment that's large enough */ 141261991Sdim if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && 142261991Sdim (smap.length >= (512 * 1024))) 143261991Sdim bios_basemem = smap.length; 144261991Sdim /* look for the first segment in 'extended' memory */ 145261991Sdim if ((smap.type == SMAP_TYPE_MEMORY) && 146261991Sdim (smap.base == 0x100000)) { 147261991Sdim bios_extmem = smap.length; 148276479Sdim } 149261991Sdim 150261991Sdim /* 151314564Sdim * Look for the largest segment in 'extended' memory beyond 152249259Sdim * 1MB but below 4GB. 153261991Sdim */ 154314564Sdim if ((smap.type == SMAP_TYPE_MEMORY) && 155249259Sdim (smap.base > 0x100000) && (smap.base < 0x100000000ull)) { 156249259Sdim size = smap.length; 157249259Sdim 158249259Sdim /* 159249259Sdim * If this segment crosses the 4GB boundary, 160249259Sdim * truncate it. 161249259Sdim */ 162249259Sdim if (smap.base + size > 0x100000000ull) 163249259Sdim size = 0x100000000ull - smap.base; 164249259Sdim 165249259Sdim if (size > high_heap_size) { 166276479Sdim high_heap_size = size; 167249259Sdim high_heap_base = smap.base; 168249259Sdim } 169249259Sdim } 170314564Sdim } while (v86.ebx != 0); 171249259Sdim 172249259Sdim /* Fall back to the old compatibility function for base memory */ 173314564Sdim if (bios_basemem == 0) { 174249259Sdim v86.ctl = 0; 175249259Sdim v86.addr = 0x12; /* int 0x12 */ 176249259Sdim v86int(); 177249259Sdim 178249259Sdim bios_basemem = (v86.eax & 0xffff) * 1024; 179314564Sdim } 180280031Sdim 181249259Sdim /* 182249259Sdim * Fall back through several compatibility functions for extended 183249259Sdim * memory 184249259Sdim */ 185249259Sdim if (bios_extmem == 0) { 186249259Sdim v86.ctl = V86_FLAGS; 187288943Sdim v86.addr = 0x15; /* int 0x15 function 0xe801*/ 188288943Sdim v86.eax = 0xe801; 189288943Sdim v86int(); 190288943Sdim if (!(v86.efl & 1)) { 191288943Sdim bios_extmem = ((v86.ecx & 0xffff) + 192288943Sdim ((v86.edx & 0xffff) * 64)) * 1024; 193288943Sdim } 194288943Sdim } 195288943Sdim if (bios_extmem == 0) { 196288943Sdim v86.ctl = 0; 197288943Sdim v86.addr = 0x15; /* int 0x15 function 0x88*/ 198288943Sdim v86.eax = 0x8800; 199288943Sdim v86int(); 200288943Sdim bios_extmem = (v86.eax & 0xffff) * 1024; 201288943Sdim } 202288943Sdim 203288943Sdim /* 204288943Sdim * If we have extended memory and did not find a suitable heap 205288943Sdim * region in the SMAP, use the last 3MB of 'extended' memory as a 206288943Sdim * high heap candidate. 207288943Sdim */ 208288943Sdim if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { 209288943Sdim high_heap_size = HEAP_MIN; 210288943Sdim high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; 211288943Sdim } 212288943Sdim} 213288943Sdim 214288943Sdimint 215249259Sdimmain(void) 216249259Sdim{ 217249259Sdim char cmd[512], cmdtmp[512]; 218249259Sdim ssize_t sz; 219288943Sdim int autoboot, dskupdated; 220280031Sdim uint64_t ino; 221280031Sdim 222249259Sdim bios_getmem(); 223249259Sdim 224288943Sdim if (high_heap_size > 0) { 225309124Sdim heap_end = PTOV(high_heap_base + high_heap_size); 226309124Sdim heap_next = PTOV(high_heap_base); 227309124Sdim } else { 228309124Sdim heap_next = (char *) 229249259Sdim (roundup2(__base + (int32_t)&_end, 0x10000) - __base); 230249259Sdim heap_end = (char *)PTOV(bios_basemem); 231249259Sdim } 232249259Sdim setheap(heap_next, heap_end); 233249259Sdim 234249259Sdim v86.ctl = V86_FLAGS; 235276479Sdim v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 236276479Sdim dsk.drive = *(uint8_t *)PTOV(ARGS); 237249259Sdim dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 238249259Sdim dsk.unit = dsk.drive & DRV_MASK; 239249259Sdim dsk.part = -1; 240249259Sdim dsk.start = 0; 241249259Sdim bootinfo.bi_version = BOOTINFO_VERSION; 242341825Sdim bootinfo.bi_size = sizeof(bootinfo); 243341825Sdim bootinfo.bi_basemem = bios_basemem / 1024; 244341825Sdim bootinfo.bi_extmem = bios_extmem / 1024; 245341825Sdim bootinfo.bi_memsizes_valid++; 246341825Sdim bootinfo.bi_bios_dev = dsk.drive; 247249259Sdim 248249259Sdim autoboot = 1; 249249259Sdim *cmd = '\0'; 250249259Sdim 251249259Sdim for (;;) { 252249259Sdim *kname = '\0'; 253249259Sdim if ((ino = cd9660_lookup(PATH_CONFIG)) || 254249259Sdim (ino = cd9660_lookup(PATH_DOTCONFIG))) { 255249259Sdim sz = cd9660_fsread(ino, cmd, sizeof(cmd) - 1); 256249259Sdim cmd[(sz < 0) ? 0 : sz] = '\0'; 257280031Sdim } 258249259Sdim if (*cmd != '\0') { 259249259Sdim memcpy(cmdtmp, cmd, sizeof(cmdtmp)); 260249259Sdim if (parse_cmds(cmdtmp, &dskupdated)) 261249259Sdim break; 262249259Sdim if (!OPT_CHECK(RBX_QUIET)) 263280031Sdim printf("%s: %s", PATH_CONFIG, cmd); 264249259Sdim *cmd = '\0'; 265249259Sdim } 266280031Sdim 267280031Sdim if (autoboot && keyhit(3)) { 268249259Sdim if (*kname == '\0') 269249259Sdim memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); 270249259Sdim break; 271249259Sdim } 272249259Sdim autoboot = 0; 273249259Sdim 274249259Sdim /* 275280031Sdim * Try to exec stage 3 boot loader. If interrupted by a 276280031Sdim * keypress, or in case of failure, try to load a kernel 277249259Sdim * directly instead. 278249259Sdim */ 279249259Sdim if (*kname != '\0') 280249259Sdim load(); 281249259Sdim memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); 282249259Sdim load(); 283280031Sdim memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 284249259Sdim load(); 285249259Sdim dsk_meta = 0; 286288943Sdim } 287288943Sdim 288288943Sdim /* Present the user with the boot2 prompt. */ 289288943Sdim 290288943Sdim for (;;) { 291288943Sdim if (!OPT_CHECK(RBX_QUIET)) { 292249259Sdim printf("\nFreeBSD/x86 boot\n" 293276479Sdim "Default: %u:%s(%up%u)%s\n" 294249259Sdim "boot: ", 295249259Sdim dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 296249259Sdim dsk.part, kname); 297249259Sdim } 298249259Sdim if (ioctrl & IO_SERIAL) 299249259Sdim sio_flush(); 300249259Sdim *cmd = '\0'; 301249259Sdim if (keyhit(0)) 302249259Sdim getstr(cmd, sizeof(cmd)); 303249259Sdim else if (!OPT_CHECK(RBX_QUIET)) 304249259Sdim putchar('\n'); 305249259Sdim if (parse_cmds(cmd, &dskupdated)) { 306280031Sdim putchar('\a'); 307280031Sdim continue; 308249259Sdim } 309249259Sdim load(); 310249259Sdim } 311249259Sdim /* NOTREACHED */ 312249259Sdim} 313249259Sdim 314249259Sdim/* Needed so btxld can link us properly; do not remove. */ 315249259Sdimvoid 316249259Sdimexit(int x) 317249259Sdim{ 318249259Sdim 319249259Sdim while (1); 320249259Sdim __unreachable(); 321249259Sdim} 322249259Sdim 323249259Sdimstatic void 324249259Sdimload(void) 325249259Sdim{ 326249259Sdim union { 327249259Sdim struct exec ex; 328249259Sdim Elf32_Ehdr eh; 329249259Sdim } hdr; 330249259Sdim static Elf32_Phdr ep[2]; 331249259Sdim static Elf32_Shdr es[2]; 332249259Sdim caddr_t p; 333280031Sdim uint64_t ino; 334280031Sdim uint32_t addr, x; 335280031Sdim int fmt, i, j; 336249259Sdim 337249259Sdim if (!(ino = cd9660_lookup(kname))) { 338249259Sdim if (!ls) { 339249259Sdim printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, 340249259Sdim kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], 341249259Sdim dsk.unit, 342249259Sdim dsk.part); 343280031Sdim } 344280031Sdim return; 345280031Sdim } 346280031Sdim if (xfsread(ino, &hdr, sizeof(hdr))) 347249259Sdim return; 348249259Sdim if (N_GETMAGIC(hdr.ex) == ZMAGIC) 349249259Sdim fmt = 0; 350249259Sdim else if (IS_ELF(hdr.eh)) 351249259Sdim fmt = 1; 352321369Sdim else { 353276479Sdim printf("Invalid %s\n", "format"); 354296417Sdim return; 355296417Sdim } 356296417Sdim if (fmt == 0) { 357296417Sdim addr = hdr.ex.a_entry & 0xffffff; 358296417Sdim p = PTOV(addr); 359296417Sdim fs_off = PAGE_SIZE; 360296417Sdim if (xfsread(ino, p, hdr.ex.a_text)) 361296417Sdim return; 362296417Sdim p += roundup2(hdr.ex.a_text, PAGE_SIZE); 363296417Sdim if (xfsread(ino, p, hdr.ex.a_data)) 364296417Sdim return; 365280031Sdim p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 366276479Sdim bootinfo.bi_symtab = VTOP(p); 367280031Sdim memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 368276479Sdim p += sizeof(hdr.ex.a_syms); 369249259Sdim if (hdr.ex.a_syms) { 370276479Sdim if (xfsread(ino, p, hdr.ex.a_syms)) 371276479Sdim return; 372276479Sdim p += hdr.ex.a_syms; 373276479Sdim if (xfsread(ino, p, sizeof(int))) 374276479Sdim return; 375276479Sdim x = *(uint32_t *)p; 376276479Sdim p += sizeof(int); 377276479Sdim x -= sizeof(int); 378276479Sdim if (xfsread(ino, p, x)) 379276479Sdim return; 380276479Sdim p += x; 381276479Sdim } 382276479Sdim } else { 383276479Sdim fs_off = hdr.eh.e_phoff; 384276479Sdim for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 385276479Sdim if (xfsread(ino, ep + j, sizeof(ep[0]))) 386276479Sdim return; 387276479Sdim if (ep[j].p_type == PT_LOAD) 388276479Sdim j++; 389276479Sdim } 390276479Sdim for (i = 0; i < 2; i++) { 391276479Sdim p = PTOV(ep[i].p_paddr & 0xffffff); 392276479Sdim fs_off = ep[i].p_offset; 393276479Sdim if (xfsread(ino, p, ep[i].p_filesz)) 394276479Sdim return; 395276479Sdim } 396276479Sdim p += roundup2(ep[1].p_memsz, PAGE_SIZE); 397309124Sdim bootinfo.bi_symtab = VTOP(p); 398276479Sdim if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 399344779Sdim fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 400249259Sdim (hdr.eh.e_shstrndx + 1); 401276479Sdim if (xfsread(ino, &es, sizeof(es))) 402276479Sdim return; 403249259Sdim for (i = 0; i < 2; i++) { 404249259Sdim memcpy(p, &es[i].sh_size, 405249259Sdim sizeof(es[i].sh_size)); 406249259Sdim p += sizeof(es[i].sh_size); 407249259Sdim fs_off = es[i].sh_offset; 408249259Sdim if (xfsread(ino, p, es[i].sh_size)) 409344779Sdim return; 410280031Sdim p += es[i].sh_size; 411249259Sdim } 412327952Sdim } 413249259Sdim addr = hdr.eh.e_entry & 0xffffff; 414249259Sdim } 415249259Sdim bootinfo.bi_esymtab = VTOP(p); 416276479Sdim bootinfo.bi_kernelname = VTOP(kname); 417249259Sdim bootinfo.bi_bios_dev = dsk.drive; 418309124Sdim __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 419249259Sdim MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.unit, 0), 420249259Sdim 0, 0, 0, VTOP(&bootinfo)); 421249259Sdim} 422249259Sdim 423249259Sdimstatic int 424249259Sdimparse_cmds(char *cmdstr, int *dskupdated) 425249259Sdim{ 426249259Sdim char *arg; 427249259Sdim char *ep, *p, *q; 428249259Sdim const char *cp; 429249259Sdim unsigned int drv; 430314564Sdim int c, i, j; 431344779Sdim 432314564Sdim arg = cmdstr; 433314564Sdim *dskupdated = 0; 434314564Sdim while ((c = *arg++)) { 435344779Sdim if (c == ' ' || c == '\t' || c == '\n') 436314564Sdim continue; 437314564Sdim for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 438280031Sdim ep = p; 439280031Sdim if (*p) 440280031Sdim *p++ = 0; 441280031Sdim if (c == '-') { 442280031Sdim while ((c = *arg++)) { 443280031Sdim if (c == 'P') { 444280031Sdim if (*(uint8_t *)PTOV(0x496) & 0x10) { 445280031Sdim cp = "yes"; 446280031Sdim } else { 447280031Sdim opts |= OPT_SET(RBX_DUAL) | 448360784Sdim OPT_SET(RBX_SERIAL); 449360784Sdim cp = "no"; 450360784Sdim } 451360784Sdim printf("Keyboard: %s\n", cp); 452360784Sdim continue; 453280031Sdim } else if (c == 'S') { 454280031Sdim j = 0; 455249259Sdim while ((unsigned int)(i = *arg++ - '0') 456249259Sdim <= 9) 457249259Sdim j = j * 10 + i; 458249259Sdim if (j > 0 && i == -'0') { 459251662Sdim comspeed = j; 460360784Sdim break; 461360784Sdim } 462249259Sdim /* 463249259Sdim * Fall through to error below 464249259Sdim * ('S' not in optstr[]). 465249259Sdim */ 466249259Sdim } 467321369Sdim for (i = 0; c != optstr[i]; i++) 468249259Sdim if (i == NOPT - 1) 469249259Sdim return (-1); 470249259Sdim opts ^= OPT_SET(flags[i]); 471249259Sdim } 472249259Sdim ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 473321369Sdim OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; 474249259Sdim if (ioctrl & IO_SERIAL) { 475249259Sdim if (sio_init(115200 / comspeed) != 0) 476249259Sdim ioctrl &= ~IO_SERIAL; 477321369Sdim } 478249259Sdim } else { 479360784Sdim for (q = arg--; *q && *q != '('; q++); 480251662Sdim if (*q) { 481360784Sdim drv = -1; 482360784Sdim if (arg[1] == ':') { 483249259Sdim drv = *arg - '0'; 484249259Sdim if (drv > 9) 485249259Sdim return (-1); 486249259Sdim arg += 2; 487249259Sdim } 488249259Sdim if (q - arg != 2) 489314564Sdim return (-1); 490249259Sdim for (i = 0; arg[0] != dev_nm[i][0] || 491249259Sdim arg[1] != dev_nm[i][1]; i++) 492249259Sdim if (i == NDEV - 1) 493249259Sdim return (-1); 494249259Sdim dsk.type = i; 495249259Sdim arg += 3; 496353358Sdim dsk.unit = *arg - '0'; 497353358Sdim if (arg[1] != 'p' || dsk.unit > 9) 498360784Sdim return (-1); 499261991Sdim arg += 2; 500353358Sdim dsk.part = *arg - '0'; 501353358Sdim if (dsk.part < 1 || dsk.part > 9) 502249259Sdim return (-1); 503360784Sdim arg++; 504360784Sdim if (arg[0] != ')') 505249259Sdim return (-1); 506344779Sdim arg++; 507344779Sdim if (drv == -1) 508309124Sdim drv = dsk.unit; 509309124Sdim dsk.drive = (dsk.type <= TYPE_MAXHARD 510309124Sdim ? DRV_HARD : 0) + drv; 511341825Sdim *dskupdated = 1; 512321369Sdim } 513321369Sdim if ((i = ep - arg)) { 514360784Sdim if ((size_t)i >= sizeof(kname)) 515344779Sdim return (-1); 516344779Sdim memcpy(kname, arg, i + 1); 517344779Sdim } 518321369Sdim } 519321369Sdim arg = p; 520321369Sdim } 521249259Sdim return (0); 522249259Sdim} 523249259Sdim