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