xref: /netbsd-src/sys/arch/powerpc/powerpc/ofw_machdep.c (revision 1c03490869c40af8030414bc1cfb2d447b637739)
1 /*	$NetBSD: ofw_machdep.c,v 1.36 2022/12/12 13:26:46 martin Exp $	*/
2 
3 /*-
4  * Copyright (c) 2007, 2021 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Tim Rightnour
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (C) 1996 Wolfgang Solfrank.
34  * Copyright (C) 1996 TooLs GmbH.
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  * 3. All advertising materials mentioning features or use of this software
46  *    must display the following acknowledgement:
47  *	This product includes software developed by TooLs GmbH.
48  * 4. The name of TooLs GmbH may not be used to endorse or promote products
49  *    derived from this software without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
52  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
53  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
54  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
56  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
57  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
58  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
59  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
60  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61  */
62 
63 #include <sys/cdefs.h>
64 __KERNEL_RCSID(0, "$NetBSD: ofw_machdep.c,v 1.36 2022/12/12 13:26:46 martin Exp $");
65 
66 #include <sys/param.h>
67 #include <sys/buf.h>
68 #include <sys/conf.h>
69 #include <sys/device.h>
70 #include <sys/disk.h>
71 #include <sys/disklabel.h>
72 #include <sys/fcntl.h>
73 #include <sys/ioctl.h>
74 #include <sys/stat.h>
75 #include <sys/systm.h>
76 
77 #include <dev/cons.h>
78 #include <dev/ofw/openfirm.h>
79 
80 #include <machine/powerpc.h>
81 #include <machine/autoconf.h>
82 
83 #include <powerpc/ofw_machdep.h>
84 
85 #ifdef DEBUG
86 #define DPRINTF ofprint
87 #else
88 #define DPRINTF while(0) printf
89 #endif
90 
91 #define	ofpanic(FORMAT, ...)	do {				\
92 		ofprint(FORMAT __VA_OPT__(,) __VA_ARGS__);	\
93 		panic(FORMAT __VA_OPT__(,) __VA_ARGS__);	\
94 	} while (0)
95 
96 int	ofw_root;
97 int	ofw_chosen;
98 
99 bool	ofw_real_mode;
100 
101 /*
102  * Bootstrap console support functions.
103  */
104 
105 int	console_node = -1, console_instance = -1;
106 int	ofw_stdin, ofw_stdout;
107 bool	ofwbootcons_suppress;
108 
109 int	ofw_address_cells;
110 int	ofw_size_cells;
111 
ofprint(const char * blah,...)112 void ofprint(const char *blah, ...)
113 {
114 	va_list va;
115 	char buf[256];
116 	int len;
117 
118 	va_start(va, blah);
119 	len = vsnprintf(buf, sizeof(buf), blah, va);
120 	va_end(va);
121 	OF_write(console_instance, buf, len);
122 	/* Apple OF only does a newline on \n, so add an explicit CR */
123 	if ((len > 0) && (buf[len - 1] == '\n'))
124 		OF_write(console_instance, "\r", 1);
125 }
126 
127 static int
ofwbootcons_cngetc(dev_t dev)128 ofwbootcons_cngetc(dev_t dev)
129 {
130 	unsigned char ch = '\0';
131 	int l;
132 
133 	if (ofwbootcons_suppress) {
134 		return ch;
135 	}
136 
137 	while ((l = OF_read(ofw_stdin, &ch, 1)) != 1) {
138 		if (l != -2 && l != 0) {
139 			return -1;
140 		}
141 	}
142 	return ch;
143 }
144 
145 static void
ofwbootcons_cnputc(dev_t dev,int c)146 ofwbootcons_cnputc(dev_t dev, int c)
147 {
148 	char ch = c;
149 
150 	if (ofwbootcons_suppress) {
151 		return;
152 	}
153 
154 	OF_write(ofw_stdout, &ch, 1);
155 }
156 
157 static struct consdev consdev_ofwbootcons = {
158 	.cn_getc = ofwbootcons_cngetc,
159 	.cn_putc = ofwbootcons_cnputc,
160 	.cn_pollc = nullcnpollc,
161 	.cn_dev = NODEV,
162 	.cn_pri = CN_INTERNAL,
163 };
164 
165 static void
ofw_bootstrap_console(void)166 ofw_bootstrap_console(void)
167 {
168 	int node;
169 
170 	if (ofw_chosen == -1) {
171 		goto nocons;
172 	}
173 
174 	if (OF_getprop(ofw_chosen, "stdout", &ofw_stdout,
175 		       sizeof(ofw_stdout)) != sizeof(ofw_stdout))
176 		goto nocons;
177 
178 	if (OF_getprop(ofw_chosen, "stdin", &ofw_stdin,
179 		       sizeof(ofw_stdin)) != sizeof(ofw_stdin))
180 		goto nocons;
181 	if (ofw_stdout == 0) {
182 		/* screen should be console, but it is not open */
183 		ofw_stdout = OF_open("screen");
184 	}
185 	node = OF_instance_to_package(ofw_stdout);
186 	console_node = node;
187 	console_instance = ofw_stdout;
188 
189 	cn_tab = &consdev_ofwbootcons;
190 
191 	return;
192  nocons:
193 	ofpanic("No /chosen could be found!\n");
194 	console_node = -1;
195 }
196 
197 #define	OFMEM_REGIONS	32
198 static struct mem_region OFmem[OFMEM_REGIONS + 1], OFavail[OFMEM_REGIONS + 3];
199 
200 static void
ofw_bootstrap_get_memory(void)201 ofw_bootstrap_get_memory(void)
202 {
203 	const char *macrisc[] = {"MacRISC", "MacRISC2", "MacRISC4", NULL};
204 	int hmem, i, cnt, memcnt, regcnt;
205 	int numregs;
206 	uint32_t regs[OFMEM_REGIONS * 4]; /* 2 values + 2 for 64bit */
207 
208 	int acells = ofw_address_cells;
209 	int scells = ofw_size_cells;
210 
211 	DPRINTF("calling mem_regions\n");
212 
213 	/* Get memory */
214 	memset(regs, 0, sizeof(regs));
215 	if ((hmem = OF_finddevice("/memory")) == -1)
216 		goto error;
217 	regcnt = OF_getprop(hmem, "reg", regs,
218 	    sizeof(regs[0]) * OFMEM_REGIONS * 4);
219 	if (regcnt <= 0)
220 		goto error;
221 
222 	/* how many mem regions did we get? */
223 	numregs = regcnt / (sizeof(uint32_t) * (acells + scells));
224 	DPRINTF("regcnt=%d num=%d acell=%d scell=%d\n",
225 	    regcnt, numregs, acells, scells);
226 
227 	/* move the data into OFmem */
228 	memset(OFmem, 0, sizeof(OFmem));
229 	for (i = 0, memcnt = 0; i < numregs; i++) {
230 		uint64_t addr, size;
231 
232 		if (acells > 1)
233 			memcpy(&addr, &regs[i * (acells + scells)],
234 			    sizeof(int32_t) * acells);
235 		else
236 			addr = regs[i * (acells + scells)];
237 
238 		if (scells > 1)
239 			memcpy(&size, &regs[i * (acells + scells) + acells],
240 			    sizeof(int32_t) * scells);
241 		else
242 			size = regs[i * (acells + scells) + acells];
243 
244 		/* skip entry of 0 size */
245 		if (size == 0)
246 			continue;
247 #ifndef _LP64
248 		if (addr > 0xFFFFFFFF || size > 0xFFFFFFFF ||
249 			(addr + size) > 0xFFFFFFFF) {
250 			ofprint("Base addr of %llx or size of %llx too"
251 			    " large for 32 bit OS. Skipping.", addr, size);
252 			continue;
253 		}
254 #endif
255 		OFmem[memcnt].start = addr;
256 		OFmem[memcnt].size = size;
257 		DPRINTF("mem region %d start=%"PRIx64" size=%"PRIx64"\n",
258 		    memcnt, addr, size);
259 		memcnt++;
260 	}
261 
262 	DPRINTF("available\n");
263 
264 	/* now do the same thing again, for the available counts */
265 	memset(regs, 0, sizeof(regs));
266 	regcnt = OF_getprop(hmem, "available", regs,
267 	    sizeof(regs[0]) * OFMEM_REGIONS * 4);
268 	if (regcnt <= 0)
269 		goto error;
270 
271 	DPRINTF("%08x %08x %08x %08x\n", regs[0], regs[1], regs[2], regs[3]);
272 
273 	/*
274 	 * according to comments in FreeBSD all Apple OF has 32bit values in
275 	 * "available", no matter what the cell sizes are
276 	 */
277 	if (of_compatible(ofw_root, macrisc)) {
278 		DPRINTF("this appears to be a mac...\n");
279 		acells = 1;
280 		scells = 1;
281 	}
282 
283 	/* how many mem regions did we get? */
284 	numregs = regcnt / (sizeof(uint32_t) * (acells + scells));
285 	DPRINTF("regcnt=%d num=%d acell=%d scell=%d\n",
286 	    regcnt, numregs, acells, scells);
287 
288 	DPRINTF("to OF_avail\n");
289 
290 	/* move the data into OFavail */
291 	memset(OFavail, 0, sizeof(OFavail));
292 	for (i = 0, cnt = 0; i < numregs; i++) {
293 		uint64_t addr, size;
294 
295 		DPRINTF("%d\n", i);
296 		if (acells > 1)
297 			memcpy(&addr, &regs[i * (acells + scells)],
298 			    sizeof(int32_t) * acells);
299 		else
300 			addr = regs[i * (acells + scells)];
301 
302 		if (scells > 1)
303 			memcpy(&size, &regs[i * (acells + scells) + acells],
304 			    sizeof(int32_t) * scells);
305 		else
306 			size = regs[i * (acells + scells) + acells];
307 		/* skip entry of 0 size */
308 		if (size == 0)
309 			continue;
310 #ifndef _LP64
311 		if (addr > 0xFFFFFFFF || size > 0xFFFFFFFF ||
312 			(addr + size) > 0xFFFFFFFF) {
313 			ofprint("Base addr of %llx or size of %llx too"
314 			    " large for 32 bit OS. Skipping.", addr, size);
315 			continue;
316 		}
317 #endif
318 		OFavail[cnt].start = addr;
319 		OFavail[cnt].size = size;
320 		DPRINTF("avail region %d start=%#"PRIx64" size=%#"PRIx64"\n",
321 		    cnt, addr, size);
322 		cnt++;
323 	}
324 
325 	if (strncmp(model_name, "Pegasos", 7) == 0) {
326 		/*
327 		 * Some versions of SmartFirmware, only recognize the first
328 		 * 256MB segment as available. Work around it and add an
329 		 * extra entry to OFavail[] to account for this.
330 		 */
331 #define AVAIL_THRESH (0x10000000-1)
332 		if (((OFavail[cnt-1].start + OFavail[cnt-1].size +
333 		    AVAIL_THRESH) & ~AVAIL_THRESH) <
334 		    (OFmem[memcnt-1].start + OFmem[memcnt-1].size)) {
335 
336 			OFavail[cnt].start =
337 			    (OFavail[cnt-1].start + OFavail[cnt-1].size +
338 			    AVAIL_THRESH) & ~AVAIL_THRESH;
339 			OFavail[cnt].size =
340 			    OFmem[memcnt-1].size - OFavail[cnt].start;
341 			ofprint("WARNING: add memory segment %lx - %" PRIxPADDR ","
342 			    "\nWARNING: which was not recognized by "
343 			    "the Firmware.\n",
344 			    (unsigned long)OFavail[cnt].start,
345 			    (unsigned long)OFavail[cnt].start +
346 			    OFavail[cnt].size);
347 			cnt++;
348 		}
349 	}
350 
351 	return;
352 
353 error:
354 #if defined (MAMBO)
355 	ofprint("no memory, assuming 512MB\n");
356 
357 	OFmem[0].start = 0x0;
358 	OFmem[0].size = 0x20000000;
359 
360 	OFavail[0].start = 0x3000;
361 	OFavail[0].size = 0x20000000 - 0x3000;
362 
363 #else
364 	ofpanic("no memory?");
365 #endif
366 	return;
367 }
368 
369 static void
ofw_bootstrap_get_translations(void)370 ofw_bootstrap_get_translations(void)
371 {
372 	/* 5 cells per: virt(1), size(1), phys(2), mode(1) */
373 	uint32_t regs[OFW_MAX_TRANSLATIONS * 5];
374 	uint32_t virt, size, mode;
375 	uint64_t phys;
376 	uint32_t *rp;
377 	int proplen;
378 	int mmu_ihandle, mmu_phandle;
379 	int idx;
380 
381 	if (OF_getprop(ofw_chosen, "mmu", &mmu_ihandle,
382 		       sizeof(mmu_ihandle)) <= 0) {
383 		ofprint("No /chosen/mmu\n");
384 		return;
385 	}
386 	mmu_phandle = OF_instance_to_package(mmu_ihandle);
387 
388 	proplen = OF_getproplen(mmu_phandle, "translations");
389 	if (proplen <= 0) {
390 		ofprint("No translations in /chosen/mmu\n");
391 		return;
392 	}
393 
394 	if (proplen > sizeof(regs)) {
395 		ofpanic("/chosen/mmu translations too large");
396 	}
397 
398 	proplen = OF_getprop(mmu_phandle, "translations", regs, sizeof(regs));
399 	int nregs = proplen / sizeof(regs[0]);
400 
401 	/* Decode into ofw_translations[]. */
402 	for (idx = 0, rp = regs; rp < &regs[nregs];) {
403 		virt = *rp++;
404 		size = *rp++;
405 		switch (ofw_address_cells) {
406 		case 1:
407 			phys = *rp++;
408 			break;
409 		case 2:
410 			phys = *rp++;
411 			phys = (phys << 32) | *rp++;
412 			break;
413 		default:
414 			ofpanic("unexpected #address-cells");
415 		}
416 		mode = *rp++;
417 		if (rp > &regs[nregs]) {
418 			ofpanic("unexpected OFW translations format");
419 		}
420 
421 		/* Wouldn't expect this, but... */
422 		if (size == 0) {
423 			continue;
424 		}
425 
426 		DPRINTF("translation %d virt=%#"PRIx32
427 		    " phys=%#"PRIx64" size=%#"PRIx32" mode=%#"PRIx32"\n",
428 		    idx, virt, phys, size, mode);
429 
430 		if (sizeof(paddr_t) < 8 && phys >= 0x100000000ULL) {
431 			ofpanic("translation phys out of range");
432 		}
433 
434 		if (idx == OFW_MAX_TRANSLATIONS) {
435 			ofpanic("too many OFW translations");
436 		}
437 
438 		ofw_translations[idx].virt = virt;
439 		ofw_translations[idx].size = size;
440 		ofw_translations[idx].phys = (paddr_t)phys;
441 		ofw_translations[idx].mode = mode;
442 		idx++;
443 	}
444 }
445 
446 static bool
ofw_option_truefalse(const char * prop,int proplen)447 ofw_option_truefalse(const char *prop, int proplen)
448 {
449 	/* These are all supposed to be strings. */
450 	switch (prop[0]) {
451 	case 'y':
452 	case 'Y':
453 	case 't':
454 	case 'T':
455 	case '1':
456 		return true;
457 	}
458 	return false;
459 }
460 
461 /*
462  * Called from ofwinit() very early in bootstrap.  We are still
463  * running on the stack provided by OpenFirmware and in the same
464  * OpenFirmware client environment as the boot loader.  Our calls
465  * to OpenFirmware are direct, and not via the trampoline that
466  * saves / restores kernel state.
467  */
468 void
ofw_bootstrap(void)469 ofw_bootstrap(void)
470 {
471 	char prop[32];
472 	int handle, proplen;
473 
474 	/* Stash the handles for "/" and "/chosen" for convenience later. */
475 	ofw_root = OF_finddevice("/");
476 	ofw_chosen = OF_finddevice("/chosen");
477 
478 	/* Initialize the early bootstrap console. */
479 	ofw_bootstrap_console();
480 
481 	/* Check to see if we're running in real-mode. */
482 	handle = OF_finddevice("/options");
483 	if (handle != -1) {
484 		proplen = OF_getprop(handle, "real-mode?", prop, sizeof(prop));
485 		if (proplen > 0) {
486 			ofw_real_mode = ofw_option_truefalse(prop, proplen);
487 		} else {
488 			ofw_real_mode = false;
489 		}
490 	}
491 	DPRINTF("OpenFirmware running in %s-mode\n",
492 	    ofw_real_mode ? "real" : "virtual");
493 
494 	/* Get #address-cells and #size-cells to fetching memory info. */
495 	if (OF_getprop(ofw_root, "#address-cells", &ofw_address_cells,
496 		       sizeof(ofw_address_cells)) <= 0)
497 		ofw_address_cells = 1;
498 
499 	if (OF_getprop(ofw_root, "#size-cells", &ofw_size_cells,
500 		       sizeof(ofw_size_cells)) <= 0)
501 		ofw_size_cells = 1;
502 
503 	/* Get the system memory configuration. */
504 	ofw_bootstrap_get_memory();
505 
506 	/* Get any translations used by OpenFirmware. */
507 	ofw_bootstrap_get_translations();
508 }
509 
510 /*
511  * This is called during initppc, before the system is really initialized.
512  * It shall provide the total and the available regions of RAM.
513  * Both lists must have a zero-size entry as terminator.
514  * The available regions need not take the kernel into account, but needs
515  * to provide space for two additional entry beyond the terminating one.
516  */
517 void
mem_regions(struct mem_region ** memp,struct mem_region ** availp)518 mem_regions(struct mem_region **memp, struct mem_region **availp)
519 {
520 	*memp = OFmem;
521 	*availp = OFavail;
522 }
523 
524 void
ppc_exit(void)525 ppc_exit(void)
526 {
527 	OF_exit();
528 }
529 
530 void
ppc_boot(char * str)531 ppc_boot(char *str)
532 {
533 	OF_boot(str);
534 }
535