1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (c) 2014 Joyent, Inc. All rights reserved. 14 * Copyright (c) 2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> 15 */ 16 17 /* 18 * Just like in i86pc, we too get the joys of mimicking the SPARC boot system. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/param.h> 23 #include <sys/bootconf.h> 24 #include <sys/bootsvcs.h> 25 #include <sys/boot_console.h> 26 #include <sys/atag.h> 27 #include <sys/varargs.h> 28 #include <sys/cmn_err.h> 29 #include <sys/sysmacros.h> 30 #include <sys/systm.h> 31 #include <sys/ctype.h> 32 #include <sys/bootstat.h> 33 #include <sys/privregs.h> 34 #include <sys/cpu_asm.h> 35 #include <sys/boot_mmu.h> 36 #include <sys/elf.h> 37 #include <sys/archsystm.h> 38 39 static bootops_t bootop; 40 41 /* 42 * Debugging help 43 */ 44 static int fakebop_prop_debug = 0; 45 static int fakebop_alloc_debug = 0; 46 static int fakebop_atag_debug = 0; 47 48 static uint_t kbm_debug = 1; 49 #define DBG_MSG(x) { if (kbm_debug) bcons_puts(x); bcons_puts("\n"); } 50 #define BUFFERSIZE 256 51 static char buffer[BUFFERSIZE]; 52 53 /* 54 * fakebop memory allocations scheme 55 * 56 * It's a virtual world out there. The loader thankfully tells us all the areas 57 * that it has mapped for us and it also tells us about the page table arena -- 58 * a set of addresses that have already been set aside for us. We have two 59 * different kinds of allocations to worry about: 60 * 61 * o Those that specify a particular vaddr 62 * o Those that do not specify a particular vaddr 63 * 64 * Those that do not specify a particular vaddr will come out of our scratch 65 * space which is a fixed size arena of 16 MB (FAKEBOP_ALLOC_SIZE) that we set 66 * aside at the beginning of the allocator. If we end up running out of that 67 * then we'll go ahead and figure out a slightly larger area to worry about. 68 * 69 * Now, for those that do specify a particular vaddr we'll allocate more 70 * physical address space for it. The loader set aside enough L2 page tables for 71 * us that we'll go ahead and use the next 4k aligned address. 72 */ 73 #define FAKEBOP_ALLOC_SIZE (16 * 1024 * 1024) 74 75 static size_t bop_alloc_scratch_size; 76 static uintptr_t bop_alloc_scratch_next; /* Next scratch address */ 77 static uintptr_t bop_alloc_scratch_last; /* Last scratch address */ 78 79 static uintptr_t bop_alloc_pnext; /* Next paddr */ 80 static uintptr_t bop_alloc_plast; /* cross this paddr and panic */ 81 82 #define BI_HAS_RAMDISK 0x1 83 84 /* 85 * TODO Generalize this 86 * This is the set of information tha we want to gather from the various atag 87 * headers. This is simple and naive and will need to evolve as we have 88 * additional boards beyond just the RPi. 89 */ 90 typedef struct bootinfo { 91 uint_t bi_flags; 92 char *bi_cmdline; 93 uint32_t bi_ramdisk; 94 uint32_t bi_ramsize; 95 } bootinfo_t; 96 97 static bootinfo_t bootinfo; /* Simple set of boot information */ 98 99 static struct boot_syscalls bop_sysp = { 100 bcons_getchar, 101 bcons_putchar, 102 bcons_ischar, 103 }; 104 105 /* 106 * stuff to store/report/manipulate boot property settings. 107 */ 108 typedef struct bootprop { 109 struct bootprop *bp_next; 110 char *bp_name; 111 uint_t bp_vlen; 112 char *bp_value; 113 } bootprop_t; 114 115 static bootprop_t *bprops = NULL; 116 117 static void 118 hexdump_stack() 119 { 120 extern char t0stack[]; 121 122 uint8_t *start = (uint8_t *)t0stack; 123 uint8_t *end = start + DEFAULTSTKSZ; 124 uint8_t *ptr; 125 int i; 126 127 bop_printf(NULL, "stack (fp = %x):\n", getfp()); 128 129 ptr = (uint8_t *)(getfp() & ~0xf); 130 if (ptr <= start || ptr >= end) 131 ptr = start; 132 133 while (ptr < end) { 134 uint32_t *tmp = (uint32_t *)ptr; 135 136 bop_printf(NULL, "%p: %08x %08x %08x %08x\n", ptr, 137 tmp[0], tmp[1], tmp[2], tmp[3]); 138 139 ptr += 16; 140 } 141 142 } 143 144 void 145 bop_panic(const char *msg) 146 { 147 bop_printf(NULL, "ARM bop_panic:\n%s\n", msg); 148 149 hexdump_stack(); 150 151 bop_printf(NULL, "Spinning Forever...", msg); 152 for (;;) 153 ; 154 } 155 156 /* 157 * XXX This is just a hack to let us see a bit more about what's going on. 158 * Normally we'd use vsnprintf, but that includes sys/systm.h which requires 159 * almost every header platform header in the world. Also, we're using hex, 160 * because hex is cool. Actually, we're really using it because it means we can 161 * bitshift instead of divide. There is no integer division in ARMv6 natively. 162 * Oops. 163 */ 164 static char * 165 fakebop_hack_ultostr(unsigned long value, char *ptr) 166 { 167 ulong_t t, val = (ulong_t)value; 168 char c; 169 170 do { 171 c = (char)('0' + val - 16 * (t = (val >> 4))); 172 if (c > '9') 173 c += 'A' - '9' - 1; 174 *--ptr = c; 175 } while ((val = t) != 0); 176 177 *--ptr = 'x'; 178 *--ptr = '0'; 179 180 return (ptr); 181 } 182 183 /* 184 * We need to map and reserve the scratch arena. As a part of this we'll go 185 * through and set up the right place for other paddrs. 186 */ 187 static void 188 fakebop_alloc_init(atag_header_t *chain) 189 { 190 uintptr_t pstart, vstart, pmax; 191 size_t len; 192 atag_illumos_mapping_t *aimp; 193 atag_illumos_status_t *aisp; 194 195 aisp = (atag_illumos_status_t *)atag_find(chain, ATAG_ILLUMOS_STATUS); 196 if (aisp == NULL) 197 bop_panic("missing ATAG_ILLUMOS_STATUS!\n"); 198 pstart = aisp->ais_freemem + aisp->ais_freeused; 199 /* Align to next 1 MB boundary */ 200 if (pstart & MMU_PAGEOFFSET1M) { 201 pstart &= MMU_PAGEMASK1M; 202 pstart += MMU_PAGESIZE1M; 203 } 204 len = FAKEBOP_ALLOC_SIZE; 205 vstart = pstart; 206 207 pmax = 0xffffffff; 208 /* Make sure the paddrs and vaddrs don't overlap at all */ 209 for (aimp = 210 (atag_illumos_mapping_t *)atag_find(chain, ATAG_ILLUMOS_MAPPING); 211 aimp != NULL; aimp = 212 (atag_illumos_mapping_t *)atag_find(atag_next(&aimp->aim_header), 213 ATAG_ILLUMOS_MAPPING)) { 214 if (aimp->aim_paddr < pstart && 215 aimp->aim_paddr + aimp->aim_vlen > pstart) 216 bop_panic("phys addresses overlap\n"); 217 if (pstart < aimp->aim_paddr && pstart + len > aimp->aim_paddr) 218 bop_panic("phys addresses overlap\n"); 219 if (aimp->aim_vaddr < vstart && aimp->aim_vaddr + 220 aimp->aim_vlen > vstart) 221 bop_panic("virt addreses overlap\n"); 222 if (vstart < aimp->aim_vaddr && vstart + len > aimp->aim_vaddr) 223 bop_panic("virt addresses overlap\n"); 224 225 if (aimp->aim_paddr > pstart && aimp->aim_paddr < pmax) 226 pmax = aimp->aim_paddr; 227 } 228 229 armboot_mmu_map(pstart, vstart, len, PF_R | PF_W | PF_X); 230 bop_alloc_scratch_next = vstart; 231 bop_alloc_scratch_last = vstart + len; 232 bop_alloc_scratch_size = len; 233 234 bop_alloc_pnext = pstart + len; 235 bop_alloc_plast = pmax; 236 } 237 238 #define DUMP_ATAG_VAL(name, val) \ 239 do { \ 240 DBG_MSG("\t" name ":"); \ 241 bcons_puts("\t"); \ 242 DBG_MSG(fakebop_hack_ultostr((val), \ 243 &buffer[BUFFERSIZE-1])); \ 244 } while (0) 245 246 static void 247 fakebop_dump_tags(void *tagstart) 248 { 249 atag_header_t *h = tagstart; 250 atag_core_t *acp; 251 atag_mem_t *amp; 252 atag_cmdline_t *alp; 253 atag_initrd_t *aip; 254 atag_illumos_status_t *aisp; 255 atag_illumos_mapping_t *aimp; 256 const char *tname; 257 int i; 258 char *c; 259 260 DBG_MSG("starting point:"); 261 DBG_MSG(fakebop_hack_ultostr((uintptr_t)h, &buffer[BUFFERSIZE-1])); 262 DBG_MSG("first atag size:"); 263 DBG_MSG(fakebop_hack_ultostr(h->ah_size, &buffer[BUFFERSIZE-1])); 264 DBG_MSG("first atag tag:"); 265 DBG_MSG(fakebop_hack_ultostr(h->ah_tag, &buffer[BUFFERSIZE-1])); 266 while (h != NULL) { 267 switch (h->ah_tag) { 268 case ATAG_CORE: 269 tname = "ATAG_CORE"; 270 break; 271 case ATAG_MEM: 272 tname = "ATAG_MEM"; 273 break; 274 case ATAG_VIDEOTEXT: 275 tname = "ATAG_VIDEOTEXT"; 276 break; 277 case ATAG_RAMDISK: 278 tname = "ATAG_RAMDISK"; 279 break; 280 case ATAG_INITRD2: 281 tname = "ATAG_INITRD2"; 282 break; 283 case ATAG_SERIAL: 284 tname = "ATAG_SERIAL"; 285 break; 286 case ATAG_REVISION: 287 tname = "ATAG_REVISION"; 288 break; 289 case ATAG_VIDEOLFB: 290 tname = "ATAG_VIDEOLFB"; 291 break; 292 case ATAG_CMDLINE: 293 tname = "ATAG_CMDLINE"; 294 break; 295 case ATAG_ILLUMOS_STATUS: 296 tname = "ATAG_ILLUMOS_STATUS"; 297 break; 298 case ATAG_ILLUMOS_MAPPING: 299 tname = "ATAG_ILLUMOS_MAPPING"; 300 break; 301 default: 302 tname = fakebop_hack_ultostr(h->ah_tag, 303 &buffer[BUFFERSIZE-1]); 304 break; 305 } 306 DBG_MSG("tag:"); 307 DBG_MSG(tname); 308 DBG_MSG("size:"); 309 DBG_MSG(fakebop_hack_ultostr(h->ah_size, 310 &buffer[BUFFERSIZE-1])); 311 /* Extended information */ 312 switch (h->ah_tag) { 313 case ATAG_CORE: 314 if (h->ah_size == 2) { 315 DBG_MSG("ATAG_CORE has no extra information"); 316 } else { 317 acp = (atag_core_t *)h; 318 DUMP_ATAG_VAL("flags", acp->ac_flags); 319 DUMP_ATAG_VAL("pagesize", acp->ac_pagesize); 320 DUMP_ATAG_VAL("rootdev", acp->ac_rootdev); 321 } 322 break; 323 case ATAG_MEM: 324 amp = (atag_mem_t *)h; 325 DUMP_ATAG_VAL("size", amp->am_size); 326 DUMP_ATAG_VAL("start", amp->am_start); 327 break; 328 case ATAG_INITRD2: 329 aip = (atag_initrd_t *)h; 330 DUMP_ATAG_VAL("size", aip->ai_size); 331 DUMP_ATAG_VAL("start", aip->ai_start); 332 break; 333 case ATAG_CMDLINE: 334 alp = (atag_cmdline_t *)h; 335 DBG_MSG("\tcmdline:"); 336 /* 337 * We have no intelligent thing to wrap our tty at 80 338 * chars so we just do this a bit more manually for now. 339 */ 340 i = 0; 341 c = alp->al_cmdline; 342 while (*c != '\0') { 343 bcons_putchar(*c++); 344 if (++i == 72) { 345 bcons_puts("\n"); 346 i = 0; 347 } 348 } 349 bcons_puts("\n"); 350 break; 351 case ATAG_ILLUMOS_STATUS: 352 aisp = (atag_illumos_status_t *)h; 353 DUMP_ATAG_VAL("version", aisp->ais_version); 354 DUMP_ATAG_VAL("ptbase", aisp->ais_ptbase); 355 DUMP_ATAG_VAL("freemem", aisp->ais_freemem); 356 DUMP_ATAG_VAL("freeused", aisp->ais_freeused); 357 DUMP_ATAG_VAL("archive", aisp->ais_archive); 358 DUMP_ATAG_VAL("archivelen", aisp->ais_archivelen); 359 DUMP_ATAG_VAL("pt_arena", aisp->ais_pt_arena); 360 DUMP_ATAG_VAL("pt_arena_max", aisp->ais_pt_arena_max); 361 DUMP_ATAG_VAL("stext", aisp->ais_stext); 362 DUMP_ATAG_VAL("etext", aisp->ais_etext); 363 DUMP_ATAG_VAL("sdata", aisp->ais_sdata); 364 DUMP_ATAG_VAL("edata", aisp->ais_edata); 365 break; 366 case ATAG_ILLUMOS_MAPPING: 367 aimp = (atag_illumos_mapping_t *)h; 368 DUMP_ATAG_VAL("paddr", aimp->aim_paddr); 369 DUMP_ATAG_VAL("plen", aimp->aim_plen); 370 DUMP_ATAG_VAL("vaddr", aimp->aim_vaddr); 371 DUMP_ATAG_VAL("vlen", aimp->aim_vlen); 372 DUMP_ATAG_VAL("mapflags", aimp->aim_mapflags); 373 break; 374 default: 375 break; 376 } 377 h = atag_next(h); 378 } 379 } 380 381 static void 382 fakebop_getatags(void *tagstart) 383 { 384 atag_mem_t *amp; 385 atag_cmdline_t *alp; 386 atag_header_t *ahp = tagstart; 387 atag_illumos_status_t *aisp; 388 bootinfo_t *bp = &bootinfo; 389 boolean_t got_mem = B_FALSE; 390 391 bp->bi_flags = 0; 392 while (ahp != NULL) { 393 switch (ahp->ah_tag) { 394 case ATAG_MEM: 395 amp = (atag_mem_t *)ahp; 396 /* 397 * We may actually get more than one ATAG_MEM if the 398 * system has discontiguous physical memory 399 */ 400 if (got_mem) { 401 bop_printf(NULL, "found multiple ATAG_MEM\n"); 402 bop_printf(NULL, "ignoring: %#x - %#x\n", 403 amp->am_start, amp->am_start + 404 amp->am_size - 1); 405 break; 406 } 407 408 bootop.boot_mem.physinstalled.ml_address = amp->am_start; 409 bootop.boot_mem.physinstalled.ml_size = amp->am_size; 410 bootop.boot_mem.physinstalled.ml_prev = NULL; 411 bootop.boot_mem.physinstalled.ml_next = NULL; 412 got_mem = B_TRUE; 413 break; 414 case ATAG_CMDLINE: 415 alp = (atag_cmdline_t *)ahp; 416 bp->bi_cmdline = alp->al_cmdline; 417 break; 418 case ATAG_ILLUMOS_STATUS: 419 aisp = (atag_illumos_status_t *)ahp; 420 bp->bi_ramdisk = aisp->ais_archive; 421 bp->bi_ramsize = aisp->ais_archivelen; 422 bp->bi_flags |= BI_HAS_RAMDISK; 423 break; 424 default: 425 break; 426 } 427 ahp = atag_next(ahp); 428 } 429 } 430 431 /* 432 * We've been asked to allocate at a specific VA. Allocate the next ragne of 433 * physical addresses and go from there. 434 */ 435 static caddr_t 436 fakebop_alloc_hint(caddr_t virt, size_t size, int align) 437 { 438 uintptr_t start = P2ROUNDUP(bop_alloc_pnext, align); 439 if (fakebop_alloc_debug != 0) 440 bop_printf(NULL, "asked to allocate %d bytes at v/p %p/%p\n", 441 size, virt, start); 442 if (start + size > bop_alloc_plast) 443 bop_panic("fakebop_alloc_hint: No more physical address -_-\n"); 444 445 armboot_mmu_map(start, (uintptr_t)virt, size, PF_R | PF_W | PF_X); 446 bop_alloc_pnext = start + size; 447 return (virt); 448 } 449 450 static caddr_t 451 fakebop_alloc(struct bootops *bops, caddr_t virthint, size_t size, int align) 452 { 453 caddr_t start; 454 455 if (virthint != NULL) 456 return (fakebop_alloc_hint(virthint, size, align)); 457 if (fakebop_alloc_debug != 0) 458 bop_printf(bops, "asked to allocate %d bytes\n", size); 459 if (bop_alloc_scratch_next == 0) 460 bop_panic("fakebop_alloc_init not called"); 461 462 if (align == BO_ALIGN_DONTCARE || align == 0) 463 align = 4; 464 465 start = (caddr_t)P2ROUNDUP(bop_alloc_scratch_next, align); 466 if ((uintptr_t)start + size > bop_alloc_scratch_last) 467 bop_panic("fakebop_alloc: ran out of scratch space!\n"); 468 if (fakebop_alloc_debug != 0) 469 bop_printf(bops, "returning address: %p\n", start); 470 bop_alloc_scratch_next = (uintptr_t)start + size; 471 472 return (start); 473 } 474 475 static void 476 fakebop_free(struct bootops *bops, caddr_t virt, size_t size) 477 { 478 bop_panic("Called into fakebop_free"); 479 } 480 481 static int 482 fakebop_getproplen(struct bootops *bops, const char *pname) 483 { 484 bootprop_t *p; 485 486 if (fakebop_prop_debug) 487 bop_printf(NULL, "fakebop_getproplen: asked for %s\n", pname); 488 for (p = bprops; p != NULL; p = p->bp_next) { 489 if (strcmp(pname, p->bp_name) == 0) 490 return (p->bp_vlen); 491 } 492 if (fakebop_prop_debug != 0) 493 bop_printf(NULL, "prop %s not found\n", pname); 494 return (-1); 495 } 496 497 static int 498 fakebop_getprop(struct bootops *bops, const char *pname, void *value) 499 { 500 bootprop_t *p; 501 502 if (fakebop_prop_debug) 503 bop_printf(NULL, "fakebop_getprop: asked for %s\n", pname); 504 for (p = bprops; p != NULL; p = p->bp_next) { 505 if (strcmp(pname, p->bp_name) == 0) 506 break; 507 } 508 if (p == NULL) { 509 if (fakebop_prop_debug) 510 bop_printf(NULL, "fakebop_getprop: ENOPROP %s\n", 511 pname); 512 return (-1); 513 } 514 if (fakebop_prop_debug) 515 bop_printf(NULL, "fakebop_getprop: copying %d bytes to 0x%x\n", 516 p->bp_vlen, value); 517 bcopy(p->bp_value, value, p->bp_vlen); 518 return (0); 519 } 520 521 void 522 bop_printf(bootops_t *bop, const char *fmt, ...) 523 { 524 va_list ap; 525 526 va_start(ap, fmt); 527 (void) vsnprintf(buffer, BUFFERSIZE, fmt, ap); 528 va_end(ap); 529 bcons_puts(buffer); 530 } 531 532 static void 533 fakebop_setprop(char *name, int nlen, void *value, int vlen) 534 { 535 size_t size; 536 bootprop_t *bp; 537 caddr_t cur; 538 539 size = sizeof (bootprop_t) + nlen + 1 + vlen; 540 cur = fakebop_alloc(NULL, NULL, size, BO_ALIGN_DONTCARE); 541 bp = (bootprop_t *)cur; 542 if (bprops == NULL) { 543 bprops = bp; 544 bp->bp_next = NULL; 545 } else { 546 bp->bp_next = bprops; 547 bprops = bp; 548 } 549 cur += sizeof (bootprop_t); 550 bp->bp_name = cur; 551 bcopy(name, cur, nlen); 552 cur += nlen; 553 *cur = '\0'; 554 cur++; 555 bp->bp_vlen = vlen; 556 bp->bp_value = cur; 557 if (vlen > 0) 558 bcopy(value, cur, vlen); 559 560 if (fakebop_prop_debug) 561 bop_printf(NULL, "setprop - name: %s, nlen: %d, vlen: %d\n", 562 bp->bp_name, nlen, bp->bp_vlen); 563 } 564 565 static void 566 fakebop_setprop_string(char *name, char *value) 567 { 568 if (fakebop_prop_debug) 569 bop_printf(NULL, "setprop_string: %s->[%s]\n", name, value); 570 fakebop_setprop(name, strlen(name), value, strlen(value) + 1); 571 } 572 573 static void 574 fakebop_setprop_32(char *name, uint32_t value) 575 { 576 if (fakebop_prop_debug) 577 bop_printf(NULL, "setprop_32: %s->[%d]\n", name, value); 578 fakebop_setprop(name, strlen(name), (void *)&value, sizeof (value)); 579 } 580 581 static void 582 fakebop_setprop_64(char *name, uint64_t value) 583 { 584 if (fakebop_prop_debug) 585 bop_printf(NULL, "setprop_64: %s->[%lld]\n", name, value); 586 fakebop_setprop(name, strlen(name), (void *)&value, sizeof (value)); 587 } 588 589 /* 590 * Here we create a bunch of the initial boot properties. This includes what 591 * we've been passed in via the command line. It also includes a few values that 592 * we compute ourselves. 593 */ 594 static void 595 fakebop_bootprops_init(void) 596 { 597 int i = 0, proplen = 0, cmdline_len = 0, quote, cont; 598 static int stdout_val = 0; 599 char *c, *prop, *cmdline, *pname; 600 bootinfo_t *bp = &bootinfo; 601 602 /* 603 * Set the ramdisk properties for kobj_boot_mountroot() can succeed. 604 */ 605 if ((bp->bi_flags & BI_HAS_RAMDISK) != 0) { 606 fakebop_setprop_64("ramdisk_start", 607 (uint64_t)(uintptr_t)bp->bi_ramdisk); 608 fakebop_setprop_64("ramdisk_end", 609 (uint64_t)(uintptr_t)bp->bi_ramdisk + bp->bi_ramsize); 610 } 611 612 /* 613 * TODO Various arm devices may spit properties at the front just like 614 * i86xpv. We should do something about them at some point. 615 */ 616 617 /* 618 * Our boot parameters always wil start with kernel /platform/..., but 619 * the bootloader may in fact stick other properties in front of us. To 620 * deal with that we go ahead and we include them. We'll find the kernel 621 * and our properties set via -B when we finally find something without 622 * an equals sign, mainly kernel. 623 */ 624 c = bp->bi_cmdline; 625 prop = strstr(c, "kernel"); 626 if (prop == NULL) 627 bop_panic("failed to find kernel string in boot params!"); 628 /* Get us past the first kernel string */ 629 prop += 6; 630 while (ISSPACE(prop[0])) 631 prop++; 632 proplen = 0; 633 while (prop[proplen] != '\0' && !ISSPACE(prop[proplen])) 634 proplen++; 635 c = prop + proplen + 1; 636 if (proplen > 0) { 637 prop[proplen] = '\0'; 638 fakebop_setprop_string("boot-file", prop); 639 /* 640 * We strip the leading path from whoami so no matter what 641 * environment we enter into here from it is consistent and 642 * makes some amount of sense. 643 */ 644 if (strstr(prop, "/platform") != NULL) 645 prop = strstr(prop, "/platform"); 646 fakebop_setprop_string("whoami", prop); 647 } else { 648 bop_panic("no kernel string in boot params!"); 649 } 650 651 /* 652 * At this point we have two different sets of properties. Anything that 653 * involves -B is a boot property, otherwise it becomes part of the 654 * kernel command line and must be saved in its own property. 655 */ 656 cmdline = fakebop_alloc(NULL, NULL, strlen(c), BO_ALIGN_DONTCARE); 657 cmdline[0] = '\0'; 658 while (*c != '\0') { 659 660 /* 661 * Just blindly copy it to the commadline if we don't find it. 662 */ 663 if (c[0] != '-' || c[1] != 'B') { 664 cmdline[cmdline_len++] = *c; 665 cmdline[cmdline_len] = '\0'; 666 c++; 667 continue; 668 } 669 670 /* Get past "-B" */ 671 c += 2; 672 while (ISSPACE(*c)) 673 c++; 674 675 /* 676 * We have a series of comma separated key-value pairs to sift 677 * through here. The key and value are separated by an equals 678 * sign. The value may quoted with either a ' or ". Note, white 679 * space will also end the value (as it indicates that we have 680 * moved on from the -B argument. 681 */ 682 for (;;) { 683 if (*c == '\0' || ISSPACE(*c)) 684 break; 685 prop = strchr(c, '='); 686 if (prop == NULL) 687 break; 688 pname = c; 689 *prop = '\0'; 690 prop++; 691 proplen = 0; 692 quote = '\0'; 693 for (;;) { 694 if (prop[proplen] == '\0') 695 break; 696 697 if (proplen == 0 && (prop[0] == '\'' || 698 prop[0] == '"')) { 699 quote = prop[0]; 700 proplen++; 701 continue; 702 } 703 704 if (quote != '\0') { 705 if (prop[proplen] == quote) 706 quote = '\0'; 707 proplen++; 708 continue; 709 } 710 711 if (prop[proplen] == ',' || 712 ISSPACE(prop[proplen])) 713 break; 714 715 /* We just have a normal character */ 716 proplen++; 717 } 718 719 /* 720 * Save whether we should continue or not and update 'c' 721 * now as we will most likely clobber the string when we 722 * are done. 723 */ 724 cont = (prop[proplen] == ','); 725 if (prop[proplen] != '\0') 726 c = prop + proplen + 1; 727 else 728 c = prop + proplen; 729 730 if (proplen == 0) { 731 fakebop_setprop_string(pname, "true"); 732 } else { 733 /* 734 * When we copy the prop, do not include the 735 * quote. 736 */ 737 if (prop[0] == prop[proplen - 1] && 738 (prop[0] == '\'' || prop[0] == '"')) { 739 prop++; 740 proplen -= 2; 741 } 742 prop[proplen] = '\0'; 743 fakebop_setprop_string(pname, prop); 744 } 745 746 if (cont == 0) 747 break; 748 } 749 } 750 751 /* 752 * Yes, we actually set both names here. The latter is set because of 753 * 1275. 754 */ 755 fakebop_setprop_string("boot-args", cmdline); 756 fakebop_setprop_string("bootargs", cmdline); 757 758 /* 759 * Here are some things that we make up, just like our i86pc brethren. 760 */ 761 fakebop_setprop_32("stdout", 0); 762 fakebop_setprop_string("mfg-name", "ARMv6"); 763 fakebop_setprop_string("impl-arch-name", "ARMv6"); 764 } 765 766 /* 767 * Nominally this should try and look for bootenv.rc, but seriously, let's not. 768 * Instead for now all we're going to to do is look and make sure that console 769 * is set. We *should* do something with it, but we're not. 770 */ 771 void 772 boot_prop_finish(void) 773 { 774 int ret; 775 776 if (fakebop_getproplen(NULL, "console") <= 0) 777 bop_panic("console not set"); 778 } 779 780 /*ARGSUSED*/ 781 int 782 boot_compinfo(int fd, struct compinfo *cbp) 783 { 784 cbp->iscmp = 0; 785 cbp->blksize = MAXBSIZE; 786 return (0); 787 } 788 789 extern void (*exception_table[])(void); 790 791 void 792 primordial_handler(void) 793 { 794 bop_panic("TRAP"); 795 } 796 797 void 798 primordial_svc(void *arg) 799 { 800 struct regs *r = (struct regs *)arg; 801 uint32_t *insaddr = (uint32_t *)(r->r_lr - 4); 802 803 bop_printf(NULL, "TRAP: svc #%d from %p\n", *insaddr & 0x00ffffff, 804 insaddr); 805 } 806 807 void 808 primordial_undef(void *arg) 809 { 810 struct regs *r = (struct regs *)arg; 811 uint32_t *insaddr = (uint32_t *)(r->r_lr - 4); 812 813 bop_printf(NULL, "TRAP: undefined instruction %x at %p\n", 814 *insaddr, insaddr); 815 } 816 817 void 818 primordial_reset(void *arg) 819 { 820 struct regs *r = (struct regs *)arg; 821 uint32_t *insaddr = (uint32_t *)(r->r_lr - 4); 822 823 bop_printf(NULL, "TRAP: reset from %p\n", 824 insaddr); 825 bop_panic("cannot recover from reset\n"); 826 } 827 828 void 829 primordial_prefetchabt(void *arg) 830 { 831 struct regs *r = (struct regs *)arg; 832 uint32_t *insaddr = (uint32_t *)(r->r_lr - 4); 833 uint32_t bkptno = ((*insaddr & 0xfff00) >> 4) | (*insaddr & 0xf); 834 835 bop_printf(NULL, "TRAP: prefetch (or bkpt #%d) at %p\n", 836 bkptno, insaddr); 837 } 838 839 /* 840 * XXX: This can't be tested without the MMU on, I don't think 841 * 842 * So while I'm sure (surely) we can decode this into a useful "what went 843 * wrong and why", I have no idea how, and couldn't test it if I did. 844 * 845 * I will say that I currently have the awful feeling that it would require an 846 * instruction decoder to decode *insaddr and find the memory refs... 847 */ 848 void 849 primordial_dataabt(void *arg) 850 { 851 struct regs *r = (struct regs *)arg; 852 /* XXX: Yes, really +8 see ARM A2.6.6 */ 853 uint32_t *insaddr = (uint32_t *)(r->r_lr - 8); 854 855 bop_printf(NULL, "TRAP: data abort at (insn) %p\n", insaddr); 856 bop_printf(NULL, "r0: %08x\tr1: %08x\n", r->r_r0, r->r_r1); 857 bop_printf(NULL, "r2: %08x\tr3: %08x\n", r->r_r2, r->r_r3); 858 bop_printf(NULL, "r4: %08x\tr5: %08x\n", r->r_r4, r->r_r5); 859 bop_printf(NULL, "r6: %08x\tr7: %08x\n", r->r_r6, r->r_r7); 860 bop_printf(NULL, "r8: %08x\tr9: %08x\n", r->r_r8, r->r_fp); 861 bop_panic("Page Fault! Go fix it\n"); 862 } 863 864 void 865 bad_interrupt(void *arg) 866 { 867 bop_panic("Interrupt with VE == 0 (non-vectored)\n"); 868 } 869 870 /* XXX: This should probably be somewhere else */ 871 void (*trap_table[ARM_EXCPT_NUM])(void *) = { 872 NULL, 873 NULL, 874 NULL, 875 NULL, 876 NULL, 877 NULL, 878 NULL, 879 NULL 880 }; 881 882 /* 883 * Welcome to the kernel. We need to make a fake version of the boot_ops and the 884 * boot_syscalls and then jump our way to _kobj_boot(). Here, we're borrowing 885 * the Linux bootloader expectations, mostly because a lot of bootloaders and 886 * boards already do this. If it turns out that we want to abstract this in the 887 * future, then we should have locore.s do that before we get here. 888 */ 889 void 890 _fakebop_start(void *zeros, uint32_t machid, void *tagstart) 891 { 892 atag_illumos_status_t *aisp; 893 bootinfo_t *bip = &bootinfo; 894 bootops_t *bops = &bootop; 895 extern void _kobj_boot(); 896 897 fakebop_getatags(tagstart); 898 bcons_init(bip->bi_cmdline); 899 900 /* Now that we have a console, we can usefully handle traps */ 901 trap_table[ARM_EXCPT_RESET] = primordial_reset; 902 trap_table[ARM_EXCPT_UNDINS] = primordial_undef; 903 trap_table[ARM_EXCPT_SVC] = primordial_svc; 904 trap_table[ARM_EXCPT_PREFETCH] = primordial_prefetchabt; 905 trap_table[ARM_EXCPT_DATA] = primordial_dataabt; 906 trap_table[ARM_EXCPT_IRQ] = bad_interrupt; 907 trap_table[ARM_EXCPT_FIQ] = bad_interrupt; 908 909 bop_printf(NULL, "Testing some exceptions\n"); 910 __asm__ __volatile__(".word 0xffffffff"); 911 __asm__ __volatile__("svc #14"); 912 /* the high and low bit in each field, so we can check the decode */ 913 __asm__ __volatile__("bkpt #32793"); 914 915 /* Clear some lines from the bootloader */ 916 bop_printf(NULL, "\nWelcome to fakebop -- ARM edition\n"); 917 if (fakebop_atag_debug != 0) 918 fakebop_dump_tags(tagstart); 919 920 aisp = (atag_illumos_status_t *)atag_find(tagstart, 921 ATAG_ILLUMOS_STATUS); 922 if (aisp == NULL) 923 bop_panic("missing ATAG_ILLUMOS_STATUS!\n"); 924 925 /* 926 * Fill in the bootops vector 927 */ 928 bops->bsys_version = BO_VERSION; 929 bops->bsys_alloc = fakebop_alloc; 930 bops->bsys_free = fakebop_free; 931 bops->bsys_getproplen = fakebop_getproplen; 932 bops->bsys_getprop = fakebop_getprop; 933 bops->bsys_printf = bop_printf; 934 935 armboot_mmu_init(tagstart); 936 fakebop_alloc_init(tagstart); 937 fakebop_bootprops_init(); 938 bop_printf(NULL, "booting into _kobj\n"); 939 940 /* 941 * krtld uses _stext, _etext, _sdata, and _edata as part of its segment 942 * brks. ARM is a bit different from other versions of unix because we 943 * have our exception vector in the binary at the top of our address 944 * space. As such, we basically pass in values to krtld that represent 945 * what our _etext and _edata would have looked like if not for the 946 * exception vector. 947 */ 948 _kobj_boot(&bop_sysp, NULL, bops, aisp->ais_stext, aisp->ais_etext, 949 aisp->ais_sdata, aisp->ais_edata, EXCEPTION_ADDRESS); 950 951 bop_panic("Returned from kobj_init\n"); 952 } 953 954 void 955 _fakebop_locore_start(struct boot_syscalls *sysp, struct bootops *bops) 956 { 957 bop_panic("Somehow made it back to fakebop_locore_start..."); 958 }