Home | History | Annotate | Line # | Download | only in virt68k
      1 /*      $NetBSD: bootinfo.c,v 1.9 2025/06/09 21:19:49 andvar 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.9 2025/06/09 21:19:49 andvar 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 kernel 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