1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/param.h>
  27 #include <sys/promif.h>
  28 #include <sys/salib.h>
  29 /* EXPORT DELETE START */
  30 #include <bootlog.h>
  31 /* EXPORT DELETE END */
  32 #include "ramdisk.h"
  33 
  34 #include <sys/param.h>
  35 #include <sys/fcntl.h>
  36 #include <sys/obpdefs.h>
  37 #include <sys/reboot.h>
  38 #include <sys/promif.h>
  39 #include <sys/stat.h>
  40 #include <sys/bootvfs.h>
  41 #include <sys/platnames.h>
  42 #include <sys/salib.h>
  43 #include <sys/elf.h>
  44 #include <sys/link.h>
  45 #include <sys/auxv.h>
  46 #include <sys/boot_policy.h>
  47 #include <sys/boot_redirect.h>
  48 #include <sys/bootconf.h>
  49 #include <sys/boot.h>
  50 #include "boot_plat.h"
  51 
  52 
  53 static char ramdisk_preamble_fth[] =
  54 
  55 ": find-abort ( name$ -- ) "
  56 "   .\" Can't find \" type abort "
  57 "; "
  58 
  59 ": get-package ( pkg$ -- ph ) "
  60 "   2dup  find-package 0=  if "
  61 "      find-abort "
  62 "   then                       ( pkg$ ph ) "
  63 "   nip nip                    ( ph ) "
  64 "; "
  65 
  66 "\" /openprom/client-services\" get-package  constant cif-ph "
  67 
  68 "instance defer cif-open     ( dev$ -- ihandle|0 ) "
  69 "instance defer cif-close    ( ihandle -- ) "
  70 
  71 ": find-cif-method ( adr,len -- acf ) "
  72 "   2dup  cif-ph find-method 0=  if    ( adr,len ) "
  73 "      find-abort "
  74 "   then                               ( adr,len acf ) "
  75 "   nip nip                            ( acf ) "
  76 "; "
  77 
  78 "\" open\"     find-cif-method to cif-open "
  79 "\" close\"    find-cif-method to cif-close "
  80 
  81 "0 value dev-ih "
  82 
  83 "d# 100 buffer: open-cstr "
  84 
  85 ": dev-open ( dev$ -- okay? ) "
  86 /* copy to C string for open  */
  87 "   0  over open-cstr +  c! "
  88 "   open-cstr swap  move "
  89 "   open-cstr  cif-open dup  if "
  90 "      dup to dev-ih "
  91 "   then "
  92 "; "
  93 
  94 ": dev-close ( -- ) "
  95 "   dev-ih cif-close "
  96 "   0 to dev-ih "
  97 "; "
  98 
  99 ": open-abort  ( file$ -- ) "
 100 "   .\" Can't open \"  type  abort "
 101 "; "
 102 ;
 103 
 104 static char ramdisk_fth[] =
 105 
 106 "\" /\" get-package  push-package "
 107 
 108 "new-device "
 109 "   \" %s\" device-name "
 110 "    "
 111 "   \" block\"          device-type "
 112 "   \" SUNW,ramdisk\"   encode-string \" compatible\"  property"
 113 
 114 "   0 instance value current-offset "
 115 "    "
 116 "   0 value ramdisk-base-va "
 117 "   0 value ramdisk-size "
 118 "   0 value alloc-size "
 119 "    "
 120 "   : set-props "
 121 "      ramdisk-size     encode-int  \" size\"        property "
 122 "      ramdisk-base-va  encode-int  \" address\"     property "
 123 "      alloc-size       encode-int  \" alloc-size\"  property "
 124 "   ; "
 125 "   set-props "
 126 "    "
 127 "   : current-va  ( -- adr )  ramdisk-base-va current-offset +  ; "
 128 "    "
 129 "   external "
 130 "    "
 131 "   : open  ( -- okay? ) "
 132 /* " .\" ramdisk-open\" cr " */
 133 "      true "
 134 "   ; "
 135 "    "
 136 "   : close  ( -- ) "
 137 "   ; "
 138 "    "
 139 "   : seek  ( off.low off.high -- error? ) "
 140 /* " 2dup .\" ramdisk-seek: \" .x .x " */
 141 "      drop  dup  ramdisk-size  >  if "
 142 /* " .\" fail\" cr " */
 143 "         drop true  exit         ( failed ) "
 144 "      then "
 145 "      to current-offset  false   ( succeeded ) "
 146 /* " .\" OK\" cr " */
 147 "   ; "
 148 "    "
 149 "   : read  ( addr len -- actual-len ) "
 150 /* " 2dup .\" ramdisk-read: \" .x .x " */
 151 "      dup  current-offset  +            ( addr len new-off ) "
 152 "      dup  ramdisk-size  >  if "
 153 "         ramdisk-size -  -              ( addr len' ) "
 154 "         ramdisk-size                   ( addr len new-off ) "
 155 "      then  -rot                        ( new-off addr len ) "
 156 "      tuck  current-va  -rot  move      ( new-off len ) "
 157 "      swap  to current-offset           ( len ) "
 158 /* " dup .x cr " */
 159 "   ; "
 160 "    "
 161 "   : create ( alloc-sz base size -- ) "
 162 "      to ramdisk-size "
 163 "      to ramdisk-base-va "
 164 "      to alloc-size "
 165 "      set-props "
 166 "   ; "
 167 "    "
 168 "finish-device "
 169 "pop-package "
 170 
 171 "\" /%s\" 2dup  dev-open  0=  if "
 172 "   open-abort "
 173 "then 2drop "
 174 
 175 /* %x %x %x will be replaced by alloc-sz, base, size respectively */
 176 "h# %x h# %x h# %x ( alloc-sz base size ) "
 177 "\" create\" dev-ih  $call-method  (  ) "
 178 "dev-close "
 179 
 180 ;
 181 
 182 char ramdisk_bootable[] =
 183 
 184 "\" /chosen\" get-package  push-package "
 185 "   \" nfs\"             encode-string  \" fstype\"  property "
 186 "   \" /%s\"             encode-string  \" bootarchive\"  property "
 187 "pop-package "
 188 
 189 "   h# %x d# 512 +  to load-base init-program "
 190 ;
 191 
 192 #define BOOT_ARCHIVE_ALLOC_SIZE (32 * 1024 * 1024)      /* 32 MB */
 193 #define BOOTFS_VIRT             ((caddr_t)0x50f00000)
 194 #define ROOTFS_VIRT             ((caddr_t)0x52000000)
 195 
 196 struct ramdisk_attr {
 197         char *rd_name;
 198         caddr_t rd_base;
 199         size_t rd_size;
 200 } ramdisk_attr[] = {
 201         RD_BOOTFS,      BOOTFS_VIRT,    0,
 202         RD_ROOTFS,      ROOTFS_VIRT,    0,
 203         0
 204 };
 205 
 206 static struct ramdisk_attr *
 207 ramdisk_lookup(char *ramdisk_name)
 208 {
 209         int i;
 210 
 211         for (i = 0; ramdisk_attr[i].rd_name != 0; i++) {
 212                 if (strcmp(ramdisk_name, ramdisk_attr[i].rd_name) == 0) {
 213                         return (&ramdisk_attr[i]);
 214                 }
 215         }
 216         return (NULL);
 217 }
 218 
 219 static void
 220 ramdisk_free_mem(caddr_t addr, size_t size)
 221 {
 222         caddr_t end_addr;
 223 
 224         for (end_addr = addr + size; addr < end_addr;
 225             addr += BOOT_ARCHIVE_ALLOC_SIZE) {
 226                 prom_free(addr, MIN(BOOT_ARCHIVE_ALLOC_SIZE, end_addr - addr));
 227         }
 228 }
 229 
 230 /*
 231  * Allocate memory for ramdisk image.
 232  */
 233 static caddr_t
 234 ramdisk_alloc_mem(caddr_t addr, size_t size)
 235 {
 236         caddr_t virt = addr;
 237         caddr_t end_addr;
 238 
 239         for (end_addr = virt + size; virt < end_addr;
 240             virt += BOOT_ARCHIVE_ALLOC_SIZE) {
 241                 if (prom_alloc(virt,
 242                     MIN(BOOT_ARCHIVE_ALLOC_SIZE, end_addr - virt),
 243                     1) == NULL) {
 244                         ramdisk_free_mem(addr, virt - addr);
 245                         return (NULL);
 246                 }
 247         }
 248         return (addr);
 249 }
 250 
 251 caddr_t
 252 create_ramdisk(char *ramdisk_name, size_t size, char **devpath)
 253 {
 254         char    *fth_buf;
 255         size_t  buf_size;
 256         struct ramdisk_attr *rdp;
 257         char tdevpath[80];
 258         caddr_t virt;
 259         static int need_preamble = 1;
 260 
 261         /*
 262          * lookup ramdisk name.
 263          */
 264         if ((rdp = ramdisk_lookup(ramdisk_name)) == NULL)
 265                 prom_panic("invalid ramdisk name");
 266 
 267         virt = rdp->rd_base;
 268 
 269         /*
 270          * Allocate memory.
 271          */
 272         size = roundup(size, PAGESIZE);
 273         if (ramdisk_alloc_mem(virt, size) == NULL)
 274                 prom_panic("can't alloc ramdisk memory");
 275 
 276         rdp->rd_size = size;
 277 
 278         if (need_preamble) {
 279                 prom_interpret(ramdisk_preamble_fth, 0, 0, 0, 0, 0);
 280                 need_preamble = 0;
 281         }
 282 
 283         /*
 284          * add some space to the size to accommodate a few words in the
 285          * snprintf() below.
 286          */
 287         buf_size = sizeof (ramdisk_fth) + 80;
 288 
 289         fth_buf = bkmem_alloc(buf_size);
 290         if (fth_buf == NULL)
 291                 prom_panic("unable to allocate Forth buffer for ramdisk");
 292 
 293         (void) snprintf(fth_buf, buf_size, ramdisk_fth,
 294             ramdisk_name, ramdisk_name,
 295             BOOT_ARCHIVE_ALLOC_SIZE, virt, size);
 296 
 297         prom_interpret(fth_buf, 0, 0, 0, 0, 0);
 298         bkmem_free(fth_buf, buf_size);
 299 
 300         if (devpath != NULL) {
 301                 (void) snprintf(tdevpath, sizeof (tdevpath), "/%s:nolabel",
 302                     ramdisk_name);
 303                 *devpath = strdup(tdevpath);
 304         }
 305 
 306         return (virt);
 307 }
 308 
 309 void
 310 destroy_ramdisk(char *ramdisk_name)
 311 {
 312         struct ramdisk_attr *rdp;
 313 
 314         /*
 315          * lookup ramdisk name.
 316          */
 317         if ((rdp = ramdisk_lookup(ramdisk_name)) == NULL)
 318                 prom_panic("invalid ramdisk name");
 319 
 320         ramdisk_free_mem(rdp->rd_base, rdp->rd_size);
 321         rdp->rd_size = 0;
 322 }
 323 
 324 /*
 325  * change cwp! to drop in the 2nd word of (init-program) - really
 326  * init-c-stack, but that word has no header.
 327  * (you are not expected to undertsnad this)
 328  */
 329 char obpfix[] = "' drop ' cwp!  ' (init-program) >body ta1+ token@ (patch";
 330 char obpver[OBP_MAXPROPNAME];
 331 const char badver[] = "OBP 4.27.";
 332 
 333 
 334 void
 335 boot_ramdisk(char *ramdisk_name)
 336 {
 337         char    *fth_buf;
 338         size_t  buf_size;
 339         struct ramdisk_attr *rdp;
 340         void do_sg_go(void);
 341 
 342         /*
 343          * OBP revs 4.27.0 to 4.27.8 started using
 344          * windowed regs for the forth kernel, but
 345          * init-program still blindly 0'd %cwp, which
 346          * causes predictably disaterous consequences
 347          * when called with %cwp != 0.
 348          *
 349          * We detect and fix this here
 350          */
 351         if (prom_version_name(obpver, OBP_MAXPROPNAME) != -1 &&
 352             strncmp(obpver, badver, sizeof (badver) - 1) == 0) {
 353                 char ch = obpver[sizeof (badver) - 1];
 354 
 355                 if (ch >= '0' && ch <= '8') {
 356                         prom_interpret(obpfix, 0, 0, 0, 0, 0);
 357                 }
 358         }
 359 
 360         /* close all open devices */
 361         closeall(1);
 362 
 363         /*
 364          * lookup ramdisk name.
 365          */
 366         if ((rdp = ramdisk_lookup(ramdisk_name)) == NULL)
 367                 prom_panic("invalid ramdisk name");
 368 
 369         /*
 370          * add some space to the size to accommodate a few words in the
 371          * snprintf() below.
 372          */
 373         buf_size = sizeof (ramdisk_bootable) + 80;
 374 
 375         fth_buf = bkmem_alloc(buf_size);
 376         if (fth_buf == NULL)
 377                 prom_panic("unable to allocate Forth buffer for ramdisk");
 378 
 379         (void) snprintf(fth_buf, buf_size, ramdisk_bootable,
 380             ramdisk_name, rdp->rd_base);
 381 
 382         prom_interpret(fth_buf, 0, 0, 0, 0, 0);
 383 
 384         /*
 385          * Ugh  Serengeti proms don't execute C programs
 386          * in init-program, and 'go' doesn't work when
 387          * launching a second C program (inetboot itself
 388          * was launched as the 1st C program).  Nested fcode
 389          * programs work, but that doesn't help the kernel.
 390          */
 391         do_sg_go();
 392 }
 393 
 394 void
 395 do_sg_go()
 396 {
 397         pnode_t chosen = prom_chosennode();
 398         Elf64_Ehdr *ehdr;
 399         Elf64_Addr entry;
 400         uint32_t eadr;
 401         extern int is_sg;
 402         extern caddr_t sg_addr;
 403         extern size_t sg_len;
 404 
 405         if (!is_sg)
 406                 prom_panic("do_sg_go");
 407 
 408         /*
 409          * The ramdisk bootblk left a pointer to the elf image
 410          * in 'elfheader-address'  Use it to find the kernel's
 411          * entry point.
 412          */
 413         if (prom_getprop(chosen, "elfheader-address", (caddr_t)&eadr) == -1)
 414                 prom_panic("no elf header property");
 415         ehdr = (Elf64_Ehdr *)(uintptr_t)eadr;
 416         if (ehdr->e_machine != EM_SPARCV9)
 417                 prom_panic("bad ELF header");
 418         entry = ehdr->e_entry;
 419 
 420         /*
 421          * free extra bootmem
 422          */
 423         prom_free(sg_addr, sg_len);
 424 
 425         /*
 426          * Use pre-newboot's exitto64() to launch the kernel
 427          */
 428         exitto64((int (*)())entry, NULL);
 429         prom_panic("exitto returned");
 430 }