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 }