#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.60 2025/03/09 01:06:41 thorpej Exp $");
#include "pci.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/disklabel.h>
#include <sys/reboot.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <dev/cons.h>
#include <dev/pci/pcivar.h>
#include <net/if.h>
#include <net/if_ether.h>
#include <machine/autoconf.h>
#include <machine/alpha.h>
#include <machine/cpu.h>
#include <machine/prom.h>
#include <machine/cpuconf.h>
#include <machine/intr.h>
struct bootdev_data *bootdev_data;
bool bootdev_is_disk;
bool bootdev_is_net;
static void parse_prom_bootdev(void);
void
cpu_configure(void)
{
parse_prom_bootdev();
(void)alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
if (config_rootfound("mainbus", NULL) == NULL)
panic("no mainbus found");
(void)spl0();
hwrpb_restart_setup();
}
static void
qemu_find_rootdev(void)
{
char buf[32] = { 0 };
if (! prom_qemu_getenv("rootdev", buf, sizeof(buf))) {
if (! prom_qemu_getenv("root", buf, sizeof(buf))) {
printf("WARNING: no rootdev= or root= arguments "
"provided by Qemu\n");
return;
}
}
const size_t devlen = strlen("/dev/");
const char *cp = buf;
char *ecp = &buf[sizeof(buf) - 1];
if (strlen(cp) > devlen && strncmp(cp, "/dev/", devlen) == 0) {
cp += devlen;
}
while (ecp != cp) {
if (*ecp >= '0' && *ecp <= '9') {
break;
}
*ecp-- = '\0';
}
snprintf(bootinfo.booted_dev, sizeof(bootinfo.booted_dev),
"root=%s", cp);
booted_device = device_find_by_xname(cp);
}
static bool
parse_dec_macaddr(const char *str, uint8_t enaddr[ETHER_ADDR_LEN])
{
char *cp;
long long l;
int i;
for (i = 0; i < ETHER_ADDR_LEN; i++) {
l = strtoll(str, &cp, 16);
if (l < 0 || l > 0xff) {
return false;
}
if (*cp == '-') {
enaddr[i] = (uint8_t)l;
str = cp + 1;
continue;
}
if (*cp == ' ' || *cp == '\0') {
enaddr[i] = (uint8_t)l;
return i == ETHER_ADDR_LEN - 1;
}
break;
}
return false;
}
static void
netboot_find_rootdev_planb(void)
{
struct psref psref;
uint8_t enaddr[ETHER_ADDR_LEN];
char ifname[IFNAMSIZ];
int i;
if (!bootdev_is_net) {
return;
}
for (i = 2; bootinfo.booted_dev[i] != '\0'; i++) {
if (bootinfo.booted_dev[i] == '-') {
if (parse_dec_macaddr(&bootinfo.booted_dev[i - 2],
enaddr)) {
break;
}
}
}
if (bootinfo.booted_dev[i] == '\0') {
return;
}
struct ifnet *ifp = if_get_bylla(enaddr, ETHER_ADDR_LEN, &psref);
if (ifp == NULL) {
return;
}
strlcpy(ifname, if_name(ifp), sizeof(ifname));
if_put(ifp, &psref);
booted_device = device_find_by_xname(ifname);
}
void
cpu_rootconf(void)
{
if (booted_device == NULL && alpha_is_qemu) {
qemu_find_rootdev();
}
if (booted_device == NULL) {
netboot_find_rootdev_planb();
}
if (booted_device == NULL) {
printf("WARNING: can't figure what device matches \"%s\"\n",
bootinfo.booted_dev);
}
rootconf();
}
static inline int
atoi(const char *s)
{
return (int)strtoll(s, NULL, 10);
}
static void
parse_prom_bootdev(void)
{
static char hacked_boot_dev[128];
static struct bootdev_data bd;
char *cp, *scp, *boot_fields[8];
int i, done;
booted_device = NULL;
booted_partition = 0;
bootdev_data = NULL;
memcpy(hacked_boot_dev, bootinfo.booted_dev,
uimin(sizeof bootinfo.booted_dev, sizeof hacked_boot_dev));
#if 0
printf("parse_prom_bootdev: boot dev = \"%s\"\n", hacked_boot_dev);
#endif
i = 0;
scp = cp = hacked_boot_dev;
for (done = 0; !done; cp++) {
if (*cp != ' ' && *cp != '\0')
continue;
if (*cp == '\0')
done = 1;
*cp = '\0';
boot_fields[i++] = scp;
scp = cp + 1;
if (i == 8)
done = 1;
}
if (i != 8)
return;
#if 0
printf("i = %d, done = %d\n", i, done);
for (i--; i >= 0; i--)
printf("%d = %s\n", i, boot_fields[i]);
#endif
bd.protocol = boot_fields[0];
bd.bus = atoi(boot_fields[1]);
bd.slot = atoi(boot_fields[2]);
bd.channel = atoi(boot_fields[3]);
bd.remote_address = boot_fields[4];
bd.unit = atoi(boot_fields[5]);
bd.boot_dev_type = atoi(boot_fields[6]);
bd.ctrl_dev_type = boot_fields[7];
#if 0
printf("parsed: proto = %s, bus = %d, slot = %d, channel = %d,\n",
bd.protocol, bd.bus, bd.slot, bd.channel);
printf("\tremote = %s, unit = %d, dev_type = %d, ctrl_type = %s\n",
bd.remote_address, bd.unit, bd.boot_dev_type, bd.ctrl_dev_type);
#endif
bootdev_data = &bd;
bootdev_is_disk = (strcasecmp(bd.protocol, "SCSI") == 0) ||
(strcasecmp(bd.protocol, "RAID") == 0) ||
(strcasecmp(bd.protocol, "I2O") == 0) ||
(strcasecmp(bd.protocol, "IDE") == 0);
bootdev_is_net = (strcasecmp(bd.protocol, "BOOTP") == 0) ||
(strcasecmp(bd.protocol, "MOP") == 0);
#if 0
printf("bootdev_is_disk = %d, bootdev_is_net = %d\n",
bootdev_is_disk, bootdev_is_net);
#endif
}
void
device_register(device_t dev, void *aux)
{
#if NPCI > 0
device_t parent = device_parent(dev);
if (parent != NULL && device_is_a(parent, "pci"))
device_pci_register(dev, aux);
#endif
if (platform.device_register)
(*platform.device_register)(dev, aux);
}