xref: /netbsd-src/sys/arch/virt68k/virt68k/bootinfo.c (revision a933f81a5cc595ed87e48bb55808cf348f714ef1)
1 /*      $NetBSD: bootinfo.c,v 1.8 2024/11/11 13:55:22 riastradh Exp $        */
2 
3 /*-
4  * Copyright (c) 2023 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
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 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: bootinfo.c,v 1.8 2024/11/11 13:55:22 riastradh Exp $");
34 
35 #include "opt_md.h"
36 
37 #include <sys/types.h>
38 #include <sys/cpu.h>
39 #include <sys/rnd.h>
40 #include <sys/rndsource.h>
41 
42 #include <uvm/uvm_extern.h>
43 
44 #ifdef MEMORY_DISK_DYNAMIC
45 #include <dev/md.h>
46 #endif
47 
48 #include <machine/bootinfo.h>
49 #include <machine/vmparam.h>
50 
51 #include "gftty.h"
52 #if NGFTTY > 0
53 #include <dev/goldfish/gfttyvar.h>
54 #endif
55 
56 struct bi_record *	bootinfo;
57 vaddr_t			bootinfo_end;
58 uint32_t		bootinfo_machtype;
59 int			bootinfo_mem_segments_ignored;
60 size_t			bootinfo_mem_segments_ignored_bytes;
61 struct bi_mem_info	bootinfo_mem_segments[VM_PHYSSEG_MAX];
62 struct bi_mem_info	bootinfo_mem_segments_avail[VM_PHYSSEG_MAX];
63 int			bootinfo_mem_nsegments;
64 int			bootinfo_mem_nsegments_avail;
65 
66 static paddr_t		bootinfo_console_addr;
67 static bool		bootinfo_console_addr_valid;
68 
69 static uint32_t		bootinfo_initrd_start;
70 static uint32_t		bootinfo_initrd_size;
71 
72 #if NGFTTY > 0
73 static bool
74 bootinfo_set_console(paddr_t pa)
75 {
76 	if (! bootinfo_console_addr_valid) {
77 		bootinfo_console_addr = pa;
78 		bootinfo_console_addr_valid = true;
79 		return true;
80 	}
81 	return false;
82 }
83 #endif
84 
85 static inline struct bi_record *
86 bootinfo_next(struct bi_record *bi)
87 {
88 	uintptr_t addr = (uintptr_t)bi;
89 
90 	addr += bi->bi_size;
91 	return (struct bi_record *)addr;
92 }
93 
94 static inline int
95 bootinfo_get_cpu(struct bi_record *bi)
96 {
97 	switch (bootinfo_get_u32(bi)) {
98 	case BI_CPU_68020:	return CPU_68020;
99 	case BI_CPU_68030:	return CPU_68030;
100 	case BI_CPU_68040:	return CPU_68040;
101 	case BI_CPU_68060:	return CPU_68060;
102 	default:		return -666;
103 	}
104 }
105 
106 static inline int
107 bootinfo_get_fpu(struct bi_record *bi)
108 {
109 	switch (bootinfo_get_u32(bi)) {
110 	case BI_FPU_68881:	return FPU_68881;
111 	case BI_FPU_68882:	return FPU_68882;
112 	case BI_FPU_68040:	return FPU_68040;
113 	case BI_FPU_68060:	return FPU_68060;
114 	default:		return FPU_UNKNOWN;
115 	}
116 }
117 
118 static inline int
119 bootinfo_get_mmu(struct bi_record *bi)
120 {
121 	switch (bootinfo_get_u32(bi)) {
122 	case BI_MMU_68851:	return MMU_68851;
123 	case BI_MMU_68030:	return MMU_68030;
124 	case BI_MMU_68040:	return MMU_68040;
125 	case BI_MMU_68060:	return MMU_68040;	/* XXX */
126 	case BI_MMU_SUN3:	return MMU_SUN;
127 	case BI_MMU_APOLLO:	/* XXX MMU_HP ??? */
128 	case BI_MMU_COLDFIRE:
129 	default:		return FPU_UNKNOWN;
130 	}
131 }
132 
133 static inline void
134 bootinfo_add_mem(struct bi_record *bi)
135 {
136 	struct bi_mem_info *m = bootinfo_dataptr(bi);
137 
138 	if (bootinfo_mem_nsegments == VM_PHYSSEG_MAX) {
139 		bootinfo_mem_segments_ignored++;
140 		bootinfo_mem_segments_ignored_bytes += m->mem_size;
141 	}
142 
143 	/*
144 	 * Make sure the start / size are properly aligned.
145 	 */
146 	if (m->mem_addr & PGOFSET) {
147 		m->mem_size -= m->mem_addr & PGOFSET;
148 		m->mem_addr = m68k_round_page(m->mem_addr);
149 	}
150 	m->mem_size = m68k_trunc_page(m->mem_size);
151 	physmem += m->mem_size >> PGSHIFT;
152 
153 	bootinfo_mem_segments[bootinfo_mem_nsegments++] = *m;
154 	bootinfo_mem_segments_avail[bootinfo_mem_nsegments_avail++] = *m;
155 }
156 
157 static inline void
158 bootinfo_add_initrd(struct bi_record *bi)
159 {
160 	struct bi_mem_info *rd = bootinfo_dataptr(bi);
161 
162 	if (bootinfo_initrd_size == 0) {
163 		bootinfo_initrd_start = rd->mem_addr;
164 		bootinfo_initrd_size  = rd->mem_size;
165 	}
166 }
167 
168 static inline void
169 bootinfo_reserve_initrd(void)
170 {
171 	if (bootinfo_initrd_size == 0) {
172 		return;
173 	}
174 
175 	paddr_t initrd_start = bootinfo_initrd_start;
176 	paddr_t initrd_end   = bootinfo_initrd_start + bootinfo_initrd_size;
177 	int i;
178 
179 	/* Page-align the RAM disk start/end. */
180 	initrd_end = m68k_round_page(initrd_end);
181 	initrd_start = m68k_trunc_page(initrd_start);
182 
183 	/*
184 	 * XXX All if this code assumes that the RAM disk fits within
185 	 * XXX a single memory segment.
186 	 */
187 
188 	for (i = 0; i < bootinfo_mem_nsegments_avail; i++) {
189 		/* Memory segment start/end already page-aligned. */
190 		paddr_t seg_start = bootinfo_mem_segments_avail[i].mem_addr;
191 		paddr_t seg_end = seg_start +
192 		    bootinfo_mem_segments_avail[i].mem_size;
193 
194 		if (initrd_start >= seg_end ||
195 		    initrd_end <= seg_start) {
196 			/* Does not fall within this segment. */
197 			continue;
198 		}
199 
200 		if (initrd_start > seg_start && initrd_end < seg_end) {
201 			/* We need to split this segment. */
202 			/* XXX */
203 			printf("WARNING: ignoring RAM disk that splits "
204 			       "memory segment.\n");
205 			bootinfo_initrd_size = 0;
206 			return;
207 		}
208 
209 		printf("Reserving RAM disk pages %p - %p from memory "
210 		       "segment %d.\n", (void *)initrd_start,
211 		       (void *)(initrd_end - 1), i);
212 
213 		if (initrd_start == seg_start) {
214 			seg_start = initrd_end;
215 		}
216 
217 		if (initrd_end == seg_end) {
218 			seg_end = initrd_start;
219 		}
220 
221 		/* Now adjust the segment. */
222 		bootinfo_mem_segments_avail[i].mem_addr = seg_start;
223 		bootinfo_mem_segments_avail[i].mem_size = seg_end - seg_start;
224 		return;
225 	}
226 }
227 
228 static inline void
229 bootinfo_gf_tty_consinit(struct bi_record *bi)
230 {
231 #if NGFTTY > 0
232 	struct bi_virt_dev *vd = bootinfo_dataptr(bi);
233 
234 	/*
235 	 * vd_mmio_base is the PA, but we're going to run mapped
236 	 * VA==PA for devices anyway once the MMU is turned on.
237 	 */
238 	if (bootinfo_set_console(vd->vd_mmio_base)) {
239 		bootinfo_md_cnattach(gftty_cnattach,
240 		    vd->vd_mmio_base, 0x1000);
241 		printf("Initialized Goldfish TTY console @ 0x%08x\n",
242 		    vd->vd_mmio_base);
243 	}
244 #endif /* NGFTTY > 0 */
245 }
246 
247 /*
248  * bootinfo_start --
249  *	Parse the boot info during early start-up.
250  */
251 void
252 bootinfo_start(struct bi_record *first)
253 {
254 	struct bi_record *bi;
255 
256 	bootinfo = first;
257 
258 	for (bi = bootinfo; bi->bi_tag != BI_LAST; bi = bootinfo_next(bi)) {
259 		switch (bi->bi_tag) {
260 		case BI_MACHTYPE:
261 			bootinfo_machtype = bootinfo_get_u32(bi);
262 			break;
263 
264 		case BI_CPUTYPE:
265 			cputype = bootinfo_get_cpu(bi);
266 			break;
267 
268 		case BI_FPUTYPE:
269 			fputype = bootinfo_get_fpu(bi);
270 			break;
271 
272 		case BI_MMUTYPE:
273 			mmutype = bootinfo_get_mmu(bi);
274 			break;
275 
276 		case BI_MEMCHUNK:
277 			bootinfo_add_mem(bi);
278 			break;
279 
280 		case BI_RAMDISK:
281 			bootinfo_add_initrd(bi);
282 			break;
283 
284 		case BI_VIRT_GF_TTY_BASE:
285 			bootinfo_gf_tty_consinit(bi);
286 			break;
287 
288 		default:
289 			break;
290 		}
291 	}
292 
293 	/* Set bootinfo_end to be just past the BI_LAST record. */
294 	bootinfo_end = (vaddr_t)bootinfo_next(bi);
295 
296 	/*
297 	 * If we have a RAM disk, we need to take it out of the
298 	 * available memory segments.
299 	 */
300 	bootinfo_reserve_initrd();
301 }
302 
303 /*
304  * bootinfo_enumerate --
305  *	Enumerate through the boot info, invoking the specified callback
306  *	for each record.  The callback returns true to keep searching,
307  *	false, to stop.
308  */
309 void
310 bootinfo_enumerate(bool (*cb)(struct bi_record *, void *), void *ctx)
311 {
312 	struct bi_record *bi = bootinfo;
313 
314 	if (bi == NULL) {
315 		return;
316 	}
317 
318 	for (; bi->bi_tag != BI_LAST; bi = bootinfo_next(bi)) {
319 		if ((*cb)(bi, ctx) == false) {
320 			break;
321 		}
322 	}
323 }
324 
325 struct bootinfo_find_ctx {
326 	uint32_t tag;
327 	struct bi_record *result;
328 };
329 
330 static bool
331 bootinfo_find_cb(struct bi_record *bi, void *v)
332 {
333 	struct bootinfo_find_ctx *ctx = v;
334 
335 	if (bi->bi_tag == ctx->tag) {
336 		ctx->result = bi;
337 		return false;
338 	}
339 
340 	return true;
341 }
342 
343 /*
344  * bootinfo_find --
345  *	Scan through the boot info looking for the first instance of
346  *	the specified tag.
347  */
348 struct bi_record *
349 bootinfo_find(uint32_t tag)
350 {
351 	struct bootinfo_find_ctx ctx = {
352 		.tag = tag,
353 	};
354 
355 	bootinfo_enumerate(bootinfo_find_cb, &ctx);
356 	return ctx.result;
357 }
358 
359 /*
360  * bootinfo_addr_is_console --
361  *	Tests to see if the device at the specified address is
362  *	the console device.
363  */
364 bool
365 bootinfo_addr_is_console(paddr_t pa)
366 {
367 	return bootinfo_console_addr_valid && bootinfo_console_addr == pa;
368 }
369 
370 /*
371  * bootinfo_setup_initrd --
372  *	Check for a BI_RAMDISK record and, if found, set it as
373  *	the root file system.
374  */
375 void
376 bootinfo_setup_initrd(void)
377 {
378 #ifdef MEMORY_DISK_DYNAMIC
379 	if (bootinfo_initrd_size != 0) {
380 		paddr_t rdstart, rdend, rdpgoff;
381 		vaddr_t rdva, rdoff;
382 		vsize_t rdvsize;
383 
384 		printf("Initializing root RAM disk @ %p - %p\n",
385 		    (void *)bootinfo_initrd_start,
386 		    (void *)(bootinfo_initrd_start + bootinfo_initrd_size - 1));
387 
388 		rdend = m68k_round_page(bootinfo_initrd_start +
389 		    bootinfo_initrd_size);
390 		rdstart = m68k_trunc_page(bootinfo_initrd_start);
391 		rdvsize = rdend - rdstart;
392 		rdpgoff = bootinfo_initrd_start & PAGE_MASK;
393 
394 		rdva = uvm_km_alloc(kernel_map, rdvsize, PAGE_SIZE,
395 		    UVM_KMF_VAONLY);
396 		if (rdva == 0) {
397 			printf("WARNING: Unable to allocate KVA for "
398 			       "RAM disk.\n");
399 			return;
400 		}
401 		for (rdoff = 0; rdoff < rdvsize; rdoff += PAGE_SIZE) {
402 			pmap_kenter_pa(rdva + rdoff, rdstart + rdoff,
403 			    VM_PROT_READ | VM_PROT_WRITE, 0);
404 		}
405 		md_root_setconf((void *)(rdva + rdpgoff),
406 		    bootinfo_initrd_size);
407 	}
408 #endif /* MEMORY_DISK_DYNAMIC */
409 }
410 
411 /*
412  * bootinfo_setup_rndseed --
413  *	Check for a BI_RNG_SEED record and, if found, use it to
414  *	seed the kenrnel entropy pool.
415  */
416 void
417 bootinfo_setup_rndseed(void)
418 {
419 	static struct krndsource bootinfo_rndsource;
420 	struct bi_record *bi = bootinfo_find(BI_RNG_SEED);
421 	if (bi != NULL) {
422 		struct bi_data *rnd = bootinfo_dataptr(bi);
423 		rnd_attach_source(&bootinfo_rndsource, "bootinfo",
424 		    RND_TYPE_RNG, RND_FLAG_DEFAULT);
425 		rnd_add_data(&bootinfo_rndsource,
426 		    rnd->data_bytes, rnd->data_length,
427 		    rnd->data_length * NBBY);
428 		explicit_memset(rnd->data_bytes, 0, rnd->data_length);
429 	}
430 }
431 
432 /*
433  * bootinfo_getarg --
434  *	Get an argument from the BI_COMMAND_LINE bootinfo record.
435  */
436 bool
437 bootinfo_getarg(const char *var, char *buf, size_t buflen)
438 {
439 	const size_t varlen = strlen(var);
440 	struct bi_record *bi = bootinfo_find(BI_COMMAND_LINE);
441 
442 	if (bi == NULL) {
443 		return false;
444 	}
445 
446 	const char *sp = bootinfo_dataptr(bi);
447 	const char *osp = sp;
448 	for (;;) {
449 		sp = strstr(sp, var);
450 		if (sp == NULL) {
451 			return false;
452 		}
453 
454 		if (sp != osp &&
455 		    sp[-1] != ' ' && sp[-1] != '\t' && sp[-1] != '-') {
456 			continue;
457 		}
458 		sp += varlen;
459 		char ch = *sp++;
460 		if (ch != '=' && ch != ' ' && ch != '\t' && ch != '\0') {
461 			continue;
462 		}
463 		/* Found it. */
464 		break;
465 	}
466 
467 	while (--buflen) {
468 		if (*sp == ' ' || *sp == '\t' || *sp == '\0') {
469 			break;
470 		}
471 		*buf++ = *sp++;
472 	}
473 	*buf = '\0';
474 
475 	return true;
476 }
477