1183651Sjhay/*- 2183651Sjhay * Copyright (c) 2008 John Hay 3183651Sjhay * Copyright (c) 1998 Robert Nordier 4183651Sjhay * All rights reserved. 5183651Sjhay * 6183651Sjhay * Redistribution and use in source and binary forms are freely 7183651Sjhay * permitted provided that the above copyright notice and this 8183651Sjhay * paragraph and the following disclaimer are duplicated in all 9183651Sjhay * such forms. 10183651Sjhay * 11183651Sjhay * This software is provided "AS IS" and without any express or 12183651Sjhay * implied warranties, including, without limitation, the implied 13183651Sjhay * warranties of merchantability and fitness for a particular 14183651Sjhay * purpose. 15183651Sjhay */ 16183651Sjhay 17183651Sjhay#include <sys/cdefs.h> 18183651Sjhay__FBSDID("$FreeBSD$"); 19183651Sjhay 20183651Sjhay#include <sys/param.h> 21183651Sjhay#include <sys/disklabel.h> 22183651Sjhay#include <sys/diskmbr.h> 23183651Sjhay#include <sys/dirent.h> 24183651Sjhay#include <sys/reboot.h> 25183651Sjhay 26183651Sjhay#include <machine/elf.h> 27183651Sjhay 28183651Sjhay#include <stdarg.h> 29183651Sjhay 30183651Sjhay#include "lib.h" 31183651Sjhay 32183651Sjhay#define RBX_ASKNAME 0x0 /* -a */ 33183651Sjhay#define RBX_SINGLE 0x1 /* -s */ 34183651Sjhay/* 0x2 is reserved for log2(RB_NOSYNC). */ 35183651Sjhay/* 0x3 is reserved for log2(RB_HALT). */ 36183651Sjhay/* 0x4 is reserved for log2(RB_INITNAME). */ 37183651Sjhay#define RBX_DFLTROOT 0x5 /* -r */ 38183651Sjhay/* #define RBX_KDB 0x6 -d */ 39183651Sjhay/* 0x7 is reserved for log2(RB_RDONLY). */ 40183651Sjhay/* 0x8 is reserved for log2(RB_DUMP). */ 41183651Sjhay/* 0x9 is reserved for log2(RB_MINIROOT). */ 42183651Sjhay#define RBX_CONFIG 0xa /* -c */ 43183651Sjhay#define RBX_VERBOSE 0xb /* -v */ 44183651Sjhay/* #define RBX_SERIAL 0xc -h */ 45183651Sjhay/* #define RBX_CDROM 0xd -C */ 46183651Sjhay/* 0xe is reserved for log2(RB_POWEROFF). */ 47183651Sjhay#define RBX_GDB 0xf /* -g */ 48183651Sjhay/* #define RBX_MUTE 0x10 -m */ 49183651Sjhay/* 0x11 is reserved for log2(RB_SELFTEST). */ 50183651Sjhay/* 0x12 is reserved for boot programs. */ 51183651Sjhay/* 0x13 is reserved for boot programs. */ 52183651Sjhay/* #define RBX_PAUSE 0x14 -p */ 53183651Sjhay/* #define RBX_QUIET 0x15 -q */ 54183651Sjhay#define RBX_NOINTR 0x1c /* -n */ 55183651Sjhay/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */ 56183651Sjhay/* #define RBX_DUAL 0x1d -D */ 57183651Sjhay/* 0x1f is reserved for log2(RB_BOOTINFO). */ 58183651Sjhay 59183651Sjhay/* pass: -a, -s, -r, -v, -g */ 60183651Sjhay#define RBX_MASK (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \ 61183651Sjhay OPT_SET(RBX_DFLTROOT) | \ 62183651Sjhay OPT_SET(RBX_VERBOSE) | \ 63183651Sjhay OPT_SET(RBX_GDB)) 64183651Sjhay 65226506Sdes#define PATH_DOTCONFIG "/boot.config" 66226506Sdes#define PATH_CONFIG "/boot/config" 67183651Sjhay#define PATH_KERNEL "/boot/kernel/kernel" 68183651Sjhay 69183651Sjhayextern uint32_t _end; 70183651Sjhay 71183651Sjhay#define NOPT 6 72183651Sjhay 73183651Sjhay#define OPT_SET(opt) (1 << (opt)) 74183651Sjhay#define OPT_CHECK(opt) ((opts) & OPT_SET(opt)) 75183651Sjhay 76183651Sjhaystatic const char optstr[NOPT] = "agnrsv"; 77183651Sjhaystatic const unsigned char flags[NOPT] = { 78183651Sjhay RBX_ASKNAME, 79183651Sjhay RBX_GDB, 80183651Sjhay RBX_NOINTR, 81183651Sjhay RBX_DFLTROOT, 82183651Sjhay RBX_SINGLE, 83183651Sjhay RBX_VERBOSE 84183651Sjhay}; 85183651Sjhay 86183651Sjhaystatic unsigned dsk_start; 87183651Sjhaystatic char cmd[512]; 88183651Sjhaystatic char kname[1024]; 89183651Sjhaystatic uint32_t opts; 90225955Sthompsastatic uint8_t dsk_meta; 91183651Sjhaystatic int bootslice; 92183651Sjhaystatic int bootpart; 93183651Sjhaystatic int disk_layout; 94183651Sjhay#define DL_UNKNOWN 0 95183651Sjhay#define DL_RAW 1 /* Dangerously dedicated */ 96183651Sjhay#define DL_SLICE 2 /* Use only slices (DOS partitions) */ 97183651Sjhay#define DL_SLICEPART 3 /* Use slices and partitions */ 98183651Sjhay 99183651Sjhaystatic void load(void); 100183651Sjhaystatic int parse(void); 101183651Sjhaystatic int dskread(void *, unsigned, unsigned); 102183651Sjhaystatic int drvread(void *, unsigned, unsigned); 103183651Sjhay#ifdef FIXUP_BOOT_DRV 104183651Sjhaystatic void fixup_boot_drv(caddr_t, int, int, int); 105183651Sjhay#endif 106183651Sjhay 107183651Sjhay#include "ufsread.c" 108183651Sjhay 109183651Sjhay#ifdef DEBUG 110183651Sjhay#define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) 111183651Sjhay#else 112183651Sjhay#define DPRINTF(fmt, ...) 113183651Sjhay#endif 114183651Sjhay 115183651Sjhaystatic inline int 116235988Sglebxfsread(ufs_ino_t inode, void *buf, size_t nbyte) 117183651Sjhay{ 118183651Sjhay if ((size_t)fsread(inode, buf, nbyte) != nbyte) 119183651Sjhay return -1; 120183651Sjhay return 0; 121183651Sjhay} 122183651Sjhay 123183651Sjhaystatic inline void 124183651Sjhaygetstr(int c) 125183651Sjhay{ 126183651Sjhay char *s; 127183651Sjhay 128183651Sjhay s = cmd; 129183651Sjhay if (c == 0) 130183651Sjhay c = getc(10000); 131183651Sjhay for (;;) { 132183651Sjhay switch (c) { 133183651Sjhay case 0: 134183651Sjhay break; 135183651Sjhay case '\177': 136183651Sjhay case '\b': 137183651Sjhay if (s > cmd) { 138183651Sjhay s--; 139183651Sjhay printf("\b \b"); 140183651Sjhay } 141183651Sjhay break; 142183651Sjhay case '\n': 143183651Sjhay case '\r': 144183651Sjhay *s = 0; 145183651Sjhay return; 146183651Sjhay default: 147183651Sjhay if (s - cmd < sizeof(cmd) - 1) 148183651Sjhay *s++ = c; 149183651Sjhay xputchar(c); 150183651Sjhay } 151183671Simp c = getc(10000); 152183651Sjhay } 153183651Sjhay} 154183651Sjhay 155183651Sjhayint 156183651Sjhaymain(void) 157183651Sjhay{ 158183651Sjhay const char *bt; 159183651Sjhay int autoboot, c = 0; 160235988Sgleb ufs_ino_t ino; 161183651Sjhay 162183651Sjhay dmadat = (void *)(0x1c0000); 163183651Sjhay p_memset((char *)dmadat, 0, 32 * 1024); 164183651Sjhay bt = board_init(); 165183651Sjhay 166186352Ssam printf("FreeBSD ARM (%s) boot2 v%d.%d\n", bt, 0, 4); 167183651Sjhay 168183651Sjhay autoboot = 1; 169183651Sjhay 170183651Sjhay /* Process configuration file */ 171226506Sdes if ((ino = lookup(PATH_CONFIG)) || 172226506Sdes (ino = lookup(PATH_DOTCONFIG))) 173183651Sjhay fsread(ino, cmd, sizeof(cmd)); 174183651Sjhay 175183651Sjhay if (*cmd) { 176183651Sjhay if (parse()) 177183651Sjhay autoboot = 0; 178183651Sjhay printf("%s: %s\n", PATH_CONFIG, cmd); 179183651Sjhay /* Do not process this command twice */ 180183651Sjhay *cmd = 0; 181183651Sjhay } 182183651Sjhay 183183651Sjhay if (*kname == '\0') 184183651Sjhay strcpy(kname, PATH_KERNEL); 185183651Sjhay 186183651Sjhay /* Present the user with the boot2 prompt. */ 187183651Sjhay for (;;) { 188183651Sjhay printf("\nDefault: %s\nboot: ", kname); 189183651Sjhay if (!autoboot || 190183651Sjhay (OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0)) 191183651Sjhay getstr(c); 192183651Sjhay xputchar('\n'); 193183651Sjhay autoboot = 0; 194183651Sjhay c = 0; 195183651Sjhay DPRINTF("cmd is '%s'\n", cmd); 196183651Sjhay if (parse()) 197183651Sjhay xputchar('\a'); 198183651Sjhay else 199183651Sjhay load(); 200183651Sjhay } 201183651Sjhay} 202183651Sjhay 203183651Sjhaystatic void 204183651Sjhayload(void) 205183651Sjhay{ 206183651Sjhay Elf32_Ehdr eh; 207183651Sjhay static Elf32_Phdr ep[2]; 208183651Sjhay caddr_t p; 209235988Sgleb ufs_ino_t ino; 210183651Sjhay uint32_t addr; 211183651Sjhay int i, j; 212183651Sjhay#ifdef FIXUP_BOOT_DRV 213183651Sjhay caddr_t staddr; 214183651Sjhay int klen; 215183651Sjhay 216183651Sjhay staddr = (caddr_t)0xffffffff; 217183651Sjhay klen = 0; 218183651Sjhay#endif 219183651Sjhay if (!(ino = lookup(kname))) { 220183651Sjhay if (!ls) 221183651Sjhay printf("No %s\n", kname); 222183651Sjhay return; 223183651Sjhay } 224183651Sjhay DPRINTF("Found %s\n", kname); 225183651Sjhay if (xfsread(ino, &eh, sizeof(eh))) 226183651Sjhay return; 227183651Sjhay if (!IS_ELF(eh)) { 228183651Sjhay printf("Invalid %s\n", "format"); 229183651Sjhay return; 230183651Sjhay } 231183651Sjhay fs_off = eh.e_phoff; 232183651Sjhay for (j = i = 0; i < eh.e_phnum && j < 2; i++) { 233183651Sjhay if (xfsread(ino, ep + j, sizeof(ep[0]))) 234183651Sjhay return; 235183651Sjhay if (ep[j].p_type == PT_LOAD) 236183651Sjhay j++; 237183651Sjhay } 238183651Sjhay for (i = 0; i < 2; i++) { 239183651Sjhay p = (caddr_t)(ep[i].p_paddr & 0x0fffffff); 240183651Sjhay fs_off = ep[i].p_offset; 241183651Sjhay#ifdef FIXUP_BOOT_DRV 242183651Sjhay if (staddr == (caddr_t)0xffffffff) 243183651Sjhay staddr = p; 244183651Sjhay klen += ep[i].p_filesz; 245183651Sjhay#endif 246183651Sjhay if (xfsread(ino, p, ep[i].p_filesz)) 247183651Sjhay return; 248183651Sjhay } 249183651Sjhay addr = eh.e_entry & 0x0fffffff; 250183651Sjhay DPRINTF("Entry point %x for %s\n", addr, kname); 251183651Sjhay clr_board(); 252183651Sjhay#ifdef FIXUP_BOOT_DRV 253183651Sjhay fixup_boot_drv(staddr, klen, bootslice, bootpart); 254183651Sjhay#endif 255183651Sjhay ((void(*)(int))addr)(RB_BOOTINFO /* XXX | (opts & RBX_MASK) */); 256183651Sjhay} 257183651Sjhay 258183651Sjhaystatic int 259183651Sjhayparse() 260183651Sjhay{ 261183651Sjhay char *arg = cmd; 262183651Sjhay char *ep, *p; 263183651Sjhay int c, i; 264183651Sjhay 265183651Sjhay while ((c = *arg++)) { 266183651Sjhay if (c == ' ' || c == '\t' || c == '\n') 267183651Sjhay continue; 268183651Sjhay for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 269183651Sjhay ep = p; 270183651Sjhay if (*p) 271183651Sjhay *p++ = 0; 272183651Sjhay if (c == '-') { 273183651Sjhay while ((c = *arg++)) { 274183651Sjhay for (i = 0; c != optstr[i]; i++) 275183651Sjhay if (i == NOPT - 1) 276183651Sjhay return -1; 277183651Sjhay opts ^= OPT_SET(flags[i]); 278183651Sjhay } 279183651Sjhay } else { 280183651Sjhay arg--; 281183651Sjhay /* look for ad0s1a:... | ad0s1:... */ 282183651Sjhay if (strlen(arg) > 6 && arg[0] == 'a' && 283183651Sjhay arg[1] == 'd' && arg[3] == 's' && 284183651Sjhay (arg[5] == ':' || arg[6] == ':')) { 285183651Sjhay /* XXX Should also handle disk. */ 286183651Sjhay bootslice = arg[4] - '0'; 287183651Sjhay if (bootslice < 1 || bootslice > 4) 288183651Sjhay return (-1); 289183651Sjhay bootpart = 0; 290183651Sjhay if (arg[5] != ':') 291183651Sjhay bootpart = arg[5] - 'a'; 292183651Sjhay if (bootpart < 0 || bootpart > 7) 293183651Sjhay return (-1); 294183651Sjhay dsk_meta = 0; 295183651Sjhay if (arg[5] == ':') 296183651Sjhay arg += 6; 297183651Sjhay else 298183651Sjhay arg += 7; 299183651Sjhay /* look for ad0a:... */ 300183651Sjhay } else if (strlen(arg) > 4 && arg[0] == 'a' && 301183651Sjhay arg[1] == 'd' && arg[2] == '0' && arg[4] == ':') { 302183651Sjhay bootslice = 0; 303183651Sjhay bootpart = arg[3] - 'a'; 304183651Sjhay if (bootpart < 0 || bootpart > 7) 305183651Sjhay return (-1); 306183651Sjhay dsk_meta = 0; 307183651Sjhay arg += 5; 308183651Sjhay } 309183651Sjhay if ((i = ep - arg)) { 310183651Sjhay if ((size_t)i >= sizeof(kname)) 311183651Sjhay return -1; 312183651Sjhay memcpy(kname, arg, i + 1); 313183651Sjhay } 314183651Sjhay } 315183651Sjhay arg = p; 316183651Sjhay } 317183651Sjhay return 0; 318183651Sjhay} 319183651Sjhay 320183651Sjhay/* 321183651Sjhay * dskread() will try to handle the disk layouts that are typically 322183651Sjhay * encountered. 323183651Sjhay * - raw or "Dangerously Dedicated" mode. No real slice table, just the 324183651Sjhay * default one that is included with bsdlabel -B. Typically this is 325183651Sjhay * used with ROOTDEVNAME=\"ufs:ad0a\". 326183651Sjhay * - slice only. Only a slice table is installed with no bsd label or 327183651Sjhay * bsd partition table. This is typically used with 328183651Sjhay * ROOTDEVNAME=\"ufs:ad0s1\". 329183651Sjhay * - slice + bsd label + partition table. This is typically done with 330183651Sjhay * with fdisk + bsdlabel and is used with ROOTDEVNAME=\"ufs:ad0s1a\". 331183651Sjhay */ 332183651Sjhaystatic int 333183651Sjhaydskread(void *buf, unsigned lba, unsigned nblk) 334183651Sjhay{ 335183651Sjhay struct dos_partition *dp; 336183651Sjhay struct disklabel *d; 337183651Sjhay char *sec; 338183651Sjhay int i; 339183651Sjhay 340183651Sjhay if (!dsk_meta) { 341183651Sjhay sec = dmadat->secbuf; 342183651Sjhay dsk_start = 0; 343183651Sjhay if (drvread(sec, DOSBBSECTOR, 1)) 344183651Sjhay return -1; 345183651Sjhay dp = (void *)(sec + DOSPARTOFF); 346183651Sjhay if (bootslice != 0) { 347183651Sjhay i = bootslice - 1; 348183651Sjhay if (dp[i].dp_typ != DOSPTYP_386BSD) 349183651Sjhay return -1; 350183651Sjhay } else { 351183651Sjhay for (i = 0; i < NDOSPART; i++) { 352183651Sjhay if ((dp[i].dp_typ == DOSPTYP_386BSD) && 353183651Sjhay (dp[i].dp_flag == 0x80)) 354183651Sjhay break; 355183651Sjhay } 356183651Sjhay } 357183651Sjhay if (i != NDOSPART) { 358183651Sjhay bootslice = i + 1; 359183651Sjhay DPRINTF("Found an active fbsd slice. (%d)\n", i + 1); 360183651Sjhay /* 361183651Sjhay * Although dp_start is aligned within the disk 362183651Sjhay * partition structure, DOSPARTOFF is 446, which 363183651Sjhay * is only word (2) aligned, not longword (4) 364183651Sjhay * aligned. Cope by using memcpy to fetch the 365183651Sjhay * start of this partition. 366183651Sjhay */ 367183651Sjhay memcpy(&dsk_start, &dp[i].dp_start, 4); 368183651Sjhay dsk_start = swap32(dsk_start); 369183651Sjhay DPRINTF("dsk_start %x\n", dsk_start); 370183651Sjhay if ((bootslice == 4) && (dsk_start == 0)) { 371183651Sjhay disk_layout = DL_RAW; 372183651Sjhay bootslice = 0; 373183651Sjhay } 374183651Sjhay } 375183651Sjhay if (drvread(sec, dsk_start + LABELSECTOR, 1)) 376183651Sjhay return -1; 377183651Sjhay d = (void *)(sec + LABELOFFSET); 378183651Sjhay if ((d->d_magic == DISKMAGIC && d->d_magic2 == DISKMAGIC) || 379183651Sjhay (swap32(d->d_magic) == DISKMAGIC && 380183651Sjhay swap32(d->d_magic2) == DISKMAGIC)) { 381183651Sjhay DPRINTF("p_size = %x\n", 382183651Sjhay !d->d_partitions[bootpart].p_size); 383183651Sjhay if (!d->d_partitions[bootpart].p_size) { 384183651Sjhay printf("Invalid partition\n"); 385183651Sjhay return -1; 386183651Sjhay } 387183651Sjhay DPRINTF("p_offset %x, RAW %x\n", 388183651Sjhay swap32(d->d_partitions[bootpart].p_offset), 389183651Sjhay swap32(d->d_partitions[RAW_PART].p_offset)); 390183651Sjhay dsk_start += swap32(d->d_partitions[bootpart].p_offset); 391183651Sjhay dsk_start -= swap32(d->d_partitions[RAW_PART].p_offset); 392183651Sjhay if ((disk_layout == DL_UNKNOWN) && (bootslice == 0)) 393183651Sjhay disk_layout = DL_RAW; 394183651Sjhay else if (disk_layout == DL_UNKNOWN) 395183651Sjhay disk_layout = DL_SLICEPART; 396183651Sjhay } else { 397183651Sjhay disk_layout = DL_SLICE; 398183651Sjhay DPRINTF("Invalid %s\n", "label"); 399183651Sjhay } 400183651Sjhay DPRINTF("bootslice %d, bootpart %d, dsk_start %u\n", bootslice, 401183651Sjhay bootpart, dsk_start); 402183651Sjhay dsk_meta++; 403183651Sjhay } 404183651Sjhay return drvread(buf, dsk_start + lba, nblk); 405183651Sjhay} 406183651Sjhay 407183651Sjhaystatic int 408183651Sjhaydrvread(void *buf, unsigned lba, unsigned nblk) 409183651Sjhay{ 410183651Sjhay static unsigned c = 0x2d5c7c2f; 411183651Sjhay 412183651Sjhay printf("%c\b", c = c << 8 | c >> 24); 413183651Sjhay return (avila_read((char *)buf, lba, nblk)); 414183651Sjhay} 415183651Sjhay 416183651Sjhay#ifdef FIXUP_BOOT_DRV 417183651Sjhay/* 418183651Sjhay * fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel 419183651Sjhay * and change it to what was specified on the comandline or /boot.conf 420183651Sjhay * file or to what was encountered on the disk. It will try to handle 3 421183651Sjhay * different disk layouts, raw (dangerously dedicated), slice only and 422183651Sjhay * slice + partition. It will look for the following strings in the 423183651Sjhay * kernel, but if it is one of the first three, the string in the kernel 424183651Sjhay * must use the correct form to match the actual disk layout: 425183651Sjhay * - ufs:ad0a 426183651Sjhay * - ufs:ad0s1 427183651Sjhay * - ufs:ad0s1a 428183651Sjhay * - ufs:ROOTDEVNAME 429183651Sjhay * In the case of the first three strings, only the "a" at the end and 430183651Sjhay * the "1" after the "s" will be modified, if they exist. The string 431183651Sjhay * length will not be changed. In the case of the last string, the 432183651Sjhay * whole string will be built up and nul, '\0' terminated. 433183651Sjhay */ 434183651Sjhaystatic void 435183651Sjhayfixup_boot_drv(caddr_t addr, int klen, int bs, int bp) 436183651Sjhay{ 437183651Sjhay const u_int8_t op[] = "ufs:ROOTDEVNAME"; 438183651Sjhay const u_int8_t op2[] = "ufs:ad0"; 439183651Sjhay u_int8_t *p, *ps; 440183651Sjhay 441183651Sjhay DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n", 442183651Sjhay (int)addr, klen, bs, bp); 443183651Sjhay if (bs > 4) 444183651Sjhay return; 445183651Sjhay if (bp > 7) 446183651Sjhay return; 447183651Sjhay ps = memmem(addr, klen, op, sizeof(op)); 448183651Sjhay if (ps != NULL) { 449183651Sjhay p = ps + 4; /* past ufs: */ 450183651Sjhay DPRINTF("Found it at 0x%x\n", (int)ps); 451183651Sjhay p[0] = 'a'; p[1] = 'd'; p[2] = '0'; /* ad0 */ 452183651Sjhay p += 3; 453183651Sjhay if (bs > 0) { 454183651Sjhay /* append slice */ 455183651Sjhay *p++ = 's'; 456183651Sjhay *p++ = bs + '0'; 457183651Sjhay } 458183651Sjhay if (disk_layout != DL_SLICE) { 459183651Sjhay /* append partition */ 460183651Sjhay *p++ = bp + 'a'; 461183651Sjhay } 462183651Sjhay *p = '\0'; 463183651Sjhay } else { 464183651Sjhay ps = memmem(addr, klen, op2, sizeof(op2) - 1); 465183651Sjhay if (ps != NULL) { 466183651Sjhay p = ps + sizeof(op2) - 1; 467183651Sjhay DPRINTF("Found it at 0x%x\n", (int)ps); 468183651Sjhay if (*p == 's') { 469183651Sjhay /* fix slice */ 470183651Sjhay p++; 471183651Sjhay *p++ = bs + '0'; 472183651Sjhay } 473183651Sjhay if (*p == 'a') 474183651Sjhay *p = bp + 'a'; 475183651Sjhay } 476183651Sjhay } 477183651Sjhay if (ps == NULL) { 478183651Sjhay printf("Could not locate \"%s\" to fix kernel boot device, " 479183651Sjhay "check ROOTDEVNAME is set\n", op); 480183651Sjhay return; 481183651Sjhay } 482183651Sjhay DPRINTF("Changed boot device to %s\n", ps); 483183651Sjhay} 484183651Sjhay#endif 485