1 /*- 2 * Copyright (c) 2008 John Hay 3 * Copyright (c) 2006 M Warner Losh <imp@freebsd.org> 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 #include "paths.h" 34 #include "rbx.h" 35 36 #undef PATH_KERNEL 37 #define PATH_KERNEL "/boot/kernel/kernel.gz.tramp" 38 39 extern uint32_t _end; 40 41 #define NOPT 6 42 43 static const char optstr[NOPT] = "agnrsv"; 44 static const unsigned char bootflags[NOPT] = { 45 RBX_ASKNAME, 46 RBX_GDB, 47 RBX_NOINTR, 48 RBX_DFLTROOT, 49 RBX_SINGLE, 50 RBX_VERBOSE 51 }; 52 53 unsigned board_id; /* board type to pass to kernel, if set by board_* code */ 54 unsigned dsk_start; 55 static char cmd[512]; 56 static char kname[1024]; 57 static uint32_t opts; 58 static uint8_t dsk_meta; 59 60 int main(void); 61 static void load(void); 62 static int parse(void); 63 static int dskread(void *, unsigned, unsigned); 64 #ifdef FIXUP_BOOT_DRV 65 static void fixup_boot_drv(caddr_t, int, int, int); 66 #endif 67 68 #define UFS_SMALL_CGBASE 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 int autoboot, c = 0; 121 ufs_ino_t ino; 122 123 dmadat = (void *)(0x20000000 + (16 << 20)); 124 board_init(); 125 126 autoboot = 1; 127 128 /* Process configuration file */ 129 if ((ino = lookup(PATH_CONFIG)) || 130 (ino = lookup(PATH_DOTCONFIG))) 131 fsread(ino, cmd, sizeof(cmd)); 132 133 if (*cmd) { 134 if (parse()) 135 autoboot = 0; 136 printf("%s: %s\n", PATH_CONFIG, cmd); 137 /* Do not process this command twice */ 138 *cmd = 0; 139 } 140 141 if (*kname == '\0') 142 strcpy(kname, PATH_KERNEL); 143 144 /* Present the user with the boot2 prompt. */ 145 for (;;) { 146 printf("\nDefault: %s\nboot: ", kname); 147 if (!autoboot || 148 (OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0)) 149 getstr(c); 150 xputchar('\n'); 151 autoboot = 0; 152 c = 0; 153 if (parse()) 154 xputchar('\a'); 155 else 156 load(); 157 } 158 return (1); 159 } 160 161 static void 162 load(void) 163 { 164 Elf32_Ehdr eh; 165 static Elf32_Phdr ep[2]; 166 caddr_t p; 167 ufs_ino_t ino; 168 uint32_t addr; 169 int i, j; 170 #ifdef FIXUP_BOOT_DRV 171 caddr_t staddr; 172 int klen; 173 174 staddr = (caddr_t)0xffffffff; 175 klen = 0; 176 #endif 177 if (!(ino = lookup(kname))) { 178 if (!ls) 179 printf("No %s\n", kname); 180 return; 181 } 182 if (xfsread(ino, &eh, sizeof(eh))) 183 return; 184 if (!IS_ELF(eh)) { 185 printf("Invalid %s\n", "format"); 186 return; 187 } 188 fs_off = eh.e_phoff; 189 for (j = i = 0; i < eh.e_phnum && j < 2; i++) { 190 if (xfsread(ino, ep + j, sizeof(ep[0]))) 191 return; 192 if (ep[j].p_type == PT_LOAD) 193 j++; 194 } 195 for (i = 0; i < 2; i++) { 196 p = (caddr_t)ep[i].p_paddr; 197 fs_off = ep[i].p_offset; 198 #ifdef FIXUP_BOOT_DRV 199 if (staddr == (caddr_t)0xffffffff) 200 staddr = p; 201 klen += ep[i].p_filesz; 202 #endif 203 if (xfsread(ino, p, ep[i].p_filesz)) 204 return; 205 } 206 addr = eh.e_entry; 207 #ifdef FIXUP_BOOT_DRV 208 fixup_boot_drv(staddr, klen, bootslice, bootpart); 209 #endif 210 ((void(*)(int, int, int, int))addr)(opts & RBX_MASK, board_id, 0, 0); 211 } 212 213 static int 214 parse() 215 { 216 char *arg = cmd; 217 char *ep, *p; 218 int c, i; 219 220 while ((c = *arg++)) { 221 if (c == ' ' || c == '\t' || c == '\n') 222 continue; 223 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 224 ep = p; 225 if (*p) 226 *p++ = 0; 227 if (c == '-') { 228 while ((c = *arg++)) { 229 for (i = 0; c != optstr[i]; i++) 230 if (i == NOPT - 1) 231 return -1; 232 opts ^= OPT_SET(bootflags[i]); 233 } 234 } else { 235 arg--; 236 if ((i = ep - arg)) { 237 if ((size_t)i >= sizeof(kname)) 238 return -1; 239 memcpy(kname, arg, i + 1); 240 } 241 } 242 arg = p; 243 } 244 return 0; 245 } 246 247 static int 248 dskread(void *buf, unsigned lba, unsigned nblk) 249 { 250 struct dos_partition *dp; 251 struct disklabel *d; 252 char *sec; 253 int i; 254 255 if (!dsk_meta) { 256 sec = dmadat->secbuf; 257 dsk_start = 0; 258 if (drvread(sec, DOSBBSECTOR, 1)) 259 return -1; 260 dp = (void *)(sec + DOSPARTOFF); 261 for (i = 0; i < NDOSPART; i++) { 262 if (dp[i].dp_typ == DOSPTYP_386BSD) 263 break; 264 } 265 if (i == NDOSPART) 266 return -1; 267 /* 268 * Although dp_start is aligned within the disk 269 * partition structure, DOSPARTOFF is 446, which is 270 * only word (2) aligned, not longword (4) aligned. 271 * Cope by using memcpy to fetch the start of this 272 * partition. 273 */ 274 memcpy(&dsk_start, &dp[1].dp_start, 4); 275 if (drvread(sec, dsk_start + LABELSECTOR, 1)) 276 return -1; 277 d = (void *)(sec + LABELOFFSET); 278 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 279 printf("Invalid %s\n", "label"); 280 return -1; 281 } 282 if (!d->d_partitions[0].p_size) { 283 printf("Invalid %s\n", "partition"); 284 return -1; 285 } 286 dsk_start += d->d_partitions[0].p_offset; 287 dsk_start -= d->d_partitions[RAW_PART].p_offset; 288 dsk_meta++; 289 } 290 return drvread(buf, dsk_start + lba, nblk); 291 } 292 293 #ifdef FIXUP_BOOT_DRV 294 /* 295 * fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel 296 * and change it to what was specified on the comandline or /boot.conf 297 * file or to what was encountered on the disk. It will try to handle 3 298 * different disk layouts, raw (dangerously dedicated), slice only and 299 * slice + partition. It will look for the following strings in the 300 * kernel, but if it is one of the first three, the string in the kernel 301 * must use the correct form to match the actual disk layout: 302 * - ufs:ad0a 303 * - ufs:ad0s1 304 * - ufs:ad0s1a 305 * - ufs:ROOTDEVNAME 306 * In the case of the first three strings, only the "a" at the end and 307 * the "1" after the "s" will be modified, if they exist. The string 308 * length will not be changed. In the case of the last string, the 309 * whole string will be built up and nul, '\0' terminated. 310 */ 311 static void 312 fixup_boot_drv(caddr_t addr, int klen, int bs, int bp) 313 { 314 const u_int8_t op[] = "ufs:ROOTDEVNAME"; 315 const u_int8_t op2[] = "ufs:ad0"; 316 u_int8_t *p, *ps; 317 318 DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n", 319 (int)addr, klen, bs, bp); 320 if (bs > 4) 321 return; 322 if (bp > 7) 323 return; 324 ps = memmem(addr, klen, op, sizeof(op)); 325 if (ps != NULL) { 326 p = ps + 4; /* past ufs: */ 327 DPRINTF("Found it at 0x%x\n", (int)ps); 328 p[0] = 'a'; p[1] = 'd'; p[2] = '0'; /* ad0 */ 329 p += 3; 330 if (bs > 0) { 331 /* append slice */ 332 *p++ = 's'; 333 *p++ = bs + '0'; 334 } 335 if (disk_layout != DL_SLICE) { 336 /* append partition */ 337 *p++ = bp + 'a'; 338 } 339 *p = '\0'; 340 } else { 341 ps = memmem(addr, klen, op2, sizeof(op2) - 1); 342 if (ps != NULL) { 343 p = ps + sizeof(op2) - 1; 344 DPRINTF("Found it at 0x%x\n", (int)ps); 345 if (*p == 's') { 346 /* fix slice */ 347 p++; 348 *p++ = bs + '0'; 349 } 350 if (*p == 'a') 351 *p = bp + 'a'; 352 } 353 } 354 if (ps == NULL) { 355 printf("Could not locate \"%s\" to fix kernel boot device, " 356 "check ROOTDEVNAME is set\n", op); 357 return; 358 } 359 DPRINTF("Changed boot device to %s\n", ps); 360 } 361 #endif 362