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
53static 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
104static 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
182char 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
196struct 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
206static struct ramdisk_attr *
207ramdisk_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
219static void
220ramdisk_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 */
233static caddr_t
234ramdisk_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
251caddr_t
252create_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
309void
310destroy_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 */
329char obpfix[] = "' drop ' cwp!  ' (init-program) >body ta1+ token@ (patch";
330char obpver[OBP_MAXPROPNAME];
331const char badver[] = "OBP 4.27.";
332
333
334void
335boot_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
394void
395do_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}
431