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