#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: linux_bootinfo.c,v 1.9 2026/07/12 22:21:59 thorpej Exp $");
#include "opt_ddb.h"
#include "opt_modular.h"
#include "opt_md.h"
#include "ksyms.h"
#include <sys/types.h>
#include <sys/cpu.h>
#include <sys/rnd.h>
#include <sys/rndsource.h>
#include <uvm/uvm_extern.h>
#ifdef MEMORY_DISK_DYNAMIC
#include <dev/md.h>
#endif
#if NKSYMS || defined(DDB) || defined(MODULAR)
#define PRESERVE_KERNEL_SYMBOLS
#endif
#include <m68k/linux_bootinfo.h>
static struct bootinfo_data bootinfo_data_store;
#define RELOC(v, t) *((t *)PMAP_BOOTSTRAP_RELOC_GLOB(&(v)))
static inline struct bi_record * __attribute__((always_inline))
bootinfo_next(struct bi_record *bi)
{
uintptr_t addr = (uintptr_t)bi;
addr += bi->bi_size;
return (struct bi_record *)addr;
}
#ifndef __mc68010__
static inline int __attribute__((always_inline))
bootinfo_get_cpu(struct bi_record *bi)
{
switch (bootinfo_get_u32(bi)) {
case BI_CPU_68020: return CPU_68020;
case BI_CPU_68030: return CPU_68030;
case BI_CPU_68040: return CPU_68040;
case BI_CPU_68060: return CPU_68060;
default: return -666;
}
}
static inline int __attribute__((always_inline))
bootinfo_get_fpu(struct bi_record *bi)
{
switch (bootinfo_get_u32(bi)) {
case BI_FPU_68881: return FPU_68881;
case BI_FPU_68882: return FPU_68882;
case BI_FPU_68040: return FPU_68040;
case BI_FPU_68060: return FPU_68060;
default: return FPU_UNKNOWN;
}
}
static inline int __attribute__((always_inline))
bootinfo_get_mmu(struct bi_record *bi)
{
switch (bootinfo_get_u32(bi)) {
case BI_MMU_68851: return MMU_68851;
case BI_MMU_68030: return MMU_68030;
case BI_MMU_68040: return MMU_68040;
case BI_MMU_68060: return MMU_68040;
case BI_MMU_SUN3: return MMU_SUN;
case BI_MMU_APOLLO:
case BI_MMU_COLDFIRE:
default: return MMU_UNKNOWN;
}
}
#endif
static inline void __attribute__((always_inline))
bootinfo_add_mem(struct bootinfo_data *bid, struct bi_record *bi)
{
struct bi_mem_info *m = bootinfo_dataptr(bi);
if (bid->bootinfo_mem_nsegments == VM_PHYSSEG_MAX) {
bid->bootinfo_mem_segments_ignored++;
bid->bootinfo_mem_segments_ignored_bytes += m->mem_size;
}
if (m->mem_addr & PAGE_MASK) {
m->mem_size -= m->mem_addr & PAGE_MASK;
m->mem_addr = m68k_round_page(m->mem_addr);
}
m->mem_size = m68k_trunc_page(m->mem_size);
bid->bootinfo_total_mem_pages += m68k_btop(m->mem_size);
int i = bid->bootinfo_mem_nsegments++;
bid->bootinfo_mem_segments[i].ps_start =
bid->bootinfo_mem_segments[i].ps_avail_start = m->mem_addr;
bid->bootinfo_mem_segments[i].ps_end =
bid->bootinfo_mem_segments[i].ps_avail_end =
m->mem_addr + m->mem_size;
}
static inline void __attribute__((always_inline))
bootinfo_add_initrd(struct bootinfo_data *bid, struct bi_record *bi)
{
struct bi_mem_info *rd = bootinfo_dataptr(bi);
if (bid->bootinfo_initrd_size == 0) {
bid->bootinfo_initrd_start = rd->mem_addr;
bid->bootinfo_initrd_size = rd->mem_size;
}
}
#ifdef PRESERVE_KERNEL_SYMBOLS
static inline paddr_t __attribute__((always_inline))
bootinfo_preserve_ksyms(struct bootinfo_data *bid, vaddr_t addr, size_t size,
paddr_t nextpa, vaddr_t reloff)
{
bid->bootinfo_ksym_start = PMAP_BOOTSTRAP_PA_TO_VA(addr);
bid->bootinfo_ksym_size = size;
if (addr + size > nextpa) {
nextpa = addr + size;
}
return nextpa;
}
#endif
static void
bootinfo_reserve_initrd(struct bootinfo_data *bid)
{
if (bid->bootinfo_initrd_size == 0) {
return;
}
paddr_t initrd_start = bid->bootinfo_initrd_start;
paddr_t initrd_end = bid->bootinfo_initrd_start +
bid->bootinfo_initrd_size;
int i;
initrd_end = m68k_round_page(initrd_end);
initrd_start = m68k_trunc_page(initrd_start);
for (i = 0; i < bid->bootinfo_mem_nsegments; i++) {
paddr_t seg_start =
bid->bootinfo_mem_segments[i].ps_avail_start;
paddr_t seg_end =
bid->bootinfo_mem_segments[i].ps_avail_end;
if (initrd_start >= seg_end ||
initrd_end <= seg_start) {
continue;
}
if (initrd_start > seg_start && initrd_end < seg_end) {
printf("WARNING: ignoring RAM disk that splits "
"memory segment.\n");
bid->bootinfo_initrd_size = 0;
return;
}
printf("Reserving RAM disk pages %p - %p from memory "
"segment %d.\n", (void *)initrd_start,
(void *)(initrd_end - 1), i);
if (initrd_start == seg_start) {
seg_start = initrd_end;
}
if (initrd_end == seg_end) {
seg_end = initrd_start;
}
bid->bootinfo_mem_segments[i].ps_avail_start = seg_start;
bid->bootinfo_mem_segments[i].ps_avail_end = seg_end;
return;
}
}
paddr_t
bootinfo_startup1(paddr_t nextpa, vaddr_t reloff)
{
const paddr_t bootinfo_pa = nextpa;
struct bootinfo_data *bid = (struct bootinfo_data *)
PMAP_BOOTSTRAP_RELOC_GLOB(&bootinfo_data_store);
struct bi_record *bi;
bid->bootinfo = (struct bi_record *)
PMAP_BOOTSTRAP_RELOC_PA(bootinfo_pa);
bid->bootinfo_mem_segments = (phys_seg_list_t *)
PMAP_BOOTSTRAP_RELOC_GLOB(phys_seg_list);
#ifdef __mc68010__
RELOC(fputype, int) = FPU_NONE;
RELOC(cputype, int) = CPU_68010;
#else
RELOC(mmutype, int) = MMU_UNKNOWN;
#endif
for (bi = bid->bootinfo; bi->bi_tag != BI_LAST;
bi = bootinfo_next(bi)) {
switch (bi->bi_tag) {
case BI_MACHTYPE:
bid->bootinfo_machtype = bootinfo_get_u32(bi);
break;
#ifndef __mc68010__
case BI_CPUTYPE:
RELOC(cputype, int) = bootinfo_get_cpu(bi);
break;
case BI_FPUTYPE:
RELOC(fputype, int) = bootinfo_get_fpu(bi);
break;
case BI_MMUTYPE:
RELOC(mmutype, int) = bootinfo_get_mmu(bi);
break;
#endif
case BI_MEMCHUNK:
bootinfo_add_mem(bid, bi);
break;
case BI_RAMDISK:
bootinfo_add_initrd(bid, bi);
break;
default:
#ifdef PRESERVE_KERNEL_SYMBOLS
if (bid->bootinfo_machtype == BI_MACH_FDT &&
bi->bi_tag == BI_FDT_ELF_SYMS) {
struct bi_mem_info *m = bootinfo_dataptr(bi);
nextpa = bootinfo_preserve_ksyms(bid,
m->mem_addr, m->mem_size, nextpa, reloff);
}
#endif
break;
}
}
#ifndef __mc68010__
if (RELOC(mmutype, int) == MMU_UNKNOWN) {
switch (RELOC(cputype, int)) {
case CPU_68020:
switch (bid->bootinfo_machtype) {
case BI_MACH_SUN3:
RELOC(mmutype, int) = MMU_SUN;
break;
default:
break;
}
break;
case CPU_68030:
RELOC(mmutype, int) = MMU_68030;
break;
case CPU_68040:
case CPU_68060:
RELOC(mmutype, int) = MMU_68040;
break;
default:
break;
}
}
#endif
paddr_t bi_end = (paddr_t)bootinfo_next(bi);
bid->bootinfo_end = PMAP_BOOTSTRAP_PA_TO_VA(bi_end);
if (bi_end > nextpa) {
nextpa = bi_end;
}
RELOC(physmem, psize_t) = bid->bootinfo_total_mem_pages;
bid->bootinfo = (struct bi_record *)
PMAP_BOOTSTRAP_PA_TO_VA(bootinfo_pa);
bid->bootinfo_mem_segments = phys_seg_list;
return nextpa;
}
void
bootinfo_startup2(paddr_t nextpa)
{
struct bootinfo_data *bid = &bootinfo_data_store;
bootinfo_reserve_initrd(bid);
}
struct bootinfo_data *
bootinfo_data(void)
{
return &bootinfo_data_store;
}
void
bootinfo_enumerate(bool (*cb)(struct bi_record *, void *), void *ctx)
{
struct bi_record *bi = bootinfo_data_store.bootinfo;
if (bi == NULL) {
return;
}
for (; bi->bi_tag != BI_LAST; bi = bootinfo_next(bi)) {
if ((*cb)(bi, ctx) == false) {
break;
}
}
}
struct bootinfo_find_ctx {
uint32_t tag;
struct bi_record *result;
};
static bool
bootinfo_find_cb(struct bi_record *bi, void *v)
{
struct bootinfo_find_ctx *ctx = v;
if (bi->bi_tag == ctx->tag) {
ctx->result = bi;
return false;
}
return true;
}
struct bi_record *
bootinfo_find(uint32_t tag)
{
struct bootinfo_find_ctx ctx = {
.tag = tag,
};
bootinfo_enumerate(bootinfo_find_cb, &ctx);
return ctx.result;
}
struct bi_record *
bootinfo_find_machdep(uint32_t mtype, uint32_t tag)
{
return bootinfo_data_store.bootinfo_machtype == mtype ?
bootinfo_find(tag) : NULL;
}
void
bootinfo_setup_initrd(void)
{
#ifdef MEMORY_DISK_DYNAMIC
struct bootinfo_data *bid = &bootinfo_data_store;
if (bid->bootinfo_initrd_size != 0) {
paddr_t rdstart, rdend, rdpgoff;
vaddr_t rdva, rdoff;
vsize_t rdvsize;
printf("Initializing root RAM disk @ %p - %p\n",
(void *)bid->bootinfo_initrd_start,
(void *)(bid->bootinfo_initrd_start +
bid->bootinfo_initrd_size - 1));
rdend = m68k_round_page(bid->bootinfo_initrd_start +
bid->bootinfo_initrd_size);
rdstart = m68k_trunc_page(bid->bootinfo_initrd_start);
rdvsize = rdend - rdstart;
rdpgoff = bid->bootinfo_initrd_start & PAGE_MASK;
rdva = uvm_km_alloc(kernel_map, rdvsize, PAGE_SIZE,
UVM_KMF_VAONLY);
if (rdva == 0) {
printf("WARNING: Unable to allocate KVA for "
"RAM disk.\n");
return;
}
for (rdoff = 0; rdoff < rdvsize; rdoff += PAGE_SIZE) {
pmap_kenter_pa(rdva + rdoff, rdstart + rdoff,
VM_PROT_READ | VM_PROT_WRITE, 0);
}
md_root_setconf((void *)(rdva + rdpgoff),
bid->bootinfo_initrd_size);
}
#endif
}
void
bootinfo_setup_rndseed(void)
{
static struct krndsource bootinfo_rndsource;
struct bi_record *bi = bootinfo_find(BI_RNG_SEED);
if (bi != NULL) {
struct bi_data *rnd = bootinfo_dataptr(bi);
rnd_attach_source(&bootinfo_rndsource, "bootinfo",
RND_TYPE_RNG, RND_FLAG_DEFAULT);
rnd_add_data(&bootinfo_rndsource,
rnd->data_bytes, rnd->data_length,
rnd->data_length * NBBY);
explicit_memset(rnd->data_bytes, 0, rnd->data_length);
}
}
bool
bootinfo_getarg(const char *var, char *buf, size_t buflen)
{
const size_t varlen = strlen(var);
struct bi_record *bi = bootinfo_find(BI_COMMAND_LINE);
if (bi == NULL) {
return false;
}
const char *sp = bootinfo_dataptr(bi);
const char *osp = sp;
for (;;) {
sp = strstr(sp, var);
if (sp == NULL) {
return false;
}
if (sp != osp &&
sp[-1] != ' ' && sp[-1] != '\t' && sp[-1] != '-') {
continue;
}
sp += varlen;
char ch = *sp++;
if (ch != '=' && ch != ' ' && ch != '\t' && ch != '\0') {
continue;
}
break;
}
while (--buflen) {
if (*sp == ' ' || *sp == '\t' || *sp == '\0') {
break;
}
*buf++ = *sp++;
}
*buf = '\0';
return true;
}