#include <sys/bootconf.h>
#include <sys/conf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_implfuncs.h>
#include <sys/hwconf.h>
#include <sys/instance.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/promif.h>
#include <sys/sunndi.h>
#include <sys/ndi_impldefs.h>
#include <sys/systeminfo.h>
#include <sys/hwconf.h>
#include <sys/sysevent_impl.h>
#include <sys/sunldi_impl.h>
#include <sys/disp.h>
#include <sys/bootconf.h>
#include <sys/fm/util.h>
#include <sys/ddifm_impl.h>
#include <sys/ddi_ufm_impl.h>
#include <sys/ksensor_impl.h>
extern dev_info_t *top_devinfo;
extern dev_info_t *scsi_vhci_dip;
extern struct hwc_class *hcl_head;
static char *rootname;
kmutex_t global_vhci_lock;
major_t mm_major;
major_t nulldriver_major;
static void impl_create_root_class(void);
static void create_devinfo_tree(void);
#if defined(__x86)
char *bootpath_prop = NULL;
char *fstype_prop = NULL;
#endif
void
setup_ddi(void)
{
impl_ddi_init_nodeid();
impl_create_root_class();
create_devinfo_tree();
e_ddi_instance_init();
impl_ddi_callback_init();
log_event_init();
fm_init();
ndi_fm_init();
irm_init();
ufm_init();
ksensor_init();
(void) i_ddi_load_drvconf(DDI_MAJOR_T_NONE);
ldi_init();
i_ddi_devices_init();
i_ddi_read_devices_files();
}
void
setup_ddi_poststartup(void)
{
extern void i_ddi_start_flush_daemon(void);
extern void i_ddi_irm_poststartup(void);
extern void i_ddi_intr_redist_all_cpus(void);
i_ddi_start_flush_daemon();
i_ddi_irm_poststartup();
i_ddi_intr_redist_all_cpus();
}
static void
impl_create_root_class(void)
{
major_t major;
size_t size;
char *cp;
if ((major = ddi_name_to_major("rootnex")) == DDI_MAJOR_T_NONE)
panic("Couldn't find major number for 'rootnex'");
size = (size_t)BOP_GETPROPLEN(bootops, "mfg-name") + 1;
rootname = kmem_zalloc(size, KM_SLEEP);
(void) BOP_GETPROP(bootops, "mfg-name", rootname);
for (cp = rootname; *cp; cp++)
if (*cp == '/')
*cp = '_';
if (make_mbind(rootname, major, NULL, mb_hashtab) != 0) {
cmn_err(CE_WARN, "A driver or driver alias has already "
"registered the name \"%s\". The root nexus needs to "
"use this name, and will override the existing entry. "
"Please correct /etc/name_to_major and/or "
"/etc/driver_aliases and reboot.", rootname);
delete_mbind(rootname, mb_hashtab);
if (make_mbind(rootname, major, NULL, mb_hashtab) != 0)
panic("mb_hashtab: inconsistent state.");
}
(void) BOP_GETPROP(bootops, "impl-arch-name", platform);
#if defined(__x86)
size = (size_t)BOP_GETPROPLEN(bootops, "bootpath");
if (size != -1) {
bootpath_prop = kmem_zalloc(size, KM_SLEEP);
(void) BOP_GETPROP(bootops, "bootpath", bootpath_prop);
setbootpath(bootpath_prop);
}
size = (size_t)BOP_GETPROPLEN(bootops, "fstype");
if (size != -1) {
fstype_prop = kmem_zalloc(size, KM_SLEEP);
(void) BOP_GETPROP(bootops, "fstype", fstype_prop);
setbootfstype(fstype_prop);
}
#endif
}
static int
getlongprop_buf(int id, char *name, char *buf, int maxlen)
{
int size;
size = prom_getproplen((pnode_t)id, name);
if (size <= 0 || (size > maxlen - 1))
return (-1);
if (-1 == prom_getprop((pnode_t)id, name, buf))
return (-1);
if (strcmp("name", name) == 0) {
if (buf[size - 1] != '\0') {
buf[size] = '\0';
size += 1;
}
}
return (size);
}
static int
get_neighbors(dev_info_t *di, int flag)
{
register int nid, snid, cnid;
dev_info_t *parent;
char buf[OBP_MAXPROPNAME];
if (di == NULL)
return (DDI_WALK_CONTINUE);
nid = ddi_get_nodeid(di);
snid = cnid = 0;
switch (flag) {
case DDI_WALK_PRUNESIB:
cnid = (int)prom_childnode((pnode_t)nid);
break;
case DDI_WALK_PRUNECHILD:
snid = (int)prom_nextnode((pnode_t)nid);
break;
case 0:
snid = (int)prom_nextnode((pnode_t)nid);
cnid = (int)prom_childnode((pnode_t)nid);
break;
default:
return (DDI_WALK_TERMINATE);
}
if (snid && (snid != -1) && ((parent = ddi_get_parent(di)) != NULL)) {
for (; snid && (snid != -1);
snid = (int)prom_nextnode((pnode_t)snid)) {
if (getlongprop_buf(snid, OBP_NAME, buf,
sizeof (buf)) > 0) {
if (check_status(snid, buf, parent) ==
DDI_SUCCESS) {
(void) ddi_add_child(parent, buf,
snid, -1);
break;
}
}
}
}
if (cnid && (cnid != -1)) {
if (getlongprop_buf(cnid, OBP_NAME, buf, sizeof (buf)) > 0) {
if (check_status(cnid, buf, di) == DDI_SUCCESS) {
(void) ddi_add_child(di, buf, cnid, -1);
} else {
for (cnid = (int)prom_nextnode((pnode_t)cnid);
cnid && (cnid != -1);
cnid = (int)prom_nextnode((pnode_t)cnid)) {
if (getlongprop_buf(cnid, OBP_NAME,
buf, sizeof (buf)) > 0) {
if (check_status(cnid, buf, di)
== DDI_SUCCESS) {
(void) ddi_add_child(
di, buf, cnid, -1);
break;
}
}
}
}
}
}
return (DDI_WALK_CONTINUE);
}
static void
di_dfs(dev_info_t *devi, int (*f)(dev_info_t *, int), caddr_t arg)
{
(void) (*f)(devi, 0);
if (devi) {
di_dfs((dev_info_t *)DEVI(devi)->devi_child, f, arg);
di_dfs((dev_info_t *)DEVI(devi)->devi_sibling, f, arg);
}
}
dev_info_t *
i_ddi_create_branch(dev_info_t *pdip, int nid)
{
char *buf;
dev_info_t *dip = NULL;
if (pdip == NULL || nid == OBP_NONODE || nid == OBP_BADNODE)
return (NULL);
buf = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP);
if (getlongprop_buf(nid, OBP_NAME, buf, OBP_MAXPROPNAME) > 0) {
if (check_status(nid, buf, pdip) == DDI_SUCCESS)
dip = ddi_add_child(pdip, buf, nid, -1);
}
kmem_free(buf, OBP_MAXPROPNAME);
if (dip == NULL)
return (NULL);
(void) get_neighbors(dip, DDI_WALK_PRUNESIB);
di_dfs(ddi_get_child(dip), get_neighbors, 0);
return (dip);
}
static void
create_devinfo_tree(void)
{
major_t major;
pnode_t nodeid;
i_ddi_node_cache_init();
#if defined(__sparc)
nodeid = prom_nextnode(0);
#else
nodeid = DEVI_SID_NODEID;
#endif
top_devinfo = i_ddi_alloc_node(NULL, rootname,
nodeid, -1, NULL, KM_SLEEP);
ndi_hold_devi(top_devinfo);
i_ddi_add_devimap(top_devinfo);
major = ddi_name_to_major("rootnex");
ASSERT(major != DDI_MAJOR_T_NONE);
DEVI(top_devinfo)->devi_major = major;
devnamesp[major].dn_head = top_devinfo;
i_ddi_set_binding_name(top_devinfo, rootname);
i_ddi_set_node_state(top_devinfo, DS_BOUND);
di_dfs(ddi_root_node(), get_neighbors, 0);
#if !defined(__sparc)
{
extern void impl_setup_ddi(void);
impl_setup_ddi();
}
#endif
}
void
i_ddi_init_root()
{
#ifdef DDI_PROP_DEBUG
(void) ddi_prop_debug(1);
#endif
if (impl_ddi_sunbus_initchild(top_devinfo) != DDI_SUCCESS)
panic("Could not initialize root nexus");
DEVI(top_devinfo)->devi_ops = ndi_hold_driver(top_devinfo);
ASSERT(DEV_OPS_HELD(DEVI(top_devinfo)->devi_ops));
DEVI(top_devinfo)->devi_instance = e_ddi_assign_instance(top_devinfo);
(void) i_ddi_load_drvconf(DEVI(top_devinfo)->devi_major);
mutex_enter(&(DEVI(top_devinfo)->devi_lock));
DEVI_SET_ATTACHING(top_devinfo);
mutex_exit(&(DEVI(top_devinfo)->devi_lock));
if (devi_attach(top_devinfo, DDI_ATTACH) != DDI_SUCCESS)
panic("Could not attach root nexus");
mutex_enter(&(DEVI(top_devinfo)->devi_lock));
DEVI_CLR_ATTACHING(top_devinfo);
mutex_exit(&(DEVI(top_devinfo)->devi_lock));
mutex_init(&global_vhci_lock, NULL, MUTEX_DEFAULT, NULL);
ndi_hold_devi(top_devinfo);
i_ddi_set_node_state(top_devinfo, DS_READY);
(void) i_ndi_make_spec_children(top_devinfo, 0);
pm_init_locks();
options_dip = i_ddi_attach_pseudo_node("options");
pseudo_dip = i_ddi_attach_pseudo_node(DEVI_PSEUDO_NEXNAME);
(void) i_ndi_make_spec_children(pseudo_dip, 0);
clone_dip = i_ddi_attach_pseudo_node("clone");
clone_major = ddi_driver_major(clone_dip);
mm_major = ddi_name_to_major("mm");
nulldriver_major = ddi_name_to_major("nulldriver");
scsi_vhci_dip = i_ddi_attach_pseudo_node("scsi_vhci");
}