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