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