1163533Simp/*- 2183636Simp * Copyright (c) 2008 John Hay 3183636Simp * Copyright (c) 2006 Warner Losh 4163533Simp * Copyright (c) 1998 Robert Nordier 5163533Simp * All rights reserved. 6163533Simp * 7163533Simp * Redistribution and use in source and binary forms are freely 8163533Simp * permitted provided that the above copyright notice and this 9163533Simp * paragraph and the following disclaimer are duplicated in all 10163533Simp * such forms. 11163533Simp * 12163533Simp * This software is provided "AS IS" and without any express or 13163533Simp * implied warranties, including, without limitation, the implied 14163533Simp * warranties of merchantability and fitness for a particular 15163533Simp * purpose. 16163533Simp */ 17163533Simp 18163533Simp#include <sys/cdefs.h> 19163533Simp__FBSDID("$FreeBSD$"); 20163533Simp 21163533Simp#include <sys/param.h> 22163533Simp#include <sys/disklabel.h> 23163533Simp#include <sys/diskmbr.h> 24163533Simp#include <sys/dirent.h> 25163533Simp#include <sys/reboot.h> 26163533Simp 27163533Simp#include <machine/elf.h> 28163533Simp 29163533Simp#include <stdarg.h> 30163533Simp 31163533Simp#include "lib.h" 32164134Simp#include "board.h" 33163533Simp 34163533Simp#define RBX_ASKNAME 0x0 /* -a */ 35163533Simp#define RBX_SINGLE 0x1 /* -s */ 36163533Simp/* 0x2 is reserved for log2(RB_NOSYNC). */ 37163533Simp/* 0x3 is reserved for log2(RB_HALT). */ 38163533Simp/* 0x4 is reserved for log2(RB_INITNAME). */ 39163533Simp#define RBX_DFLTROOT 0x5 /* -r */ 40163533Simp/* #define RBX_KDB 0x6 -d */ 41163533Simp/* 0x7 is reserved for log2(RB_RDONLY). */ 42163533Simp/* 0x8 is reserved for log2(RB_DUMP). */ 43163533Simp/* 0x9 is reserved for log2(RB_MINIROOT). */ 44163533Simp#define RBX_CONFIG 0xa /* -c */ 45163533Simp#define RBX_VERBOSE 0xb /* -v */ 46163533Simp/* #define RBX_SERIAL 0xc -h */ 47163533Simp/* #define RBX_CDROM 0xd -C */ 48163533Simp/* 0xe is reserved for log2(RB_POWEROFF). */ 49163533Simp#define RBX_GDB 0xf /* -g */ 50163533Simp/* #define RBX_MUTE 0x10 -m */ 51163533Simp/* 0x11 is reserved for log2(RB_SELFTEST). */ 52163533Simp/* 0x12 is reserved for boot programs. */ 53163533Simp/* 0x13 is reserved for boot programs. */ 54163533Simp/* #define RBX_PAUSE 0x14 -p */ 55163533Simp/* #define RBX_QUIET 0x15 -q */ 56183636Simp#define RBX_NOINTR 0x1c /* -n */ 57163533Simp/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */ 58163533Simp/* #define RBX_DUAL 0x1d -D */ 59163533Simp/* 0x1f is reserved for log2(RB_BOOTINFO). */ 60163533Simp 61163533Simp/* pass: -a, -s, -r, -v, -g */ 62163533Simp#define RBX_MASK (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \ 63163533Simp OPT_SET(RBX_DFLTROOT) | \ 64163533Simp OPT_SET(RBX_VERBOSE) | \ 65163533Simp OPT_SET(RBX_GDB)) 66163533Simp 67231287Sbapt#define PATH_DOTCONFIG "/boot.config" 68231287Sbapt#define PATH_CONFIG "/boot/config" 69163533Simp//#define PATH_KERNEL "/boot/kernel/kernel" 70164134Simp#define PATH_KERNEL "/boot/kernel/kernel.gz.tramp" 71163533Simp 72183636Simpextern uint32_t _end; 73163533Simp 74183636Simp#define NOPT 6 75183636Simp 76163533Simp#define OPT_SET(opt) (1 << (opt)) 77163533Simp#define OPT_CHECK(opt) ((opts) & OPT_SET(opt)) 78163533Simp 79183636Simpstatic const char optstr[NOPT] = "agnrsv"; 80163533Simpstatic const unsigned char flags[NOPT] = { 81183636Simp RBX_ASKNAME, 82183636Simp RBX_GDB, 83183636Simp RBX_NOINTR, 84183636Simp RBX_DFLTROOT, 85183636Simp RBX_SINGLE, 86183636Simp RBX_VERBOSE 87163533Simp}; 88163533Simp 89163533Simpunsigned dsk_start; 90163533Simpstatic char cmd[512]; 91163533Simpstatic char kname[1024]; 92163533Simpstatic uint32_t opts; 93163533Simpstatic int dsk_meta; 94163533Simp 95163533Simpstatic void load(void); 96163533Simpstatic int parse(void); 97163533Simpstatic int xfsread(ino_t, void *, size_t); 98163533Simpstatic int dskread(void *, unsigned, unsigned); 99183673Simp#ifdef FIXUP_BOOT_DRV 100183673Simpstatic void fixup_boot_drv(caddr_t, int, int, int); 101183673Simp#endif 102163533Simp 103173040Sjhb#define UFS_SMALL_CGBASE 104163533Simp#include "ufsread.c" 105163533Simp 106183636Simp#ifdef DEBUG 107183636Simp#define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) 108183636Simp#else 109183636Simp#define DPRINTF(fmt, ...) 110183636Simp#endif 111183636Simp 112163533Simpstatic inline int 113163533Simpxfsread(ino_t inode, void *buf, size_t nbyte) 114163533Simp{ 115183634Simp if ((size_t)fsread(inode, buf, nbyte) != nbyte) 116183634Simp return -1; 117183634Simp return 0; 118163533Simp} 119163533Simp 120163533Simpstatic inline void 121163533Simpgetstr(int c) 122163533Simp{ 123183634Simp char *s; 124163533Simp 125183634Simp s = cmd; 126183634Simp if (c == 0) 127183634Simp c = getc(10000); 128183634Simp for (;;) { 129183634Simp switch (c) { 130183634Simp case 0: 131183634Simp break; 132183634Simp case '\177': 133183634Simp case '\b': 134183634Simp if (s > cmd) { 135183634Simp s--; 136183634Simp printf("\b \b"); 137183634Simp } 138183634Simp break; 139183634Simp case '\n': 140183634Simp case '\r': 141183634Simp *s = 0; 142183634Simp return; 143183634Simp default: 144183634Simp if (s - cmd < sizeof(cmd) - 1) 145183634Simp *s++ = c; 146183634Simp xputchar(c); 147183634Simp } 148183634Simp c = getc(10000); 149163533Simp } 150163533Simp} 151163533Simp 152163533Simpint 153163533Simpmain(void) 154163533Simp{ 155183634Simp int autoboot, c = 0; 156183634Simp ino_t ino; 157163533Simp 158183636Simp dmadat = (void *)(0x20000000 + (16 << 20)); 159183634Simp board_init(); 160163533Simp 161183634Simp autoboot = 1; 162163533Simp 163183636Simp /* Process configuration file */ 164231287Sbapt if ((ino = lookup(PATH_CONFIG)) || 165231287Sbapt (ino = lookup(PATH_DOTCONFIG))) 166183634Simp fsread(ino, cmd, sizeof(cmd)); 167163533Simp 168183634Simp if (*cmd) { 169183634Simp if (parse()) 170183634Simp autoboot = 0; 171183636Simp printf("%s: %s\n", PATH_CONFIG, cmd); 172183634Simp /* Do not process this command twice */ 173183634Simp *cmd = 0; 174183634Simp } 175163533Simp 176183634Simp if (*kname == '\0') 177183634Simp strcpy(kname, PATH_KERNEL); 178183636Simp 179183636Simp /* Present the user with the boot2 prompt. */ 180183634Simp for (;;) { 181183634Simp printf("\nDefault: %s\nboot: ", kname); 182183636Simp if (!autoboot || 183183636Simp (OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0)) 184183634Simp getstr(c); 185183634Simp xputchar('\n'); 186183634Simp autoboot = 0; 187183634Simp c = 0; 188183634Simp if (parse()) 189183634Simp xputchar('\a'); 190183634Simp else 191183634Simp load(); 192183634Simp } 193163533Simp} 194163533Simp 195163533Simpstatic void 196163533Simpload(void) 197163533Simp{ 198183634Simp Elf32_Ehdr eh; 199183634Simp static Elf32_Phdr ep[2]; 200183634Simp caddr_t p; 201183634Simp ino_t ino; 202183634Simp uint32_t addr; 203183634Simp int i, j; 204183673Simp#ifdef FIXUP_BOOT_DRV 205183673Simp caddr_t staddr; 206183673Simp int klen; 207163533Simp 208183673Simp staddr = (caddr_t)0xffffffff; 209183673Simp klen = 0; 210183673Simp#endif 211183634Simp if (!(ino = lookup(kname))) { 212183634Simp if (!ls) 213183634Simp printf("No %s\n", kname); 214183634Simp return; 215183634Simp } 216183634Simp if (xfsread(ino, &eh, sizeof(eh))) 217183634Simp return; 218183634Simp if (!IS_ELF(eh)) { 219183634Simp printf("Invalid %s\n", "format"); 220183634Simp return; 221183634Simp } 222183634Simp fs_off = eh.e_phoff; 223183634Simp for (j = i = 0; i < eh.e_phnum && j < 2; i++) { 224183634Simp if (xfsread(ino, ep + j, sizeof(ep[0]))) 225183634Simp return; 226183634Simp if (ep[j].p_type == PT_LOAD) 227183634Simp j++; 228183634Simp } 229183634Simp for (i = 0; i < 2; i++) { 230183634Simp p = (caddr_t)ep[i].p_paddr; 231183634Simp fs_off = ep[i].p_offset; 232183673Simp#ifdef FIXUP_BOOT_DRV 233183673Simp if (staddr == (caddr_t)0xffffffff) 234183673Simp staddr = p; 235183673Simp klen += ep[i].p_filesz; 236183673Simp#endif 237183634Simp if (xfsread(ino, p, ep[i].p_filesz)) 238183634Simp return; 239183634Simp } 240183634Simp addr = eh.e_entry; 241183673Simp#ifdef FIXUP_BOOT_DRV 242183673Simp fixup_boot_drv(staddr, klen, bootslice, bootpart); 243183673Simp#endif 244183634Simp ((void(*)(int))addr)(opts & RBX_MASK); 245163533Simp} 246163533Simp 247163533Simpstatic int 248163533Simpparse() 249163533Simp{ 250183634Simp char *arg = cmd; 251183634Simp char *ep, *p; 252183634Simp int c, i; 253163533Simp 254183634Simp while ((c = *arg++)) { 255183634Simp if (c == ' ' || c == '\t' || c == '\n') 256183634Simp continue; 257183634Simp for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 258183634Simp ep = p; 259183634Simp if (*p) 260183634Simp *p++ = 0; 261183634Simp if (c == '-') { 262183634Simp while ((c = *arg++)) { 263183634Simp for (i = 0; c != optstr[i]; i++) 264183634Simp if (i == NOPT - 1) 265183634Simp return -1; 266183634Simp opts ^= OPT_SET(flags[i]); 267183634Simp } 268183634Simp } else { 269183634Simp arg--; 270183634Simp if ((i = ep - arg)) { 271183634Simp if ((size_t)i >= sizeof(kname)) 272183634Simp return -1; 273183634Simp memcpy(kname, arg, i + 1); 274183634Simp } 275183634Simp } 276183634Simp arg = p; 277163533Simp } 278183634Simp return 0; 279163533Simp} 280163533Simp 281163533Simpstatic int 282163533Simpdskread(void *buf, unsigned lba, unsigned nblk) 283163533Simp{ 284183634Simp struct dos_partition *dp; 285183634Simp struct disklabel *d; 286183634Simp char *sec; 287183634Simp int i; 288163533Simp 289183634Simp if (!dsk_meta) { 290183634Simp sec = dmadat->secbuf; 291183634Simp dsk_start = 0; 292183634Simp if (drvread(sec, DOSBBSECTOR, 1)) 293183634Simp return -1; 294183634Simp dp = (void *)(sec + DOSPARTOFF); 295183634Simp for (i = 0; i < NDOSPART; i++) { 296183634Simp if (dp[i].dp_typ == DOSPTYP_386BSD) 297183634Simp break; 298183634Simp } 299183634Simp if (i == NDOSPART) 300183634Simp return -1; 301183634Simp /* 302183634Simp * Although dp_start is aligned within the disk 303183634Simp * partition structure, DOSPARTOFF is 446, which is 304183634Simp * only word (2) aligned, not longword (4) aligned. 305183634Simp * Cope by using memcpy to fetch the start of this 306183634Simp * partition. 307183634Simp */ 308183634Simp memcpy(&dsk_start, &dp[1].dp_start, 4); 309183634Simp if (drvread(sec, dsk_start + LABELSECTOR, 1)) 310183634Simp return -1; 311183634Simp d = (void *)(sec + LABELOFFSET); 312183634Simp if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 313183634Simp printf("Invalid %s\n", "label"); 314183634Simp return -1; 315183634Simp } 316183634Simp if (!d->d_partitions[0].p_size) { 317183634Simp printf("Invalid %s\n", "partition"); 318183634Simp return -1; 319183634Simp } 320183634Simp dsk_start += d->d_partitions[0].p_offset; 321183634Simp dsk_start -= d->d_partitions[RAW_PART].p_offset; 322183634Simp dsk_meta++; 323163533Simp } 324183634Simp return drvread(buf, dsk_start + lba, nblk); 325163533Simp} 326183673Simp 327183673Simp#ifdef FIXUP_BOOT_DRV 328183673Simp/* 329183673Simp * fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel 330183673Simp * and change it to what was specified on the comandline or /boot.conf 331183673Simp * file or to what was encountered on the disk. It will try to handle 3 332183673Simp * different disk layouts, raw (dangerously dedicated), slice only and 333183673Simp * slice + partition. It will look for the following strings in the 334183673Simp * kernel, but if it is one of the first three, the string in the kernel 335183673Simp * must use the correct form to match the actual disk layout: 336183673Simp * - ufs:ad0a 337183673Simp * - ufs:ad0s1 338183673Simp * - ufs:ad0s1a 339183673Simp * - ufs:ROOTDEVNAME 340183673Simp * In the case of the first three strings, only the "a" at the end and 341183673Simp * the "1" after the "s" will be modified, if they exist. The string 342183673Simp * length will not be changed. In the case of the last string, the 343183673Simp * whole string will be built up and nul, '\0' terminated. 344183673Simp */ 345183673Simpstatic void 346183673Simpfixup_boot_drv(caddr_t addr, int klen, int bs, int bp) 347183673Simp{ 348183673Simp const u_int8_t op[] = "ufs:ROOTDEVNAME"; 349183673Simp const u_int8_t op2[] = "ufs:ad0"; 350183673Simp u_int8_t *p, *ps; 351183673Simp 352183673Simp DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n", 353183673Simp (int)addr, klen, bs, bp); 354183673Simp if (bs > 4) 355183673Simp return; 356183673Simp if (bp > 7) 357183673Simp return; 358183673Simp ps = memmem(addr, klen, op, sizeof(op)); 359183673Simp if (ps != NULL) { 360183673Simp p = ps + 4; /* past ufs: */ 361183673Simp DPRINTF("Found it at 0x%x\n", (int)ps); 362183673Simp p[0] = 'a'; p[1] = 'd'; p[2] = '0'; /* ad0 */ 363183673Simp p += 3; 364183673Simp if (bs > 0) { 365183673Simp /* append slice */ 366183673Simp *p++ = 's'; 367183673Simp *p++ = bs + '0'; 368183673Simp } 369183673Simp if (disk_layout != DL_SLICE) { 370183673Simp /* append partition */ 371183673Simp *p++ = bp + 'a'; 372183673Simp } 373183673Simp *p = '\0'; 374183673Simp } else { 375183673Simp ps = memmem(addr, klen, op2, sizeof(op2) - 1); 376183673Simp if (ps != NULL) { 377183673Simp p = ps + sizeof(op2) - 1; 378183673Simp DPRINTF("Found it at 0x%x\n", (int)ps); 379183673Simp if (*p == 's') { 380183673Simp /* fix slice */ 381183673Simp p++; 382183673Simp *p++ = bs + '0'; 383183673Simp } 384183673Simp if (*p == 'a') 385183673Simp *p = bp + 'a'; 386183673Simp } 387183673Simp } 388183673Simp if (ps == NULL) { 389183673Simp printf("Could not locate \"%s\" to fix kernel boot device, " 390183673Simp "check ROOTDEVNAME is set\n", op); 391183673Simp return; 392183673Simp } 393183673Simp DPRINTF("Changed boot device to %s\n", ps); 394183673Simp} 395183673Simp#endif 396