#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.252 2026/07/09 07:47:40 jdc Exp $");
#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_modular.h"
#include "opt_multiprocessor.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/disklabel.h>
#include <sys/device.h>
#include <sys/disk.h>
#include <sys/conf.h>
#include <sys/reboot.h>
#include <sys/socket.h>
#include <sys/vnode.h>
#include <sys/fcntl.h>
#include <sys/queue.h>
#include <sys/msgbuf.h>
#include <sys/boot_flag.h>
#include <sys/ksyms.h>
#include <sys/kauth.h>
#include <sys/userconf.h>
#include <prop/proplib.h>
#include <net/if.h>
#include <net/if_ether.h>
#include <net/ether_calls.h>
#include <dev/cons.h>
#include <sparc64/dev/cons.h>
#include <uvm/uvm_extern.h>
#include <sys/bus.h>
#include <machine/autoconf.h>
#include <machine/openfirm.h>
#include <machine/sparc64.h>
#include <machine/cpu.h>
#include <machine/pmap.h>
#include <machine/bootinfo.h>
#include <sparc64/sparc64/cache.h>
#include <sparc64/sparc64/ofw_patch.h>
#include <sparc64/sparc64/timerreg.h>
#include <sparc64/dev/cbusvar.h>
#include <dev/ata/atavar.h>
#include <dev/pci/pcivar.h>
#include <dev/ebus/ebusvar.h>
#include <dev/sbus/sbusvar.h>
#include <dev/i2c/i2cvar.h>
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#endif
#ifdef RASTERCONSOLE
#error options RASTERCONSOLE is obsolete for sparc64 - remove it from your config file
#endif
#include <dev/wsfb/genfbvar.h>
#include "ksyms.h"
int autoconf_debug = 0x0;
struct evcnt intr_evcnts[] = {
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "spur"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev1"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev2"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev3"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev4"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev5"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev6"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev7"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev8"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev9"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "clock"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev11"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev12"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev13"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "prof"),
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intr", "lev15")
};
void *bootinfo = 0;
#ifdef KGDB
int kgdb_break_at_attach;
#endif
#define OFPATHLEN 128
char machine_banner[100];
char machine_model[100];
char ofbootpath[OFPATHLEN], *ofboottarget, *ofbootpartition;
char ofbootargs[OFPATHLEN], *ofbootfile, *ofbootflags;
int ofbootpackage;
static int mbprint(void *, const char *);
int mainbus_match(device_t, cfdata_t, void *);
static void mainbus_attach(device_t, device_t, void *);
static void get_ncpus(void);
static void get_bootpath_from_prom(void);
struct tlb_entry *kernel_tlbs;
int kernel_dtlb_slots;
int kernel_itlb_slots;
struct intrmap intrmap[] = {
{ "block", PIL_FD },
{ "serial", PIL_SER },
{ "scsi", PIL_BIO },
{ "scsi-2", PIL_BIO },
{ "network", PIL_NET },
{ "display", PIL_VIDEO },
{ "audio", PIL_AUD },
{ "ide", PIL_BIO },
{ "socal", PIL_BIO },
{ "SUNW,CS4231", PIL_AUD },
{ "SUNW,bpp", PIL_BIO },
{ NULL, 0 }
};
#ifdef SUN4V
void sun4v_soft_state_init(void);
void sun4v_set_soft_state(int, const char *);
#define __align32 __attribute__((__aligned__(32)))
char sun4v_soft_state_booting[] __align32 = "NetBSD booting";
char sun4v_soft_state_running[] __align32 = "NetBSD running";
void sun4v_interrupt_init(void);
#if 0
XXX notyet
void sun4v_sdio_init(void);
#endif
#endif
int console_node, console_instance;
struct genfb_colormap_callback gfb_cb;
static void of_set_palette(void *, int, int, int, int);
static void copyprops(device_t, int, prop_dictionary_t, int);
static void
get_ncpus(void)
{
#ifdef MULTIPROCESSOR
int node, l;
char sbuf[32];
node = findroot();
sparc_ncpus = 0;
for (node = OF_child(node); node; node = OF_peer(node)) {
if (OF_getprop(node, "device_type", sbuf, sizeof(sbuf)) <= 0)
continue;
if (strcmp(sbuf, "cpu") != 0)
continue;
sparc_ncpus++;
l = prom_getpropint(node, "dcache-line-size", 0);
if (l > dcache_line_size)
dcache_line_size = l;
l = prom_getpropint(node, "icache-line-size", 0);
if (l > icache_line_size)
icache_line_size = l;
}
#else
icache_line_size = dcache_line_size = 8;
#endif
}
void *
lookup_bootinfo(int type)
{
struct btinfo_common *bt;
char *help = bootinfo;
if (help == NULL)
return (NULL);
do {
bt = (struct btinfo_common *)help;
if (bt->type == type)
return ((void *)help);
help += bt->next;
} while (bt->next != 0 &&
(size_t)help < (size_t)bootinfo + BOOTINFO_SIZE);
return (NULL);
}
void
bootstrap(void *o0, void *bootargs, void *bootsize, void *o3, void *ofw)
{
void *bi;
long bmagic;
char buf[32];
#if NKSYMS || defined(DDB) || defined(MODULAR)
struct btinfo_symtab *bi_sym;
#endif
struct btinfo_count *bi_count;
struct btinfo_kernend *bi_kend;
struct btinfo_tlb *bi_tlb;
struct btinfo_boothowto *bi_howto;
extern void *romtba;
extern void* get_romtba(void);
extern void OF_val2sym32(void *);
extern void OF_sym2val32(void *);
extern struct consdev consdev_prom;
romp = ofw;
romtba = get_romtba();
prom_init();
console_instance = promops.po_stdout;
console_node = OF_instance_to_package(promops.po_stdout);
cn_tab = &consdev_prom;
(*cn_tab->cn_init)(cn_tab);
DPRINTF(ACDB_BOOTARGS,
("sparc64_init(%p, %p, %p, %p, %p)\n", o0, bootargs, bootsize,
o3, ofw));
if ((long)bootsize >= (4 * sizeof(uint64_t))) {
bi = (void*)(u_long)(((uint64_t*)bootargs)[3]);
bmagic = (long)(((uint64_t*)bootargs)[0]);
} else if ((long)bootsize >= (4 * sizeof(uint32_t))) {
bi = (void*)(u_long)(((uint32_t*)bootargs)[3]);
bmagic = (long)(((uint32_t*)bootargs)[0]);
} else {
printf("Bad bootinfo size.\n");
die_old_boot_loader:
printf("This kernel requires NetBSD boot loader version 1.9 "
"or newer\n");
panic("sparc64_init.");
}
DPRINTF(ACDB_BOOTARGS,
("sparc64_init: bmagic=%lx, bi=%p\n", bmagic, bi));
if (SPARC_MACHINE_OPENFIRMWARE != bmagic) {
printf("No bootinfo information.\n");
goto die_old_boot_loader;
}
bootinfo = (void*)(u_long)((uint64_t*)bi)[1];
LOOKUP_BOOTINFO(bi_kend, BTINFO_KERNEND);
if (bi_kend->addr == (vaddr_t)0) {
panic("Kernel end address is not found in bootinfo.\n");
}
#if NKSYMS || defined(DDB) || defined(MODULAR)
LOOKUP_BOOTINFO(bi_sym, BTINFO_SYMTAB);
ksyms_addsyms_elf(bi_sym->nsym, (int *)(u_long)bi_sym->ssym,
(int *)(u_long)bi_sym->esym);
#ifdef DDB
#ifdef __arch64__
OF_set_symbol_lookup(OF_sym2val, OF_val2sym);
#else
OF_set_symbol_lookup(OF_sym2val32, OF_val2sym32);
#endif
#endif
#endif
if (OF_getprop(findroot(), "compatible", buf, sizeof(buf)) > 0) {
if (strcmp(buf, "sun4us") == 0)
setcputyp(CPU_SUN4US);
else if (strcmp(buf, "sun4v") == 0)
setcputyp(CPU_SUN4V);
}
bi_howto = lookup_bootinfo(BTINFO_BOOTHOWTO);
if (bi_howto)
boothowto = bi_howto->boothowto;
LOOKUP_BOOTINFO(bi_count, BTINFO_DTLB_SLOTS);
kernel_dtlb_slots = bi_count->count;
kernel_itlb_slots = kernel_dtlb_slots-1;
bi_count = lookup_bootinfo(BTINFO_ITLB_SLOTS);
if (bi_count)
kernel_itlb_slots = bi_count->count;
LOOKUP_BOOTINFO(bi_tlb, BTINFO_DTLB);
kernel_tlbs = &bi_tlb->tlb[0];
get_ncpus();
pmap_bootstrap(KERNBASE, bi_kend->addr);
#ifdef SUN4V
if (CPU_ISSUN4V) {
sun4v_soft_state_init();
sun4v_set_soft_state(SIS_TRANSITION, sun4v_soft_state_booting);
sun4v_interrupt_init();
#if 0
XXX notyet
sun4v_sdio_init();
#endif
}
#endif
}
static void
get_bootpath_from_prom(void)
{
struct btinfo_bootdev *bdev = NULL;
char sbuf[OFPATHLEN], *cp;
int chosen;
if ((chosen = OF_finddevice("/chosen")) == -1)
return;
bdev = lookup_bootinfo(BTINFO_BOOTDEV);
if (bdev != NULL) {
strcpy(ofbootpath, bdev->name);
} else {
if (OF_getprop(chosen, "bootpath", sbuf, sizeof(sbuf)) < 0)
return;
strcpy(ofbootpath, sbuf);
}
DPRINTF(ACDB_BOOTDEV, ("bootpath: %s\n", ofbootpath));
ofbootpackage = prom_finddevice(ofbootpath);
cp = strrchr(ofbootpath, ':');
if (cp) {
*cp = '\0';
ofbootpartition = cp+1;
}
cp = strrchr(ofbootpath, '@');
if (cp) {
for (; cp != ofbootpath; cp--) {
if (*cp == '/') {
ofboottarget = cp+1;
break;
}
}
}
DPRINTF(ACDB_BOOTDEV, ("bootpath phandle: 0x%x\n", ofbootpackage));
DPRINTF(ACDB_BOOTDEV, ("boot target: %s\n",
ofboottarget ? ofboottarget : "<none>"));
DPRINTF(ACDB_BOOTDEV, ("boot partition: %s\n",
ofbootpartition ? ofbootpartition : "<none>"));
if (OF_getprop(chosen, "bootargs", sbuf, sizeof(sbuf)) == -1)
return;
strcpy(ofbootargs, sbuf);
cp = ofbootargs;
while (*cp) {
while(*cp == ' ' || *cp == '\t') cp++;
if (*cp == '-' || *cp == '\0')
break;
while(*cp != ' ' && *cp != '\t' && *cp != '\0') cp++;
if (*cp != '\0')
*cp++ = '\0';
}
if (cp != ofbootargs)
ofbootfile = ofbootargs;
ofbootflags = cp;
if (*cp != '-')
return;
for (;*++cp;) {
int fl;
fl = 0;
BOOT_FLAG(*cp, fl);
if (!fl) {
printf("unknown option `%c'\n", *cp);
continue;
}
boothowto |= fl;
if (*cp == 'd') {
#if defined(KGDB)
kgdb_break_at_attach = 1;
#elif defined(DDB)
Debugger();
#else
printf("kernel has no debugger\n");
#endif
} else if (*cp == 't') {
extern int trap_trace_dis;
trap_trace_dis = 0;
}
}
}
void
cpu_configure(void)
{
bool userconf = (boothowto & RB_USERCONF) != 0;
get_bootpath_from_prom();
if (((boothowto & RB_USERCONF) != 0) && !userconf)
userconf_prompt();
splclock();
setpstate(getpstate()|PSTATE_IE);
if (config_rootfound("mainbus", NULL) == NULL)
panic("mainbus not configured");
setpstate(getpstate()|PSTATE_IE);
(void)spl0();
#ifdef SUN4V
if (CPU_ISSUN4V)
sun4v_set_soft_state(SIS_NORMAL, sun4v_soft_state_running);
#endif
}
#ifdef SUN4V
#define HSVC_GROUP_INTERRUPT 0x002
#define HSVC_GROUP_SOFT_STATE 0x003
#define HSVC_GROUP_SDIO 0x108
int sun4v_soft_state_initialized = 0;
void
sun4v_soft_state_init(void)
{
uint64_t minor;
if (prom_set_sun4v_api_version(HSVC_GROUP_SOFT_STATE, 1, 0, &minor))
return;
prom_sun4v_soft_state_supported();
sun4v_soft_state_initialized = 1;
}
void
sun4v_set_soft_state(int state, const char *desc)
{
paddr_t pa;
int err;
if (!sun4v_soft_state_initialized)
return;
if (!pmap_extract(pmap_kernel(), (vaddr_t)desc, &pa))
panic("sun4v_set_soft_state: pmap_extract failed");
err = hv_soft_state_set(state, pa);
if (err != H_EOK)
printf("soft_state_set: %d\n", err);
}
void
sun4v_interrupt_init(void)
{
uint64_t minor;
if (prom_set_sun4v_api_version(HSVC_GROUP_INTERRUPT, 3, 0, &minor))
return;
sun4v_group_interrupt_major = 3;
}
#if 0
XXX notyet
void
sun4v_sdio_init(void)
{
uint64_t minor;
if (prom_set_sun4v_api_version(HSVC_GROUP_SDIO, 1, 0, &minor))
return;
sun4v_group_sdio_major = 1;
}
#endif
#endif
void
cpu_rootconf(void)
{
if (booted_device == NULL) {
printf("FATAL: boot device not found, check your firmware "
"settings!\n");
}
rootconf();
}
char *
clockfreq(uint64_t freq)
{
static char buf[10];
size_t len;
freq /= 1000;
len = snprintf(buf, sizeof(buf), "%" PRIu64, freq / 1000);
freq %= 1000;
if (freq)
snprintf(buf + len, sizeof(buf) - len, ".%03" PRIu64, freq);
return buf;
}
static int
mbprint(void *aux, const char *name)
{
struct mainbus_attach_args *ma = aux;
if (name)
aprint_normal("%s at %s", ma->ma_name, name);
if (ma->ma_address)
aprint_normal(" addr 0x%08lx", (u_long)ma->ma_address[0]);
if (ma->ma_pri)
aprint_normal(" ipl %d", ma->ma_pri);
return (UNCONF);
}
int
mainbus_match(device_t parent, cfdata_t cf, void *aux)
{
return (1);
}
static void
mainbus_attach(device_t parent, device_t dev, void *aux)
{
extern struct sparc_bus_dma_tag mainbus_dma_tag;
extern struct sparc_bus_space_tag mainbus_space_tag;
struct mainbus_attach_args ma;
char sbuf[32];
const char *const *ssp, *sp = NULL;
char *c;
int node0, node, rv, i;
static const char *const openboot_special[] = {
"virtual-memory",
"aliases",
"memory",
"openprom",
"options",
"packages",
"chosen",
NULL
};
if (OF_getprop(findroot(), "banner-name", machine_banner,
sizeof machine_banner) < 0)
i = 0;
else {
i = 1;
if (((c = strchr(machine_banner, '(')) != NULL) &&
c != &machine_banner[0]) {
while (*c == '(' || *c == ' ') {
*c = '\0';
c--;
}
}
}
OF_getprop(findroot(), "name", machine_model, sizeof machine_model);
prom_getidprom();
if (i)
aprint_normal(": %s (%s): hostid %lx\n", machine_model,
machine_banner, hostid);
else
aprint_normal(": %s: hostid %lx\n", machine_model, hostid);
aprint_naive("\n");
devhandle_t selfh = device_handle(dev);
if (sparc_ncpus == 0)
panic("None of the CPUs found");
for (i = 0; i < __arraycount(intr_evcnts); i++)
evcnt_attach_static(&intr_evcnts[i]);
node = findroot();
for (node = OF_child(node); node; node = OF_peer(node)) {
if (OF_getprop(node, "device_type", sbuf, sizeof(sbuf)) <= 0)
continue;
if (strcmp(sbuf, "cpu") != 0)
continue;
memset(&ma, 0, sizeof(ma));
ma.ma_bustag = &mainbus_space_tag;
ma.ma_dmatag = &mainbus_dma_tag;
ma.ma_node = node;
ma.ma_name = "cpu";
config_found(dev, &ma, mbprint,
CFARGS(.devhandle = devhandle_from_of(selfh, ma.ma_node)));
}
node = findroot();
node0 = OF_child(node);
for (node = node0; node; node = OF_peer(node)) {
int portid;
DPRINTF(ACDB_PROBE, ("Node: %x", node));
if ((OF_getprop(node, "device_type", sbuf, sizeof(sbuf)) > 0) &&
strcmp(sbuf, "cpu") == 0)
continue;
OF_getprop(node, "name", sbuf, sizeof(sbuf));
DPRINTF(ACDB_PROBE, (" name %s\n", sbuf));
for (ssp = openboot_special; (sp = *ssp) != NULL; ssp++)
if (strcmp(sbuf, sp) == 0)
break;
if (sp != NULL)
continue;
memset(&ma, 0, sizeof ma);
ma.ma_bustag = &mainbus_space_tag;
ma.ma_dmatag = &mainbus_dma_tag;
ma.ma_name = sbuf;
ma.ma_node = node;
if (OF_getprop(node, "upa-portid", &portid, sizeof(portid)) !=
sizeof(portid) &&
OF_getprop(node, "portid", &portid, sizeof(portid)) !=
sizeof(portid))
portid = -1;
ma.ma_upaid = portid;
#define NREG32 3
if (prom_getproplen(node, "reg") == NREG32 * 4) {
int n;
int32_t *reg32p = NULL;
if (prom_getprop(node, "reg", sizeof(int32_t),
&n, ®32p) != 0)
continue;
if (n != NREG32) {
free(reg32p, M_DEVBUF);
continue;
}
ma.ma_reg = malloc(sizeof(*ma.ma_reg), M_DEVBUF,
M_NOWAIT);
if (ma.ma_reg == NULL) {
free(reg32p, M_DEVBUF);
continue;
}
ma.ma_reg->ur_paddr = reg32p[0];
ma.ma_reg->ur_paddr = ma.ma_reg->ur_paddr << 32;
ma.ma_reg->ur_paddr |= reg32p[1];
ma.ma_reg->ur_len = reg32p[2];
ma.ma_nreg = 1;
free(reg32p, M_DEVBUF);
#ifdef DEBUG
if (autoconf_debug & ACDB_PROBE) {
printf(" fixed up 64/32 reg property\n");
}
#endif
} else
if (prom_getprop(node, "reg", sizeof(*ma.ma_reg),
&ma.ma_nreg, &ma.ma_reg) != 0)
continue;
#ifdef DEBUG
if (autoconf_debug & ACDB_PROBE) {
if (ma.ma_nreg)
printf(" reg %08lx.%08lx\n",
(long)ma.ma_reg->ur_paddr,
(long)ma.ma_reg->ur_len);
else
printf(" no reg\n");
}
#endif
rv = prom_getprop(node, "interrupts", sizeof(*ma.ma_interrupts),
&ma.ma_ninterrupts, &ma.ma_interrupts);
if (rv != 0 && rv != ENOENT) {
free(ma.ma_reg, M_DEVBUF);
continue;
}
#ifdef DEBUG
if (autoconf_debug & ACDB_PROBE) {
if (ma.ma_interrupts)
printf(" interrupts %08x\n", *ma.ma_interrupts);
else
printf(" no interrupts\n");
}
#endif
rv = prom_getprop(node, "address", sizeof(*ma.ma_address),
&ma.ma_naddress, &ma.ma_address);
if (rv != 0 && rv != ENOENT) {
free(ma.ma_reg, M_DEVBUF);
if (ma.ma_ninterrupts)
free(ma.ma_interrupts, M_DEVBUF);
continue;
}
#ifdef DEBUG
if (autoconf_debug & ACDB_PROBE) {
if (ma.ma_naddress)
printf(" address %08x\n", *ma.ma_address);
else
printf(" no address\n");
}
#endif
(void) config_found(dev, (void *)&ma, mbprint,
CFARGS(.devhandle = prom_node_to_devhandle(selfh,
ma.ma_node)));
free(ma.ma_reg, M_DEVBUF);
if (ma.ma_ninterrupts)
free(ma.ma_interrupts, M_DEVBUF);
if (ma.ma_naddress)
free(ma.ma_address, M_DEVBUF);
}
memset(&ma, 0, sizeof ma);
ma.ma_name = "pcons";
(void) config_found(dev, (void *)&ma, mbprint, CFARGS_NONE);
}
CFATTACH_DECL_NEW(mainbus, 0,
mainbus_match, mainbus_attach, NULL, NULL);
int
romgetcursoraddr(int **rowp, int **colp)
{
cell_t row = 0UL, col = 0UL;
OF_interpret("stdout @ is my-self addr line# addr column# ", 0, 2,
&col, &row);
*rowp = (int *)(intptr_t)(row+4);
*colp = (int *)(intptr_t)(col+4);
return (row == 0UL || col == 0UL);
}
static void
dev_path_exact_match(device_t dev, int ofnode)
{
if (ofnode != ofbootpackage)
return;
booted_device = dev;
DPRINTF(ACDB_BOOTDEV, ("found bootdevice: %s\n", device_xname(dev)));
}
static void
dev_path_drive_match(device_t dev, int ctrlnode, int target,
uint64_t wwn, int lun)
{
int child = 0, ide_node = 0;
char buf[OFPATHLEN];
DPRINTF(ACDB_BOOTDEV, ("dev_path_drive_match: %s, controller %x, "
"target %d wwn %016" PRIx64 " lun %d\n", device_xname(dev),
ctrlnode, target, wwn, lun));
for (child = prom_firstchild(ctrlnode); child != 0;
child = prom_nextsibling(child))
if (child == ofbootpackage)
break;
if (child != ofbootpackage) {
for (ide_node = prom_firstchild(ctrlnode); ide_node != 0;
ide_node = prom_nextsibling(ide_node)) {
const char * name = prom_getpropstring(ide_node,
"device_type");
if (strcmp(name, "ide") != 0) continue;
for (child = prom_firstchild(ide_node); child != 0;
child = prom_nextsibling(child))
if (child == ofbootpackage)
break;
if (child == ofbootpackage)
break;
}
}
if (child == ofbootpackage) {
const char * name = prom_getpropstring(child, "name");
DPRINTF(ACDB_BOOTDEV, ("found controller of bootdevice\n"));
if (wwn)
snprintf(buf, sizeof(buf), "%s@w%016" PRIx64 ",%d",
name, wwn, lun);
else if (ide_node)
snprintf(buf, sizeof(buf), "%s@0",
device_is_a(dev, "cd") ? "cdrom" : "disk");
else
snprintf(buf, sizeof(buf), "%s@%d,%d",
name, target, lun);
if (ofboottarget && strcmp(buf, ofboottarget) == 0) {
booted_device = dev;
if (ofbootpartition)
booted_partition = *ofbootpartition - 'a';
DPRINTF(ACDB_BOOTDEV, ("found boot device: %s"
", partition %d\n", device_xname(dev),
booted_partition));
}
}
}
static bool
has_child_node(int parent, int search)
{
int child;
for (child = prom_firstchild(parent); child != 0;
child = prom_nextsibling(child)) {
if (child == search)
return true;
if (has_child_node(child, search))
return true;
}
return false;
}
static bool
openbios_secondary_ata_heuristic(int parent)
{
char tmp[OFPATHLEN];
int regs[4];
if (OF_getprop(parent, "device_type", tmp, sizeof(tmp)) <= 0)
return false;
if (strcmp(tmp, "ide") != 0)
return false;
DPRINTF(ACDB_BOOTDEV, ("parent device_type is ide\n"));
if (OF_getprop(parent, "compatible", tmp, sizeof(tmp)) > 0)
return false;
DPRINTF(ACDB_BOOTDEV, ("parent has no compatible property\n"));
if (OF_getprop(parent, "name", tmp, sizeof(tmp)) <= 0)
return false;
if (strcmp(tmp, "ide1") == 0) {
DPRINTF(ACDB_BOOTDEV, ("parent seems to be an (old) OpenBIOS"
" secondary ATA bus, applying workaround target+2\n"));
return true;
} else if (strcmp(tmp, "ide") == 0) {
if (OF_getprop(parent, "reg", ®s, sizeof(regs))
>= sizeof(regs[0])) {
DPRINTF(ACDB_BOOTDEV, ("parent seems to be an OpenBIOS"
" ATA bus #%u\n", regs[0]));
return regs[0] == 1;
}
}
return false;
}
static void
dev_bi_unit_drive_match(device_t dev, int ctrlnode, int target,
uint64_t wwn, int lun)
{
static struct btinfo_bootdev_unit *bi_unit = NULL;
uint32_t off = 0;
static bool passed = false;
#ifdef DEBUG
char ctrl_path[OFPATHLEN], parent_path[OFPATHLEN], dev_path[OFPATHLEN];
#endif
if (!passed) {
bi_unit = lookup_bootinfo(BTINFO_BOOTDEV_UNIT);
passed = true;
}
if (bi_unit == NULL) {
dev_path_drive_match(dev, ctrlnode, target, wwn, lun);
return;
}
#ifdef DEBUG
DPRINTF(ACDB_BOOTDEV, ("dev_bi_unit_drive_match: %s, controller %x, "
"target %d wwn %016" PRIx64 " lun %d\n", device_xname(dev),
ctrlnode, target, wwn, lun));
OF_package_to_path(ctrlnode, ctrl_path, sizeof(ctrl_path));
OF_package_to_path(bi_unit->phandle, dev_path, sizeof(dev_path));
OF_package_to_path(bi_unit->parent, parent_path, sizeof(parent_path));
DPRINTF(ACDB_BOOTDEV, ("controller %x : %s\n", ctrlnode, ctrl_path));
DPRINTF(ACDB_BOOTDEV, ("phandle %x : %s\n", bi_unit->phandle, dev_path));
DPRINTF(ACDB_BOOTDEV, ("parent %x : %s\n", bi_unit->parent, parent_path));
#endif
if (ctrlnode != bi_unit->parent
&& !has_child_node(ctrlnode, bi_unit->phandle)) {
DPRINTF(ACDB_BOOTDEV, ("controller %x : %s does not match "
"bootinfo: %x : %s\n",
ctrlnode, ctrl_path, bi_unit->parent, parent_path));
return;
}
if (ctrlnode == bi_unit->parent) {
DPRINTF(ACDB_BOOTDEV, ("controller %x : %s is bootinfo"
" parent\n", ctrlnode, ctrl_path));
} else {
DPRINTF(ACDB_BOOTDEV, ("controller %x : %s is parent of"
" %x : %s\n", ctrlnode, ctrl_path, bi_unit->parent,
parent_path));
if (openbios_secondary_ata_heuristic(bi_unit->parent))
off = 2;
}
if (bi_unit->wwn != wwn || (bi_unit->target+off) != target
|| bi_unit->lun != lun) {
DPRINTF(ACDB_BOOTDEV, ("mismatch: wwn %016" PRIx64 " - %016" PRIx64
", target %d - %d, lun %d - %d\n",
bi_unit->wwn, wwn, bi_unit->target, target, bi_unit->lun, lun));
return;
}
booted_device = dev;
if (ofbootpartition)
booted_partition = *ofbootpartition - 'a';
DPRINTF(ACDB_BOOTDEV, ("found boot device: %s"
", partition %d\n", device_xname(dev),
booted_partition));
}
static int
sparc64_ether_get_mac_address(device_t dev, devhandle_t call_handle, void *v)
{
struct ether_get_mac_address_args *args = v;
int node = devhandle_to_of(call_handle);
char name[32], device_type[32];
if (OF_getprop(node, "name", name, sizeof(name)) <= 0) {
name[0] = '\0';
}
if (OF_getprop(node, "device_type", device_type,
sizeof(device_type)) <= 0) {
device_type[0] = '\0';
}
if (strcmp(name, "network") == 0 ||
strcmp(device_type, "network") == 0) {
prom_getether(node, args->enaddr);
} else {
if (! prom_get_node_ether(node, args->enaddr)) {
return ENOENT;
}
}
return 0;
}
OF_DEVICE_CALL_REGISTER(ETHER_GET_MAC_ADDRESS_STR,
sparc64_ether_get_mac_address)
void
device_register(device_t dev, void *aux)
{
device_t busdev = device_parent(dev);
devhandle_t devhandle;
int ofnode = 0;
devhandle = device_handle(dev);
if (devhandle_type(devhandle) == DEVHANDLE_TYPE_OF) {
ofnode = devhandle_to_of(devhandle);
}
if (busdev == NULL) {
} else if (device_is_a(busdev, "iic")) {
struct i2c_attach_args *ia = aux;
if (ia->ia_name == NULL)
return;
set_i2c_dev_props(dev, device_parent(busdev), aux);
return;
} else if (device_is_a(dev, "sd") || device_is_a(dev, "cd")) {
struct scsipibus_attach_args *sa = aux;
struct scsipi_periph *periph = sa->sa_periph;
int off = 0;
if (device_is_a(busdev, "atapibus")) {
busdev = device_parent(busdev);
if (periph->periph_channel->chan_channel == 1)
off = 2;
}
busdev = device_parent(busdev);
devhandle = device_handle(busdev);
if (devhandle_type(devhandle) != DEVHANDLE_TYPE_OF)
{
printf("Not OF: %d\n", devhandle_type(devhandle));
return;
}
ofnode = devhandle_to_of(devhandle);
if (CPU_ISSUN4V && device_is_a(busdev, "vdsk")) {
dev_path_exact_match(dev, ofnode);
} else {
dev_bi_unit_drive_match(dev, ofnode,
periph->periph_target + off, 0, periph->periph_lun);
}
if (device_is_a(busdev, "scsibus")) {
add_drivebay_props(dev, ofnode, aux);
}
return;
} else if (device_is_a(dev, "wd")) {
struct ata_device *adev = aux;
devhandle = device_handle(device_parent(busdev));
if (devhandle_type(devhandle) != DEVHANDLE_TYPE_OF)
return;
ofnode = devhandle_to_of(devhandle);
dev_bi_unit_drive_match(dev, ofnode, adev->adev_channel*2+
adev->adev_drv_data->drive, 0, 0);
return;
} else if (device_is_a(dev, "ld")) {
devhandle = device_handle(busdev);
ofnode = devhandle_to_of(devhandle);
}
if (busdev == NULL)
return;
if (ofnode != 0) {
char tmpstr[32];
char tmpstr2[32];
int node;
uint32_t id = 0;
uint64_t nwwn = 0, pwwn = 0;
prop_dictionary_t dict;
prop_number_t pwwnd = NULL, nwwnd = NULL;
prop_number_t idd = NULL;
dev_path_exact_match(dev, ofnode);
if (OF_getprop(ofnode, "name", tmpstr, sizeof(tmpstr)) <= 0)
tmpstr[0] = 0;
if (OF_getprop(ofnode, "device_type", tmpstr2, sizeof(tmpstr2)) <= 0)
tmpstr2[0] = 0;
if (strcmp(tmpstr, "network") == 0 ||
strcmp(tmpstr2, "network") == 0) {
device_setprop_bool(dev, "without-seeprom", true);
}
if (strcmp(tmpstr, "scsi-fcp") == 0) {
dict = device_properties(dev);
if (OF_getprop(ofnode, "port-wwn", &pwwn, sizeof(pwwn))
== sizeof(pwwn)) {
pwwnd =
prop_number_create_unsigned(pwwn);
prop_dictionary_set(dict, "port-wwn", pwwnd);
prop_object_release(pwwnd);
}
if (OF_getprop(ofnode, "node-wwn", &nwwn, sizeof(nwwn))
== sizeof(nwwn)) {
nwwnd =
prop_number_create_unsigned(nwwn);
prop_dictionary_set(dict, "node-wwn", nwwnd);
prop_object_release(nwwnd);
}
}
if (strcmp(tmpstr2, "scsi") == 0 ||
strcmp(tmpstr2, "scsi-2") == 0) {
dict = device_properties(dev);
for (node = ofnode; node != 0; node = OF_parent(node)) {
if (OF_getprop(node, "scsi-initiator-id", &id,
sizeof(id)) <= 0)
continue;
idd = prop_number_create_unsigned(id);
prop_dictionary_set(dict,
"scsi-initiator-id", idd);
prop_object_release(idd);
break;
}
}
}
if (device_is_a(dev, "iic")) {
devhandle_t bushandle = device_handle(busdev);
int busnode =
devhandle_type(bushandle) == DEVHANDLE_TYPE_OF ?
devhandle_to_of(bushandle) : 0;
if (busnode) {
prop_dictionary_t props = device_properties(busdev);
prop_object_t cfg = prop_dictionary_get(props,
"i2c-child-devices");
if (!cfg) {
int node;
const char *name;
name = prom_getpropstring(busnode, "name");
if (strcmp(name, "pmu") == 0) {
for (node = OF_child(busnode);
node != 0; node = OF_peer(node)) {
name = prom_getpropstring(node,
"name");
if (strcmp(name, "i2c") == 0) {
busnode = node;
break;
}
}
}
cfg = of_copy_i2c_devs(busnode,
sizeof(cell_t), 1);
if (cfg != NULL) {
prop_dictionary_set(props,
"i2c-child-devices", cfg);
prop_object_release(cfg);
}
}
}
set_i2c_bus_props(busdev, busnode);
}
if (device_is_a(busdev, "pci")) {
struct pci_attach_args *pa = aux;
prop_dictionary_t dict;
int sub;
int console = 0;
dict = device_properties(dev);
if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY)
return;
console = (ofnode == console_node);
if (!console) {
sub = OF_child(ofnode);
while ((sub != 0) && (sub != console_node)) {
sub = OF_peer(sub);
}
if (sub == console_node) {
console = true;
}
}
copyprops(busdev, ofnode, dict, console);
if (console) {
uint64_t cmap_cb;
prop_dictionary_set_uint32(dict,
"instance_handle", console_instance);
gfb_cb.gcc_cookie =
(void *)(intptr_t)console_instance;
gfb_cb.gcc_set_mapreg = of_set_palette;
cmap_cb = (uint64_t)(uintptr_t)&gfb_cb;
prop_dictionary_set_uint64(dict,
"cmap_callback", cmap_cb);
}
#ifdef notyet
else {
int width;
if (OF_getprop(node, "width", &width, sizeof(width))
!= 4) {
instance = OF_open(name);
}
}
#endif
set_static_edid(dict);
}
}
void
device_register_post_config(device_t dev, void *aux)
{
if (booted_device == NULL && device_is_a(dev, "sd")) {
struct scsipibus_attach_args *sa = aux;
struct scsipi_periph *periph = sa->sa_periph;
uint64_t wwn = 0;
if (prop_dictionary_get_uint64(device_properties(dev),
"port-wwn", &wwn)) {
devhandle_t ctlr_devhandle = device_handle(
device_parent(device_parent(dev)));
if (devhandle_type(ctlr_devhandle) != DEVHANDLE_TYPE_OF)
return;
int ofnode = devhandle_to_of(ctlr_devhandle);
for (ofnode = OF_child(ofnode);
ofnode != 0 && booted_device == NULL;
ofnode = OF_peer(ofnode)) {
dev_bi_unit_drive_match(dev, ofnode,
periph->periph_target,
wwn, periph->periph_lun);
}
}
}
}
static void
copyprops(device_t busdev, int node, prop_dictionary_t dict, int is_console)
{
device_t cntrlr;
prop_dictionary_t psycho;
paddr_t fbpa, mem_base = 0;
uint32_t temp, fboffset;
uint32_t fbaddr = 0;
int options;
char output_device[OFPATHLEN];
char *pos;
cntrlr = device_parent(busdev);
if (cntrlr != NULL) {
psycho = device_properties(cntrlr);
prop_dictionary_get_uint64(psycho, "mem_base", &mem_base);
}
if (is_console)
prop_dictionary_set_bool(dict, "is_console", 1);
of_to_uint32_prop(dict, node, "width", "width");
of_to_uint32_prop(dict, node, "height", "height");
of_to_uint32_prop(dict, node, "linebytes", "linebytes");
if (!of_to_uint32_prop(dict, node, "depth", "depth") &&
!of_to_uint32_prop(dict, node, "depth ", "depth")) {
prop_dictionary_set_uint32(dict, "depth", 8);
}
OF_getprop(node, "address", &fbaddr, sizeof(fbaddr));
if (fbaddr != 0) {
pmap_extract(pmap_kernel(), fbaddr, &fbpa);
#ifdef DEBUG
printf("membase: %lx fbpa: %lx\n", (unsigned long)mem_base,
(unsigned long)fbpa);
#endif
if (mem_base == 0) {
fboffset = (uint32_t)(fbpa & 0xffffffff);
}
fboffset = (uint32_t)(fbpa - mem_base);
prop_dictionary_set_uint32(dict, "address", fboffset);
}
if (!of_to_dataprop(dict, node, "EDID", "EDID"))
of_to_dataprop(dict, node, "edid", "EDID");
temp = 0;
if (OF_getprop(node, "ATY,RefCLK", &temp, sizeof(temp)) != 4) {
OF_getprop(OF_parent(node), "ATY,RefCLK", &temp,
sizeof(temp));
}
if (temp != 0)
prop_dictionary_set_uint32(dict, "refclk", temp / 10);
if (!is_console)
return;
options = OF_finddevice("/options");
if ((options == 0) || (options == -1))
return;
if (OF_getprop(options, "output-device", output_device, OFPATHLEN) == 0)
return;
pos = strstr(output_device, ":r");
if (pos == NULL)
return;
prop_dictionary_set_string(dict, "videomode", pos + 2);
}
static void
of_set_palette(void *cookie, int index, int r, int g, int b)
{
int ih = (int)((intptr_t)cookie);
OF_call_method_1("color!", ih, 4, r, g, b, index);
}