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