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 static void
 208 fakebop_dump_tags(void *tagstart)
 209 {
 210         atag_header_t *h = tagstart;
 211         atag_core_t *acp;
 212         atag_mem_t *amp;
 213         atag_cmdline_t *alp;
 214         atag_initrd_t *aip;
 215         const char *tname;
 216         int i;
 217         char *c;
 218 
 219         DBG_MSG("starting point:");
 220         DBG_MSG(fakebop_hack_ultostr((uintptr_t)h, &buffer[BUFFERSIZE-1]));
 221         DBG_MSG("first atag size:");
 222         DBG_MSG(fakebop_hack_ultostr(h->ah_size, &buffer[BUFFERSIZE-1]));
 223         DBG_MSG("first atag tag:");
 224         DBG_MSG(fakebop_hack_ultostr(h->ah_tag, &buffer[BUFFERSIZE-1]));
 225         while (h != NULL) {
 226                 switch (h->ah_tag) {
 227                 case ATAG_CORE:
 228                         tname = "ATAG_CORE";
 229                         break;
 230                 case ATAG_MEM:
 231                         tname = "ATAG_MEM";
 232                         break;
 233                 case ATAG_VIDEOTEXT:
 234                         tname = "ATAG_VIDEOTEXT";
 235                         break;
 236                 case ATAG_RAMDISK:
 237                         tname = "ATAG_RAMDISK";
 238                         break;
 239                 case ATAG_INITRD2:
 240                         tname = "ATAG_INITRD2";
 241                         break;
 242                 case ATAG_SERIAL:
 243                         tname = "ATAG_SERIAL";
 244                         break;
 245                 case ATAG_REVISION:
 246                         tname = "ATAG_REVISION";
 247                         break;
 248                 case ATAG_VIDEOLFB:
 249                         tname = "ATAG_VIDEOLFB";
 250                         break;
 251                 case ATAG_CMDLINE:
 252                         tname = "ATAG_CMDLINE";
 253                         break;
 254                 default:
 255                         tname = fakebop_hack_ultostr(h->ah_tag,
 256                             &buffer[BUFFERSIZE-1]);
 257                         break;
 258                 }
 259                 DBG_MSG("tag:");
 260                 DBG_MSG(tname);
 261                 DBG_MSG("size:");
 262                 DBG_MSG(fakebop_hack_ultostr(h->ah_size,
 263                     &buffer[BUFFERSIZE-1]));
 264                 /* Extended information */
 265                 switch (h->ah_tag) {
 266                 case ATAG_CORE:
 267                         if (h->ah_size == 2) {
 268                                 DBG_MSG("ATAG_CORE has no extra information");
 269                         } else {
 270                                 acp = (atag_core_t *)h;
 271                                 DBG_MSG("\tflags:");
 272                                 bcons_puts("\t");
 273                                 DBG_MSG(fakebop_hack_ultostr(acp->ac_flags,
 274                                     &buffer[BUFFERSIZE-1]));
 275                                 DBG_MSG("\tpage:");
 276                                 bcons_puts("\t");
 277                                 DBG_MSG(fakebop_hack_ultostr(acp->ac_pagesize,
 278                                     &buffer[BUFFERSIZE-1]));
 279                                 DBG_MSG("\troot:");
 280                                 bcons_puts("\t");
 281                                 DBG_MSG(fakebop_hack_ultostr(acp->ac_rootdev,
 282                                     &buffer[BUFFERSIZE-1]));
 283                         }
 284                         break;
 285                 case ATAG_MEM:
 286                         amp = (atag_mem_t *)h;
 287                         DBG_MSG("\tsize:");
 288                         bcons_puts("\t");
 289                         DBG_MSG(fakebop_hack_ultostr(amp->am_size,
 290                             &buffer[BUFFERSIZE-1]));
 291                         DBG_MSG("\tstart:");
 292                         bcons_puts("\t");
 293                         DBG_MSG(fakebop_hack_ultostr(amp->am_start,
 294                             &buffer[BUFFERSIZE-1]));
 295                         break;
 296                 case ATAG_INITRD2:
 297                         aip = (atag_initrd_t *)h;
 298                         DBG_MSG("\tsize:");
 299                         bcons_puts("\t");
 300                         DBG_MSG(fakebop_hack_ultostr(aip->ai_size,
 301                             &buffer[BUFFERSIZE-1]));
 302                         DBG_MSG("\tstart:");
 303                         bcons_puts("\t");
 304                         DBG_MSG(fakebop_hack_ultostr(aip->ai_start,
 305                             &buffer[BUFFERSIZE-1]));
 306                         break;
 307                 case ATAG_CMDLINE:
 308                         alp = (atag_cmdline_t *)h;
 309                         DBG_MSG("\tcmdline:");
 310                         /*
 311                          * We have no intelligent thing to wrap our tty at 80
 312                          * chars so we just do this a bit more manually for now.
 313                          */
 314                         i = 0;
 315                         c = alp->al_cmdline;
 316                         while (*c != '\0') {
 317                                 bcons_putchar(*c++);
 318                                 if (++i == 72) {
 319                                         bcons_puts("\n");
 320                                         i = 0;
 321                                 }
 322                         }
 323                         bcons_puts("\n");
 324                         break;
 325                 default:
 326                         break;
 327                 }
 328                 h = atag_next(h);
 329         }
 330 }
 331 
 332 static void
 333 fakebop_getatags(void *tagstart)
 334 {
 335         atag_mem_t *amp;
 336         atag_cmdline_t *alp;
 337         atag_header_t *ahp = tagstart;
 338         atag_illumos_status_t *aisp;
 339         bootinfo_t *bp = &bootinfo;
 340 
 341         bp->bi_flags = 0;
 342         while (ahp != NULL) {
 343                 switch (ahp->ah_tag) {
 344                 case ATAG_MEM:
 345                         amp = (atag_mem_t *)ahp;
 346                         bp->bi_memsize = amp->am_size;
 347                         bp->bi_memstart = amp->am_start;
 348                         break;
 349                 case ATAG_CMDLINE:
 350                         alp = (atag_cmdline_t *)ahp;
 351                         bp->bi_cmdline = alp->al_cmdline;
 352                         break;
 353                 case ATAG_ILLUMOS_STATUS:
 354                         aisp = (atag_illumos_status_t *)ahp;
 355                         bp->bi_ramdisk = aisp->ais_archive;
 356                         bp->bi_ramsize = aisp->ais_archivelen;
 357                         bp->bi_flags |= BI_HAS_RAMDISK;
 358                         break;
 359                 default:
 360                         break;
 361                 }
 362                 ahp = atag_next(ahp);
 363         }
 364 }
 365 
 366 /*
 367  * We've been asked to allocate at a specific VA. Allocate the next ragne of
 368  * physical addresses and go from there.
 369  */
 370 static caddr_t
 371 fakebop_alloc_hint(caddr_t virt, size_t size, int align)
 372 {
 373         uintptr_t start = P2ROUNDUP(bop_alloc_pnext, align);
 374         if (fakebop_alloc_debug != 0)
 375                 bop_printf(NULL, "asked to allocate %d bytes at v/p %p/%p\n",
 376                     size, virt, start);
 377         if (start + size > bop_alloc_plast)
 378                 bop_panic("fakebop_alloc_hint: No more physical address -_-\n");
 379 
 380         armboot_mmu_map(start, (uintptr_t)virt, size, PF_R | PF_W | PF_X);
 381         bop_alloc_pnext = start + size;
 382         return (virt);
 383 }
 384 
 385 static caddr_t
 386 fakebop_alloc(struct bootops *bops, caddr_t virthint, size_t size, int align)
 387 {
 388         caddr_t start;
 389 
 390         if (virthint != NULL)
 391                 return (fakebop_alloc_hint(virthint, size, align));
 392         if (fakebop_alloc_debug != 0)
 393                 bop_printf(bops, "asked to allocate %d bytes\n", size);
 394         if (bop_alloc_scratch_next == 0)
 395                 bop_panic("fakebop_alloc_init not called");
 396 
 397         if (align == BO_ALIGN_DONTCARE || align == 0)
 398                 align = 4;
 399 
 400         start = (caddr_t)P2ROUNDUP(bop_alloc_scratch_next, align);
 401         if ((uintptr_t)start + size > bop_alloc_scratch_last)
 402                 bop_panic("fakebop_alloc: ran out of scratch space!\n");
 403         if (fakebop_alloc_debug != 0)
 404                 bop_printf(bops, "returning address: %p\n", start);
 405         bop_alloc_scratch_next = (uintptr_t)start + size;
 406 
 407         return (start);
 408 }
 409 
 410 static void
 411 fakebop_free(struct bootops *bops, caddr_t virt, size_t size)
 412 {
 413         bop_panic("Called into fakebop_free");
 414 }
 415 
 416 static int
 417 fakebop_getproplen(struct bootops *bops, const char *pname)
 418 {
 419         bootprop_t *p;
 420 
 421         if (fakebop_prop_debug)
 422                 bop_printf(NULL, "fakebop_getproplen: asked for %s\n", pname);
 423         for (p = bprops; p != NULL; p = p->bp_next) {
 424                 if (strcmp(pname, p->bp_name) == 0)
 425                         return (p->bp_vlen);
 426         }
 427         if (fakebop_prop_debug != 0)
 428                 bop_printf(NULL, "prop %s not found\n", pname);
 429         return (-1);
 430 }
 431 
 432 static int
 433 fakebop_getprop(struct bootops *bops, const char *pname, void *value)
 434 {
 435         bootprop_t *p;
 436 
 437         if (fakebop_prop_debug)
 438                 bop_printf(NULL, "fakebop_getprop: asked for %s\n", pname);
 439         for (p = bprops; p != NULL; p = p->bp_next) {
 440                 if (strcmp(pname, p->bp_name) == 0)
 441                         break;
 442         }
 443         if (p == NULL) {
 444                 if (fakebop_prop_debug)
 445                         bop_printf(NULL, "fakebop_getprop: ENOPROP %s\n",
 446                             pname);
 447                 return (-1);
 448         }
 449         if (fakebop_prop_debug)
 450                 bop_printf(NULL, "fakebop_getprop: copying %d bytes to 0x%x\n",
 451                     p->bp_vlen, value);
 452         bcopy(p->bp_value, value, p->bp_vlen);
 453         return (0);
 454 }
 455 
 456 void
 457 bop_printf(bootops_t *bop, const char *fmt, ...)
 458 {
 459         va_list ap;
 460 
 461         va_start(ap, fmt);
 462         (void) vsnprintf(buffer, BUFFERSIZE, fmt, ap);
 463         va_end(ap);
 464         bcons_puts(buffer);
 465 }
 466 
 467 static void
 468 fakebop_setprop(char *name, int nlen, void *value, int vlen)
 469 {
 470         size_t size;
 471         bootprop_t *bp;
 472         caddr_t cur;
 473 
 474         size = sizeof (bootprop_t) + nlen + 1 + vlen;
 475         cur = fakebop_alloc(NULL, NULL, size, BO_ALIGN_DONTCARE);
 476         bp = (bootprop_t *)cur;
 477         if (bprops == NULL) {
 478                 bprops = bp;
 479                 bp->bp_next = NULL;
 480         } else {
 481                 bp->bp_next = bprops;
 482                 bprops = bp;
 483         }
 484         cur += sizeof (bootprop_t);
 485         bp->bp_name = cur;
 486         bcopy(name, cur, nlen);
 487         cur += nlen;
 488         *cur = '\0';
 489         cur++;
 490         bp->bp_vlen = vlen;
 491         bp->bp_value = cur;
 492         if (vlen > 0)
 493                 bcopy(value, cur, vlen);
 494 
 495         if (fakebop_prop_debug)
 496                 bop_printf(NULL, "setprop - name: %s, nlen: %d, vlen: %d\n",
 497                     bp->bp_name, nlen, bp->bp_vlen);
 498 }
 499 
 500 static void
 501 fakebop_setprop_string(char *name, char *value)
 502 {
 503         if (fakebop_prop_debug)
 504                 bop_printf(NULL, "setprop_string: %s->[%s]\n", name, value);
 505         fakebop_setprop(name, strlen(name), value, strlen(value) + 1);
 506 }
 507 
 508 static void
 509 fakebop_setprop_32(char *name, uint32_t value)
 510 {
 511         if (fakebop_prop_debug)
 512                 bop_printf(NULL, "setprop_32: %s->[%d]\n", name, value);
 513         fakebop_setprop(name, strlen(name), (void *)&value, sizeof (value));
 514 }
 515 
 516 static void
 517 fakebop_setprop_64(char *name, uint64_t value)
 518 {
 519         if (fakebop_prop_debug)
 520                 bop_printf(NULL, "setprop_64: %s->[%lld]\n", name, value);
 521         fakebop_setprop(name, strlen(name), (void *)&value, sizeof (value));
 522 }
 523 
 524 /*
 525  * Here we create a bunch of the initial boot properties. This includes what
 526  * we've been passed in via the command line. It also includes a few values that
 527  * we compute ourselves.
 528  */
 529 static void
 530 fakebop_bootprops_init(void)
 531 {
 532         int i = 0, proplen = 0, cmdline_len = 0, quote, cont;
 533         static int stdout_val = 0;
 534         char *c, *prop, *cmdline, *pname;
 535         bootinfo_t *bp = &bootinfo;
 536 
 537         /*
 538          * Set the ramdisk properties for kobj_boot_mountroot() can succeed.
 539          */
 540         if ((bp->bi_flags & BI_HAS_RAMDISK) != 0) {
 541                 fakebop_setprop_64("ramdisk_start",
 542                     (uint64_t)(uintptr_t)bp->bi_ramdisk);
 543                 fakebop_setprop_64("ramdisk_end",
 544                     (uint64_t)(uintptr_t)bp->bi_ramdisk + bp->bi_ramsize);
 545         }
 546 
 547         /*
 548          * TODO Various arm devices may spit properties at the front just like
 549          * i86xpv. We should do something about them at some point.
 550          */
 551 
 552         /*
 553          * Our boot parameters always wil start with kernel /platform/..., but
 554          * the bootloader may in fact stick other properties in front of us. To
 555          * deal with that we go ahead and we include them. We'll find the kernel
 556          * and our properties set via -B when we finally find something without
 557          * an equals sign, mainly kernel.
 558          */
 559         c = bp->bi_cmdline;
 560         prop = strstr(c, "kernel");
 561         if (prop == NULL)
 562                 bop_panic("failed to find kernel string in boot params!");
 563         /* Get us past the first kernel string */
 564         prop += 6;
 565         while (ISSPACE(prop[0]))
 566                 prop++;
 567         proplen = 0;
 568         while (prop[proplen] != '\0' && !ISSPACE(prop[proplen]))
 569                 proplen++;
 570         c = prop + proplen + 1;
 571         if (proplen > 0) {
 572                 prop[proplen] = '\0';
 573                 fakebop_setprop_string("boot-file", prop);
 574                 /*
 575                  * We strip the leading path from whoami so no matter what
 576                  * environment we enter into here from it is consistent and
 577                  * makes some amount of sense.
 578                  */
 579                 if (strstr(prop, "/platform") != NULL)
 580                         prop = strstr(prop, "/platform");
 581                 fakebop_setprop_string("whoami", prop);
 582         } else {
 583                 bop_panic("no kernel string in boot params!");
 584         }
 585 
 586         /*
 587          * At this point we have two different sets of properties. Anything that
 588          * involves -B is a boot property, otherwise it becomes part of the
 589          * kernel command line and must be saved in its own property.
 590          */
 591         cmdline = fakebop_alloc(NULL, NULL, strlen(c), BO_ALIGN_DONTCARE);
 592         cmdline[0] = '\0';
 593         while (*c != '\0') {
 594 
 595                 /*
 596                  * Just blindly copy it to the commadline if we don't find it.
 597                  */
 598                 if (c[0] != '-' || c[1] != 'B') {
 599                         cmdline[cmdline_len++] = *c;
 600                         cmdline[cmdline_len] = '\0';
 601                         c++;
 602                         continue;
 603                 }
 604 
 605                 /* Get past "-B" */
 606                 c += 2;
 607                 while (ISSPACE(*c))
 608                         c++;
 609 
 610                 /*
 611                  * We have a series of comma separated key-value pairs to sift
 612                  * through here. The key and value are separated by an equals
 613                  * sign. The value may quoted with either a ' or ". Note, white
 614                  * space will also end the value (as it indicates that we have
 615                  * moved on from the -B argument.
 616                  */
 617                 for (;;) {
 618                         if (*c == '\0' || ISSPACE(*c))
 619                                 break;
 620                         prop = strchr(c, '=');
 621                         if (prop == NULL)
 622                                 break;
 623                         pname = c;
 624                         *prop = '\0';
 625                         prop++;
 626                         proplen = 0;
 627                         quote = '\0';
 628                         for (;;) {
 629                                 if (prop[proplen] == '\0')
 630                                         break;
 631 
 632                                 if (proplen == 0 && (prop[0] == '\'' ||
 633                                     prop[0] == '"')) {
 634                                         quote = prop[0];
 635                                         proplen++;
 636                                         continue;
 637                                 }
 638 
 639                                 if (quote != '\0') {
 640                                         if (prop[proplen] == quote)
 641                                                 quote = '\0';
 642                                         proplen++;
 643                                         continue;
 644                                 }
 645 
 646                                 if (prop[proplen] == ',' ||
 647                                     ISSPACE(prop[proplen]))
 648                                         break;
 649 
 650                                 /* We just have a normal character */
 651                                 proplen++;
 652                         }
 653 
 654                         /*
 655                          * Save whether we should continue or not and update 'c'
 656                          * now as we will most likely clobber the string when we
 657                          * are done.
 658                          */
 659                         cont = (prop[proplen] == ',');
 660                         if (prop[proplen] != '\0')
 661                                 c = prop + proplen + 1;
 662                         else
 663                                 c = prop + proplen;
 664 
 665                         if (proplen == 0) {
 666                                 fakebop_setprop_string(pname, "true");
 667                         } else {
 668                                 /*
 669                                  * When we copy the prop, do not include the
 670                                  * quote.
 671                                  */
 672                                 if (prop[0] == prop[proplen - 1] &&
 673                                     (prop[0] == '\'' || prop[0] == '"')) {
 674                                         prop++;
 675                                         proplen -= 2;
 676                                 }
 677                                 prop[proplen] = '\0';
 678                                 fakebop_setprop_string(pname, prop);
 679                         }
 680 
 681                         if (cont == 0)
 682                                 break;
 683                 }
 684         }
 685 
 686         /*
 687          * Yes, we actually set both names here. The latter is set because of
 688          * 1275.
 689          */
 690         fakebop_setprop_string("boot-args", cmdline);
 691         fakebop_setprop_string("bootargs", cmdline);
 692 
 693         /*
 694          * Here are some things that we make up, just like our i86pc brethren.
 695          */
 696         fakebop_setprop_32("stdout", 0);
 697         fakebop_setprop_string("mfg-name", "ARMv6");
 698         fakebop_setprop_string("impl-arch-name", "ARMv6");
 699 }
 700 
 701 /*
 702  * Nominally this should try and look for bootenv.rc, but seriously, let's not.
 703  * Instead for now all we're going to to do is look and make sure that console
 704  * is set. We *should* do something with it, but we're not.
 705  */
 706 void
 707 boot_prop_finish(void)
 708 {
 709         int ret;
 710 
 711         if (fakebop_getproplen(NULL, "console") <= 0)
 712                 bop_panic("console not set");
 713 }
 714 
 715 /*ARGSUSED*/
 716 int
 717 boot_compinfo(int fd, struct compinfo *cbp)
 718 {
 719         cbp->iscmp = 0;
 720         cbp->blksize = MAXBSIZE;
 721         return (0);
 722 }
 723 
 724 extern void (*exception_table[])(void);
 725 
 726 void
 727 primordial_handler(void)
 728 {
 729         bop_panic("TRAP");
 730 }
 731 
 732 void
 733 primordial_svc(void *arg)
 734 {
 735         struct regs *r = (struct regs *)arg;
 736         uint32_t *insaddr = (uint32_t *)(r->r_lr - 4);
 737 
 738         bop_printf(NULL, "TRAP: svc #%d from %p\n", *insaddr & 0x00ffffff,
 739             insaddr);
 740 }
 741 
 742 void
 743 primordial_undef(void *arg)
 744 {
 745         struct regs *r = (struct regs *)arg;
 746         uint32_t *insaddr = (uint32_t *)(r->r_lr - 4);
 747 
 748         bop_printf(NULL, "TRAP: undefined instruction %x at %p\n",
 749             *insaddr, insaddr);
 750 }
 751 
 752 void
 753 primordial_reset(void *arg)
 754 {
 755         struct regs *r = (struct regs *)arg;
 756         uint32_t *insaddr = (uint32_t *)(r->r_lr - 4);
 757 
 758         bop_printf(NULL, "TRAP: reset from %p\n",
 759             insaddr);
 760         bop_panic("cannot recover from reset\n");
 761 }
 762 
 763 void
 764 primordial_prefetchabt(void *arg)
 765 {
 766         struct regs *r = (struct regs *)arg;
 767         uint32_t *insaddr = (uint32_t *)(r->r_lr - 4);
 768         uint32_t bkptno = ((*insaddr & 0xfff00) >> 4) | (*insaddr & 0xf);
 769 
 770         bop_printf(NULL, "TRAP: prefetch (or bkpt #%d) at %p\n",
 771             bkptno, insaddr);
 772 }
 773 
 774 /*
 775  * XXX: This can't be tested without the MMU on, I don't think
 776  *
 777  * So while I'm sure (surely) we can decode this into a useful "what went
 778  * wrong and why", I have no idea how, and couldn't test it if I did.
 779  *
 780  * I will say that I currently have the awful feeling that it would require an
 781  * instruction decoder to decode *insaddr and find the memory refs...
 782  */
 783 void
 784 primordial_dataabt(void *arg)
 785 {
 786         struct regs *r = (struct regs *)arg;
 787         /* XXX: Yes, really +8 see ARM A2.6.6 */
 788         uint32_t *insaddr = (uint32_t *)(r->r_lr - 8);
 789 
 790         bop_printf(NULL, "TRAP: data abort at (insn) %p\n", insaddr);
 791         bop_printf(NULL, "r0: %08x\tr1: %08x\n", r->r_r0, r->r_r1);
 792         bop_printf(NULL, "r2: %08x\tr3: %08x\n", r->r_r2, r->r_r3);
 793         bop_printf(NULL, "r4: %08x\tr5: %08x\n", r->r_r4, r->r_r5);
 794         bop_printf(NULL, "r6: %08x\tr7: %08x\n", r->r_r6, r->r_r7);
 795         bop_printf(NULL, "r8: %08x\tr9: %08x\n", r->r_r8, r->r_fp);
 796         bop_panic("Page Fault! Go fix it\n");
 797 }
 798 
 799 void
 800 bad_interrupt(void *arg)
 801 {
 802         bop_panic("Interrupt with VE == 0 (non-vectored)\n");
 803 }
 804 
 805 /* XXX: This should probably be somewhere else */
 806 void (*trap_table[ARM_EXCPT_NUM])(void *) = {
 807         NULL,
 808         NULL,
 809         NULL,
 810         NULL,
 811         NULL,
 812         NULL,
 813         NULL,
 814         NULL
 815 };
 816 
 817 /*
 818  * Welcome to the kernel. We need to make a fake version of the boot_ops and the
 819  * boot_syscalls and then jump our way to _kobj_boot(). Here, we're borrowing
 820  * the Linux bootloader expectations, mostly because a lot of bootloaders and
 821  * boards already do this. If it turns out that we want to abstract this in the
 822  * future, then we should have locore.s do that before we get here.
 823  */
 824 void
 825 _fakebop_start(void *zeros, uint32_t machid, void *tagstart)
 826 {
 827         atag_illumos_status_t *aisp;
 828         bootinfo_t *bip = &bootinfo;
 829         bootops_t *bops = &bootop;
 830         extern void _kobj_boot();
 831 
 832         fakebop_getatags(tagstart);
 833         bcons_init(bip->bi_cmdline);
 834 
 835         /* Now that we have a console, we can usefully handle traps */
 836         trap_table[ARM_EXCPT_RESET] = primordial_reset;
 837         trap_table[ARM_EXCPT_UNDINS] = primordial_undef;
 838         trap_table[ARM_EXCPT_SVC] = primordial_svc;
 839         trap_table[ARM_EXCPT_PREFETCH] = primordial_prefetchabt;
 840         trap_table[ARM_EXCPT_DATA] = primordial_dataabt;
 841         trap_table[ARM_EXCPT_IRQ] = bad_interrupt;
 842         trap_table[ARM_EXCPT_FIQ] = bad_interrupt;
 843 
 844         bop_printf(NULL, "Testing some exceptions\n");
 845         __asm__ __volatile__(".word 0xffffffff");
 846         __asm__ __volatile__("svc #14");
 847         /* the high and low bit in each field, so we can check the decode */
 848         __asm__ __volatile__("bkpt #32793");
 849 
 850         /* Clear some lines from the bootloader */
 851         bop_printf(NULL, "\nWelcome to fakebop -- ARM edition\n");
 852         if (fakebop_atag_debug != 0)
 853                 fakebop_dump_tags(tagstart);
 854 
 855         aisp = (atag_illumos_status_t *)atag_find(tagstart,
 856             ATAG_ILLUMOS_STATUS);
 857         if (aisp == NULL)
 858                 bop_panic("missing ATAG_ILLUMOS_STATUS!\n");
 859 
 860         /*
 861          * Fill in the bootops vector
 862          */
 863         bops->bsys_version = BO_VERSION;
 864         bops->bsys_alloc = fakebop_alloc;
 865         bops->bsys_free = fakebop_free;
 866         bops->bsys_getproplen = fakebop_getproplen;
 867         bops->bsys_getprop = fakebop_getprop;
 868         bops->bsys_printf = bop_printf;
 869 
 870         armboot_mmu_init(tagstart);
 871         fakebop_alloc_init(tagstart);
 872         fakebop_bootprops_init();
 873         bop_printf(NULL, "booting into _kobj\n");
 874 
 875         /*
 876          * krtld uses _stext, _etext, _sdata, and _edata as part of its segment
 877          * brks. ARM is a bit different from other versions of unix because we
 878          * have our exception vector in the binary at the top of our address
 879          * space. As such, we basically pass in values to krtld that represent
 880          * what our _etext and _edata would have looked like if not for the
 881          * exception vector.
 882          */
 883         _kobj_boot(&bop_sysp, NULL, bops, aisp->ais_stext, aisp->ais_etext,
 884             aisp->ais_sdata, aisp->ais_edata, EXCEPTION_ADDRESS);
 885 
 886         bop_panic("Returned from kobj_init\n");
 887 }
 888 
 889 void
 890 _fakebop_locore_start(struct boot_syscalls *sysp, struct bootops *bops)
 891 {
 892         bop_panic("Somehow made it back to fakebop_locore_start...");
 893 }