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