1/*- 2 * Copyright (c) 2008 John Hay 3 * Copyright (c) 2006 Warner Losh 4 * Copyright (c) 1998 Robert Nordier 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms are freely 8 * permitted provided that the above copyright notice and this 9 * paragraph and the following disclaimer are duplicated in all 10 * such forms. 11 * 12 * This software is provided "AS IS" and without any express or 13 * implied warranties, including, without limitation, the implied 14 * warranties of merchantability and fitness for a particular 15 * purpose. 16 */ 17 18#include <sys/cdefs.h> 19__FBSDID("$FreeBSD$"); 20 21#include <sys/param.h> 22#include <sys/disklabel.h> 23#include <sys/diskmbr.h> 24#include <sys/dirent.h> 25#include <sys/reboot.h> 26 27#include <machine/elf.h> 28 29#include <stdarg.h> 30 31#include "lib.h" 32#include "board.h" 33 34#define RBX_ASKNAME 0x0 /* -a */ 35#define RBX_SINGLE 0x1 /* -s */ 36/* 0x2 is reserved for log2(RB_NOSYNC). */ 37/* 0x3 is reserved for log2(RB_HALT). */ 38/* 0x4 is reserved for log2(RB_INITNAME). */ 39#define RBX_DFLTROOT 0x5 /* -r */ 40/* #define RBX_KDB 0x6 -d */ 41/* 0x7 is reserved for log2(RB_RDONLY). */ 42/* 0x8 is reserved for log2(RB_DUMP). */ 43/* 0x9 is reserved for log2(RB_MINIROOT). */ 44#define RBX_CONFIG 0xa /* -c */ 45#define RBX_VERBOSE 0xb /* -v */ 46/* #define RBX_SERIAL 0xc -h */ 47/* #define RBX_CDROM 0xd -C */ 48/* 0xe is reserved for log2(RB_POWEROFF). */ 49#define RBX_GDB 0xf /* -g */ 50/* #define RBX_MUTE 0x10 -m */ 51/* 0x11 is reserved for log2(RB_SELFTEST). */ 52/* 0x12 is reserved for boot programs. */ 53/* 0x13 is reserved for boot programs. */ 54/* #define RBX_PAUSE 0x14 -p */ 55/* #define RBX_QUIET 0x15 -q */ 56#define RBX_NOINTR 0x1c /* -n */ 57/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */ 58/* #define RBX_DUAL 0x1d -D */ 59/* 0x1f is reserved for log2(RB_BOOTINFO). */ 60 61/* pass: -a, -s, -r, -v, -g */ 62#define RBX_MASK (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \ 63 OPT_SET(RBX_DFLTROOT) | \ 64 OPT_SET(RBX_VERBOSE) | \ 65 OPT_SET(RBX_GDB)) 66 67#define PATH_DOTCONFIG "/boot.config" 68#define PATH_CONFIG "/boot/config" 69//#define PATH_KERNEL "/boot/kernel/kernel" 70#define PATH_KERNEL "/boot/kernel/kernel.gz.tramp" 71 72extern uint32_t _end; 73 74#define NOPT 6 75 76#define OPT_SET(opt) (1 << (opt)) 77#define OPT_CHECK(opt) ((opts) & OPT_SET(opt)) 78 79static const char optstr[NOPT] = "agnrsv"; 80static const unsigned char flags[NOPT] = { 81 RBX_ASKNAME, 82 RBX_GDB, 83 RBX_NOINTR, 84 RBX_DFLTROOT, 85 RBX_SINGLE, 86 RBX_VERBOSE 87}; 88 89unsigned dsk_start; 90static char cmd[512]; 91static char kname[1024]; 92static uint32_t opts; 93static int dsk_meta; 94 95static void load(void); 96static int parse(void); 97static int xfsread(ino_t, void *, size_t); 98static int dskread(void *, unsigned, unsigned); 99#ifdef FIXUP_BOOT_DRV 100static void fixup_boot_drv(caddr_t, int, int, int); 101#endif 102 103#define UFS_SMALL_CGBASE 104#include "ufsread.c" 105 106#ifdef DEBUG 107#define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) 108#else 109#define DPRINTF(fmt, ...) 110#endif 111 112static inline int 113xfsread(ino_t inode, void *buf, size_t nbyte) 114{ 115 if ((size_t)fsread(inode, buf, nbyte) != nbyte) 116 return -1; 117 return 0; 118} 119 120static inline void 121getstr(int c) 122{ 123 char *s; 124 125 s = cmd; 126 if (c == 0) 127 c = getc(10000); 128 for (;;) { 129 switch (c) { 130 case 0: 131 break; 132 case '\177': 133 case '\b': 134 if (s > cmd) { 135 s--; 136 printf("\b \b"); 137 } 138 break; 139 case '\n': 140 case '\r': 141 *s = 0; 142 return; 143 default: 144 if (s - cmd < sizeof(cmd) - 1) 145 *s++ = c; 146 xputchar(c); 147 } 148 c = getc(10000); 149 } 150} 151 152int 153main(void) 154{ 155 int autoboot, c = 0; 156 ino_t ino; 157 158 dmadat = (void *)(0x20000000 + (16 << 20)); 159 board_init(); 160 161 autoboot = 1; 162 163 /* Process configuration file */ 164 if ((ino = lookup(PATH_CONFIG)) || 165 (ino = lookup(PATH_DOTCONFIG))) 166 fsread(ino, cmd, sizeof(cmd)); 167 168 if (*cmd) { 169 if (parse()) 170 autoboot = 0; 171 printf("%s: %s\n", PATH_CONFIG, cmd); 172 /* Do not process this command twice */ 173 *cmd = 0; 174 } 175 176 if (*kname == '\0') 177 strcpy(kname, PATH_KERNEL); 178 179 /* Present the user with the boot2 prompt. */ 180 for (;;) { 181 printf("\nDefault: %s\nboot: ", kname); 182 if (!autoboot || 183 (OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0)) 184 getstr(c); 185 xputchar('\n'); 186 autoboot = 0; 187 c = 0; 188 if (parse()) 189 xputchar('\a'); 190 else 191 load(); 192 } 193} 194 195static void 196load(void) 197{ 198 Elf32_Ehdr eh; 199 static Elf32_Phdr ep[2]; 200 caddr_t p; 201 ino_t ino; 202 uint32_t addr; 203 int i, j; 204#ifdef FIXUP_BOOT_DRV 205 caddr_t staddr; 206 int klen; 207 208 staddr = (caddr_t)0xffffffff; 209 klen = 0; 210#endif 211 if (!(ino = lookup(kname))) { 212 if (!ls) 213 printf("No %s\n", kname); 214 return; 215 } 216 if (xfsread(ino, &eh, sizeof(eh))) 217 return; 218 if (!IS_ELF(eh)) { 219 printf("Invalid %s\n", "format"); 220 return; 221 } 222 fs_off = eh.e_phoff; 223 for (j = i = 0; i < eh.e_phnum && j < 2; i++) { 224 if (xfsread(ino, ep + j, sizeof(ep[0]))) 225 return; 226 if (ep[j].p_type == PT_LOAD) 227 j++; 228 } 229 for (i = 0; i < 2; i++) { 230 p = (caddr_t)ep[i].p_paddr; 231 fs_off = ep[i].p_offset; 232#ifdef FIXUP_BOOT_DRV 233 if (staddr == (caddr_t)0xffffffff) 234 staddr = p; 235 klen += ep[i].p_filesz; 236#endif 237 if (xfsread(ino, p, ep[i].p_filesz)) 238 return; 239 } 240 addr = eh.e_entry; 241#ifdef FIXUP_BOOT_DRV 242 fixup_boot_drv(staddr, klen, bootslice, bootpart); 243#endif 244 ((void(*)(int))addr)(opts & RBX_MASK); 245} 246 247static int 248parse() 249{ 250 char *arg = cmd; 251 char *ep, *p; 252 int c, i; 253 254 while ((c = *arg++)) { 255 if (c == ' ' || c == '\t' || c == '\n') 256 continue; 257 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 258 ep = p; 259 if (*p) 260 *p++ = 0; 261 if (c == '-') { 262 while ((c = *arg++)) { 263 for (i = 0; c != optstr[i]; i++) 264 if (i == NOPT - 1) 265 return -1; 266 opts ^= OPT_SET(flags[i]); 267 } 268 } else { 269 arg--; 270 if ((i = ep - arg)) { 271 if ((size_t)i >= sizeof(kname)) 272 return -1; 273 memcpy(kname, arg, i + 1); 274 } 275 } 276 arg = p; 277 } 278 return 0; 279} 280 281static int 282dskread(void *buf, unsigned lba, unsigned nblk) 283{ 284 struct dos_partition *dp; 285 struct disklabel *d; 286 char *sec; 287 int i; 288 289 if (!dsk_meta) { 290 sec = dmadat->secbuf; 291 dsk_start = 0; 292 if (drvread(sec, DOSBBSECTOR, 1)) 293 return -1; 294 dp = (void *)(sec + DOSPARTOFF); 295 for (i = 0; i < NDOSPART; i++) { 296 if (dp[i].dp_typ == DOSPTYP_386BSD) 297 break; 298 } 299 if (i == NDOSPART) 300 return -1; 301 /* 302 * Although dp_start is aligned within the disk 303 * partition structure, DOSPARTOFF is 446, which is 304 * only word (2) aligned, not longword (4) aligned. 305 * Cope by using memcpy to fetch the start of this 306 * partition. 307 */ 308 memcpy(&dsk_start, &dp[1].dp_start, 4); 309 if (drvread(sec, dsk_start + LABELSECTOR, 1)) 310 return -1; 311 d = (void *)(sec + LABELOFFSET); 312 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 313 printf("Invalid %s\n", "label"); 314 return -1; 315 } 316 if (!d->d_partitions[0].p_size) { 317 printf("Invalid %s\n", "partition"); 318 return -1; 319 } 320 dsk_start += d->d_partitions[0].p_offset; 321 dsk_start -= d->d_partitions[RAW_PART].p_offset; 322 dsk_meta++; 323 } 324 return drvread(buf, dsk_start + lba, nblk); 325} 326 327#ifdef FIXUP_BOOT_DRV 328/* 329 * fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel 330 * and change it to what was specified on the comandline or /boot.conf 331 * file or to what was encountered on the disk. It will try to handle 3 332 * different disk layouts, raw (dangerously dedicated), slice only and 333 * slice + partition. It will look for the following strings in the 334 * kernel, but if it is one of the first three, the string in the kernel 335 * must use the correct form to match the actual disk layout: 336 * - ufs:ad0a 337 * - ufs:ad0s1 338 * - ufs:ad0s1a 339 * - ufs:ROOTDEVNAME 340 * In the case of the first three strings, only the "a" at the end and 341 * the "1" after the "s" will be modified, if they exist. The string 342 * length will not be changed. In the case of the last string, the 343 * whole string will be built up and nul, '\0' terminated. 344 */ 345static void 346fixup_boot_drv(caddr_t addr, int klen, int bs, int bp) 347{ 348 const u_int8_t op[] = "ufs:ROOTDEVNAME"; 349 const u_int8_t op2[] = "ufs:ad0"; 350 u_int8_t *p, *ps; 351 352 DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n", 353 (int)addr, klen, bs, bp); 354 if (bs > 4) 355 return; 356 if (bp > 7) 357 return; 358 ps = memmem(addr, klen, op, sizeof(op)); 359 if (ps != NULL) { 360 p = ps + 4; /* past ufs: */ 361 DPRINTF("Found it at 0x%x\n", (int)ps); 362 p[0] = 'a'; p[1] = 'd'; p[2] = '0'; /* ad0 */ 363 p += 3; 364 if (bs > 0) { 365 /* append slice */ 366 *p++ = 's'; 367 *p++ = bs + '0'; 368 } 369 if (disk_layout != DL_SLICE) { 370 /* append partition */ 371 *p++ = bp + 'a'; 372 } 373 *p = '\0'; 374 } else { 375 ps = memmem(addr, klen, op2, sizeof(op2) - 1); 376 if (ps != NULL) { 377 p = ps + sizeof(op2) - 1; 378 DPRINTF("Found it at 0x%x\n", (int)ps); 379 if (*p == 's') { 380 /* fix slice */ 381 p++; 382 *p++ = bs + '0'; 383 } 384 if (*p == 'a') 385 *p = bp + 'a'; 386 } 387 } 388 if (ps == NULL) { 389 printf("Could not locate \"%s\" to fix kernel boot device, " 390 "check ROOTDEVNAME is set\n", op); 391 return; 392 } 393 DPRINTF("Changed boot device to %s\n", ps); 394} 395#endif 396