1119482Sobrien/*- 240269Srnordier * Copyright (c) 1998 Robert Nordier 340269Srnordier * All rights reserved. 440269Srnordier * 540269Srnordier * Redistribution and use in source and binary forms are freely 640269Srnordier * permitted provided that the above copyright notice and this 740269Srnordier * paragraph and the following disclaimer are duplicated in all 840269Srnordier * such forms. 940269Srnordier * 1040269Srnordier * This software is provided "AS IS" and without any express or 1140269Srnordier * implied warranties, including, without limitation, the implied 1240269Srnordier * warranties of merchantability and fitness for a particular 1340269Srnordier * purpose. 1440269Srnordier */ 1540269Srnordier 16119482Sobrien#include <sys/cdefs.h> 17119482Sobrien__FBSDID("$FreeBSD$"); 1840269Srnordier 1940269Srnordier#include <sys/param.h> 20172940Sjhb#include <sys/gpt.h> 2140269Srnordier#include <sys/dirent.h> 22122463Sbde#include <sys/reboot.h> 23122463Sbde 2440269Srnordier#include <machine/bootinfo.h> 2576224Sobrien#include <machine/elf.h> 26181436Sjhb#include <machine/psl.h> 2740269Srnordier 2840269Srnordier#include <stdarg.h> 2940269Srnordier 3040269Srnordier#include <a.out.h> 3140269Srnordier 3240269Srnordier#include <btxv86.h> 3340269Srnordier 3440404Srnordier#include "lib.h" 35213136Spjd#include "rbx.h" 36213136Spjd#include "drv.h" 37213136Spjd#include "util.h" 38213136Spjd#include "cons.h" 39213136Spjd#include "gpt.h" 4040404Srnordier 41231287Sbapt#define PATH_DOTCONFIG "/boot.config" 42231287Sbapt#define PATH_CONFIG "/boot/config" 4340269Srnordier#define PATH_BOOT3 "/boot/loader" 44148767Sssouhlal#define PATH_KERNEL "/boot/kernel/kernel" 4540269Srnordier 4642478Speter#define ARGS 0x900 47163707Sru#define NOPT 14 48108000Simp#define NDEV 3 4940269Srnordier#define MEM_BASE 0x12 5040269Srnordier#define MEM_EXT 0x15 5140269Srnordier 5240307Srnordier#define DRV_HARD 0x80 5340307Srnordier#define DRV_MASK 0x7f 5440307Srnordier 5557090Sru#define TYPE_AD 0 56108000Simp#define TYPE_DA 1 5798542Smckusick#define TYPE_MAXHARD TYPE_DA 58108000Simp#define TYPE_FD 2 5940307Srnordier 6040269Srnordierextern uint32_t _end; 6140269Srnordier 62172940Sjhbstatic const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; 63163707Srustatic const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 6440269Srnordierstatic const unsigned char flags[NOPT] = { 65213136Spjd RBX_DUAL, 66213136Spjd RBX_SERIAL, 67213136Spjd RBX_ASKNAME, 68213136Spjd RBX_CDROM, 69213136Spjd RBX_CONFIG, 70213136Spjd RBX_KDB, 71213136Spjd RBX_GDB, 72213136Spjd RBX_MUTE, 73213136Spjd RBX_NOINTR, 74213136Spjd RBX_PAUSE, 75213136Spjd RBX_QUIET, 76213136Spjd RBX_DFLTROOT, 77213136Spjd RBX_SINGLE, 78213136Spjd RBX_VERBOSE 7940269Srnordier}; 80213136Spjduint32_t opts; 8140269Srnordier 82108000Simpstatic const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 83108000Simpstatic const unsigned char dev_maj[NDEV] = {30, 4, 2}; 8440269Srnordier 85213136Spjdstatic struct dsk dsk; 8653159Sobrienstatic char kname[1024]; 87149212Siedowsestatic int comspeed = SIOSPD; 8840269Srnordierstatic struct bootinfo bootinfo; 8940269Srnordier 9040269Srnordiervoid exit(int); 91108000Simpstatic void load(void); 92213136Spjdstatic int parse(char *, int *); 9340323Srnordierstatic int xfsread(ino_t, void *, size_t); 94172940Sjhbstatic int dskread(void *, daddr_t, unsigned); 95108000Simpstatic uint32_t memsize(void); 9640269Srnordier 9797860Sphk#include "ufsread.c" 9862665Sjhb 99151382Ssobomaxstatic inline int 10098542Smckusickxfsread(ino_t inode, void *buf, size_t nbyte) 10162665Sjhb{ 102213136Spjd 103213136Spjd if ((size_t)fsread(inode, buf, nbyte) != nbyte) { 104213136Spjd printf("Invalid %s\n", "format"); 105213136Spjd return (-1); 106213136Spjd } 107213136Spjd return (0); 10862665Sjhb} 10962665Sjhb 110108000Simpstatic inline uint32_t 111108000Simpmemsize(void) 112108000Simp{ 113213136Spjd 114213136Spjd v86.addr = MEM_EXT; 115213136Spjd v86.eax = 0x8800; 116213136Spjd v86int(); 117213136Spjd return (v86.eax); 118108000Simp} 119108000Simp 120213136Spjdstatic int 121213136Spjdgptinit(void) 12262665Sjhb{ 12362665Sjhb 124213136Spjd if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) { 125213136Spjd printf("%s: unable to load GPT\n", BOOTPROG); 126213136Spjd return (-1); 12762665Sjhb } 128213136Spjd if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) { 129213136Spjd printf("%s: no UFS partition was found\n", BOOTPROG); 130213136Spjd return (-1); 131213136Spjd } 132213136Spjd dsk_meta = 0; 133213136Spjd return (0); 13462665Sjhb} 13562665Sjhb 13640269Srnordierint 13740269Srnordiermain(void) 13840269Srnordier{ 139213136Spjd char cmd[512], cmdtmp[512]; 140213136Spjd int autoboot, dskupdated; 141213136Spjd ino_t ino; 14240269Srnordier 143213136Spjd dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 144213136Spjd v86.ctl = V86_FLAGS; 145213136Spjd v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 146213136Spjd dsk.drive = *(uint8_t *)PTOV(ARGS); 147213136Spjd dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 148213136Spjd dsk.unit = dsk.drive & DRV_MASK; 149213136Spjd dsk.part = -1; 150213136Spjd dsk.start = 0; 151213136Spjd bootinfo.bi_version = BOOTINFO_VERSION; 152213136Spjd bootinfo.bi_size = sizeof(bootinfo); 153213136Spjd bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ 154213136Spjd bootinfo.bi_extmem = memsize(); 155213136Spjd bootinfo.bi_memsizes_valid++; 15694411Spb 157213136Spjd /* Process configuration file */ 15894411Spb 159213136Spjd if (gptinit() != 0) 160213136Spjd return (-1); 16198542Smckusick 162213136Spjd autoboot = 1; 163213136Spjd *cmd = '\0'; 16498542Smckusick 165213136Spjd for (;;) { 166213136Spjd *kname = '\0'; 167231287Sbapt if ((ino = lookup(PATH_CONFIG)) || 168231287Sbapt (ino = lookup(PATH_DOTCONFIG))) 169213136Spjd fsread(ino, cmd, sizeof(cmd)); 17094411Spb 171213136Spjd if (*cmd != '\0') { 172213136Spjd memcpy(cmdtmp, cmd, sizeof(cmdtmp)); 173213136Spjd if (parse(cmdtmp, &dskupdated)) 174213136Spjd break; 175213136Spjd if (dskupdated && gptinit() != 0) 176213136Spjd break; 177213136Spjd if (!OPT_CHECK(RBX_QUIET)) 178213136Spjd printf("%s: %s", PATH_CONFIG, cmd); 179213136Spjd *cmd = '\0'; 180213136Spjd } 18194411Spb 182213136Spjd if (autoboot && keyhit(3)) { 183213136Spjd if (*kname == '\0') 184213136Spjd memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 185213136Spjd break; 186213136Spjd } 187213136Spjd autoboot = 0; 188213136Spjd 189213136Spjd /* 190213136Spjd * Try to exec stage 3 boot loader. If interrupted by a 191213136Spjd * keypress, or in case of failure, try to load a kernel 192213136Spjd * directly instead. 193213136Spjd */ 194213136Spjd if (*kname != '\0') 195213136Spjd load(); 196213136Spjd memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 197213136Spjd load(); 198213136Spjd memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 199213136Spjd load(); 200213136Spjd gptbootfailed(&dsk); 201213136Spjd if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1) 202213136Spjd break; 203213136Spjd dsk_meta = 0; 20440269Srnordier } 20594411Spb 206213136Spjd /* Present the user with the boot2 prompt. */ 20794411Spb 208213136Spjd for (;;) { 209213136Spjd if (!OPT_CHECK(RBX_QUIET)) { 210213136Spjd printf("\nFreeBSD/x86 boot\n" 211213136Spjd "Default: %u:%s(%up%u)%s\n" 212213136Spjd "boot: ", 213213136Spjd dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 214213136Spjd dsk.part, kname); 215213136Spjd } 216213136Spjd if (ioctrl & IO_SERIAL) 217213136Spjd sio_flush(); 218213136Spjd *cmd = '\0'; 219213136Spjd if (keyhit(0)) 220213136Spjd getstr(cmd, sizeof(cmd)); 221213136Spjd else if (!OPT_CHECK(RBX_QUIET)) 222213136Spjd putchar('\n'); 223213136Spjd if (parse(cmd, &dskupdated)) { 224213136Spjd putchar('\a'); 225213136Spjd continue; 226213136Spjd } 227213136Spjd if (dskupdated && gptinit() != 0) 228213136Spjd continue; 229213136Spjd load(); 230213136Spjd } 231213136Spjd /* NOTREACHED */ 23240269Srnordier} 23340269Srnordier 23462665Sjhb/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 23540269Srnordiervoid 23640269Srnordierexit(int x) 23740269Srnordier{ 23840269Srnordier} 23940269Srnordier 24040269Srnordierstatic void 241108000Simpload(void) 24240269Srnordier{ 243151381Ssobomax union { 24440269Srnordier struct exec ex; 24540269Srnordier Elf32_Ehdr eh; 24640269Srnordier } hdr; 247151382Ssobomax static Elf32_Phdr ep[2]; 248151382Ssobomax static Elf32_Shdr es[2]; 24940269Srnordier caddr_t p; 25040269Srnordier ino_t ino; 25140269Srnordier uint32_t addr, x; 25240269Srnordier int fmt, i, j; 25340269Srnordier 254108000Simp if (!(ino = lookup(kname))) { 255213136Spjd if (!ls) { 256213136Spjd printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, 257213136Spjd kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 258213136Spjd dsk.part); 259213136Spjd } 26040269Srnordier return; 26140269Srnordier } 26240323Srnordier if (xfsread(ino, &hdr, sizeof(hdr))) 26340269Srnordier return; 26440269Srnordier if (N_GETMAGIC(hdr.ex) == ZMAGIC) 26540269Srnordier fmt = 0; 26640269Srnordier else if (IS_ELF(hdr.eh)) 26740269Srnordier fmt = 1; 26840269Srnordier else { 26940269Srnordier printf("Invalid %s\n", "format"); 27040269Srnordier return; 27140269Srnordier } 27240269Srnordier if (fmt == 0) { 273163914Sru addr = hdr.ex.a_entry & 0xffffff; 27440269Srnordier p = PTOV(addr); 27540269Srnordier fs_off = PAGE_SIZE; 27640323Srnordier if (xfsread(ino, p, hdr.ex.a_text)) 27740269Srnordier return; 27840269Srnordier p += roundup2(hdr.ex.a_text, PAGE_SIZE); 27940323Srnordier if (xfsread(ino, p, hdr.ex.a_data)) 28040269Srnordier return; 28141010Srnordier p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 28240269Srnordier bootinfo.bi_symtab = VTOP(p); 283107889Sobrien memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 28440269Srnordier p += sizeof(hdr.ex.a_syms); 28540269Srnordier if (hdr.ex.a_syms) { 28640323Srnordier if (xfsread(ino, p, hdr.ex.a_syms)) 28740269Srnordier return; 28840269Srnordier p += hdr.ex.a_syms; 28940323Srnordier if (xfsread(ino, p, sizeof(int))) 29040269Srnordier return; 29140269Srnordier x = *(uint32_t *)p; 29240269Srnordier p += sizeof(int); 29340269Srnordier x -= sizeof(int); 29440323Srnordier if (xfsread(ino, p, x)) 29540269Srnordier return; 29640269Srnordier p += x; 29740269Srnordier } 29840269Srnordier } else { 29940269Srnordier fs_off = hdr.eh.e_phoff; 30048034Srnordier for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 30140323Srnordier if (xfsread(ino, ep + j, sizeof(ep[0]))) 30240269Srnordier return; 30340269Srnordier if (ep[j].p_type == PT_LOAD) 30440269Srnordier j++; 30540269Srnordier } 30640269Srnordier for (i = 0; i < 2; i++) { 307163914Sru p = PTOV(ep[i].p_paddr & 0xffffff); 30840269Srnordier fs_off = ep[i].p_offset; 30940323Srnordier if (xfsread(ino, p, ep[i].p_filesz)) 31040269Srnordier return; 31140269Srnordier } 31240269Srnordier p += roundup2(ep[1].p_memsz, PAGE_SIZE); 31340269Srnordier bootinfo.bi_symtab = VTOP(p); 31440269Srnordier if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 31540269Srnordier fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 31640269Srnordier (hdr.eh.e_shstrndx + 1); 31740323Srnordier if (xfsread(ino, &es, sizeof(es))) 31840269Srnordier return; 31940269Srnordier for (i = 0; i < 2; i++) { 320107889Sobrien memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 32140269Srnordier p += sizeof(es[i].sh_size); 32240269Srnordier fs_off = es[i].sh_offset; 32340323Srnordier if (xfsread(ino, p, es[i].sh_size)) 32440269Srnordier return; 32540269Srnordier p += es[i].sh_size; 32640269Srnordier } 32740269Srnordier } 328163914Sru addr = hdr.eh.e_entry & 0xffffff; 32940269Srnordier } 33040269Srnordier bootinfo.bi_esymtab = VTOP(p); 331108000Simp bootinfo.bi_kernelname = VTOP(kname); 33243113Smsmith bootinfo.bi_bios_dev = dsk.drive; 333151381Ssobomax __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 334172940Sjhb MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff), 33540307Srnordier 0, 0, 0, VTOP(&bootinfo)); 33640269Srnordier} 33740269Srnordier 33840269Srnordierstatic int 339213136Spjdparse(char *cmdstr, int *dskupdated) 34040269Srnordier{ 341213136Spjd char *arg = cmdstr; 342149212Siedowse char *ep, *p, *q; 343149212Siedowse const char *cp; 344108000Simp unsigned int drv; 345149212Siedowse int c, i, j; 34640269Srnordier 347213136Spjd *dskupdated = 0; 34840269Srnordier while ((c = *arg++)) { 34961627Sru if (c == ' ' || c == '\t' || c == '\n') 35040269Srnordier continue; 35161627Sru for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 352149212Siedowse ep = p; 35340269Srnordier if (*p) 35440269Srnordier *p++ = 0; 35540404Srnordier if (c == '-') { 35640269Srnordier while ((c = *arg++)) { 357149212Siedowse if (c == 'P') { 358149212Siedowse if (*(uint8_t *)PTOV(0x496) & 0x10) { 359149212Siedowse cp = "yes"; 360149212Siedowse } else { 361151999Sru opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); 362149212Siedowse cp = "no"; 363149212Siedowse } 364149212Siedowse printf("Keyboard: %s\n", cp); 365149212Siedowse continue; 366149212Siedowse } else if (c == 'S') { 367149212Siedowse j = 0; 368149212Siedowse while ((unsigned int)(i = *arg++ - '0') <= 9) 369149212Siedowse j = j * 10 + i; 370149212Siedowse if (j > 0 && i == -'0') { 371149212Siedowse comspeed = j; 372149212Siedowse break; 373149212Siedowse } 374149212Siedowse /* Fall through to error below ('S' not in optstr[]). */ 375149212Siedowse } 37640269Srnordier for (i = 0; c != optstr[i]; i++) 37740269Srnordier if (i == NOPT - 1) 37840269Srnordier return -1; 379151999Sru opts ^= OPT_SET(flags[i]); 38040269Srnordier } 381151999Sru ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 382151999Sru OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; 383242562Savg if (ioctrl & IO_SERIAL) { 384242562Savg if (sio_init(115200 / comspeed) != 0) 385242562Savg ioctrl &= ~IO_SERIAL; 386242562Savg } 38740404Srnordier } else { 38840269Srnordier for (q = arg--; *q && *q != '('; q++); 38940269Srnordier if (*q) { 39040269Srnordier drv = -1; 39140269Srnordier if (arg[1] == ':') { 39240269Srnordier drv = *arg - '0'; 393108000Simp if (drv > 9) 394108000Simp return (-1); 39540269Srnordier arg += 2; 39640269Srnordier } 39740269Srnordier if (q - arg != 2) 39840269Srnordier return -1; 39940269Srnordier for (i = 0; arg[0] != dev_nm[i][0] || 40040314Srnordier arg[1] != dev_nm[i][1]; i++) 40140269Srnordier if (i == NDEV - 1) 40240269Srnordier return -1; 40340269Srnordier dsk.type = i; 40440269Srnordier arg += 3; 405108000Simp dsk.unit = *arg - '0'; 406196326Sjhay if (arg[1] != 'p' || dsk.unit > 9) 40740269Srnordier return -1; 40840269Srnordier arg += 2; 409196326Sjhay dsk.part = *arg - '0'; 410196326Sjhay if (dsk.part < 1 || dsk.part > 9) 411196326Sjhay return -1; 412196326Sjhay arg++; 413172940Sjhb if (arg[0] != ')') 41440269Srnordier return -1; 415172940Sjhb arg++; 41640269Srnordier if (drv == -1) 41740307Srnordier drv = dsk.unit; 41898542Smckusick dsk.drive = (dsk.type <= TYPE_MAXHARD 41998542Smckusick ? DRV_HARD : 0) + drv; 420213136Spjd *dskupdated = 1; 42140269Srnordier } 422149212Siedowse if ((i = ep - arg)) { 423107889Sobrien if ((size_t)i >= sizeof(kname)) 42440269Srnordier return -1; 42540269Srnordier memcpy(kname, arg, i + 1); 42640269Srnordier } 42740269Srnordier } 42840269Srnordier arg = p; 42940269Srnordier } 43040269Srnordier return 0; 43140269Srnordier} 43240269Srnordier 43340269Srnordierstatic int 434172940Sjhbdskread(void *buf, daddr_t lba, unsigned nblk) 43540269Srnordier{ 43640269Srnordier 437213136Spjd return drvread(&dsk, buf, lba + dsk.start, nblk); 43840269Srnordier} 439