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, ®s[i * (acells + scells)],
234 sizeof(int32_t) * acells);
235 else
236 addr = regs[i * (acells + scells)];
237
238 if (scells > 1)
239 memcpy(&size, ®s[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, ®s[i * (acells + scells)],
298 sizeof(int32_t) * acells);
299 else
300 addr = regs[i * (acells + scells)];
301
302 if (scells > 1)
303 memcpy(&size, ®s[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 < ®s[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 > ®s[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