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