103831d35Sstevel /*
203831d35Sstevel * CDDL HEADER START
303831d35Sstevel *
403831d35Sstevel * The contents of this file are subject to the terms of the
503831d35Sstevel * Common Development and Distribution License (the "License").
603831d35Sstevel * You may not use this file except in compliance with the License.
703831d35Sstevel *
803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel * See the License for the specific language governing permissions
1103831d35Sstevel * and limitations under the License.
1203831d35Sstevel *
1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel *
1903831d35Sstevel * CDDL HEADER END
2003831d35Sstevel */
2103831d35Sstevel
2203831d35Sstevel /*
23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2403831d35Sstevel * Use is subject to license terms.
2503831d35Sstevel */
2603831d35Sstevel
2703831d35Sstevel #include <sys/types.h>
2803831d35Sstevel #include <sys/cmn_err.h>
2903831d35Sstevel #include <sys/conf.h>
3003831d35Sstevel #include <sys/autoconf.h>
3103831d35Sstevel #include <sys/systm.h>
3203831d35Sstevel #include <sys/modctl.h>
3303831d35Sstevel #include <sys/ddi.h>
3403831d35Sstevel #include <sys/sunddi.h>
3503831d35Sstevel #include <sys/sunndi.h>
3603831d35Sstevel #include <sys/ndi_impldefs.h>
3703831d35Sstevel #include <sys/ddi_impldefs.h>
3803831d35Sstevel #include <sys/promif.h>
3903831d35Sstevel #include <sys/stat.h>
4003831d35Sstevel #include <sys/kmem.h>
4103831d35Sstevel #include <sys/promif.h>
4203831d35Sstevel #include <sys/conf.h>
4303831d35Sstevel #include <sys/obpdefs.h>
4403831d35Sstevel #include <sys/sgsbbc_mailbox.h>
4503831d35Sstevel #include <sys/cpuvar.h>
4603831d35Sstevel #include <vm/seg_kmem.h>
4703831d35Sstevel #include <sys/prom_plat.h>
4803831d35Sstevel #include <sys/machsystm.h>
4903831d35Sstevel #include <sys/cheetahregs.h>
5003831d35Sstevel
5103831d35Sstevel #include <sys/sbd_ioctl.h>
5203831d35Sstevel #include <sys/sbd.h>
5303831d35Sstevel #include <sys/sbdp_priv.h>
5403831d35Sstevel
5503831d35Sstevel static int sbdp_detach_nodes(attach_pkt_t *);
5603831d35Sstevel static void
sbdp_walk_prom_tree_worker(pnode_t node,int (* f)(pnode_t,void *,uint_t),void * arg)5703831d35Sstevel sbdp_walk_prom_tree_worker(
5803831d35Sstevel pnode_t node,
5903831d35Sstevel int(*f)(pnode_t, void *, uint_t),
6003831d35Sstevel void *arg)
6103831d35Sstevel {
6203831d35Sstevel /*
6303831d35Sstevel * Ignore return value from callback. Return value from callback
6403831d35Sstevel * does NOT indicate subsequent walk behavior.
6503831d35Sstevel */
6603831d35Sstevel (void) (*f)(node, arg, 0);
6703831d35Sstevel
6803831d35Sstevel if (node != OBP_NONODE) {
6903831d35Sstevel sbdp_walk_prom_tree_worker(prom_childnode(node), f, arg);
7003831d35Sstevel sbdp_walk_prom_tree_worker(prom_nextnode(node), f, arg);
7103831d35Sstevel }
7203831d35Sstevel }
7303831d35Sstevel
7403831d35Sstevel struct sbdp_walk_prom_tree_args {
7503831d35Sstevel pnode_t node;
7603831d35Sstevel int (*f)(pnode_t, void *, uint_t);
7703831d35Sstevel void *arg;
7803831d35Sstevel };
7903831d35Sstevel
8003831d35Sstevel /*ARGSUSED*/
8103831d35Sstevel static int
sbdp_walk_prom_tree_start(void * arg,int has_changed)8203831d35Sstevel sbdp_walk_prom_tree_start(void *arg, int has_changed)
8303831d35Sstevel {
8403831d35Sstevel struct sbdp_walk_prom_tree_args *argbp = arg;
8503831d35Sstevel
8603831d35Sstevel sbdp_walk_prom_tree_worker(argbp->node, argbp->f, argbp->arg);
8703831d35Sstevel return (0);
8803831d35Sstevel }
8903831d35Sstevel
9003831d35Sstevel void
sbdp_walk_prom_tree(pnode_t node,int (* f)(pnode_t,void *,uint_t),void * arg)9103831d35Sstevel sbdp_walk_prom_tree(pnode_t node, int(*f)(pnode_t, void *, uint_t), void *arg)
9203831d35Sstevel {
9303831d35Sstevel struct sbdp_walk_prom_tree_args arg_block;
9403831d35Sstevel
9503831d35Sstevel arg_block.node = node;
9603831d35Sstevel arg_block.f = f;
9703831d35Sstevel arg_block.arg = arg;
98*07d06da5SSurya Prakki (void) prom_tree_access(sbdp_walk_prom_tree_start, &arg_block, NULL);
9903831d35Sstevel }
10003831d35Sstevel
10103831d35Sstevel static void
sbdp_attach_branch(dev_info_t * pdip,pnode_t node,void * arg)10203831d35Sstevel sbdp_attach_branch(dev_info_t *pdip, pnode_t node, void *arg)
10303831d35Sstevel {
10403831d35Sstevel attach_pkt_t *apktp = (attach_pkt_t *)arg;
10503831d35Sstevel pnode_t child;
10603831d35Sstevel dev_info_t *dip = NULL;
10703831d35Sstevel static int err = 0;
10803831d35Sstevel static int len = 0;
10903831d35Sstevel char name[OBP_MAXDRVNAME];
11003831d35Sstevel #if OBP_MAXDRVNAME == OBP_MAXPROPNAME
11103831d35Sstevel #define buf name
11203831d35Sstevel #else
11303831d35Sstevel char buf[OBP_MAXPROPNAME];
11403831d35Sstevel #endif
11503831d35Sstevel static fn_t f = "sbdp_attach_branch";
11603831d35Sstevel
11703831d35Sstevel SBDP_DBG_FUNC("%s\n", f);
11803831d35Sstevel
11903831d35Sstevel if (node == OBP_NONODE)
12003831d35Sstevel return;
12103831d35Sstevel
12203831d35Sstevel /*
12303831d35Sstevel * Get the status for this node
12403831d35Sstevel * If it has failed we imitate boot by not creating a node
12503831d35Sstevel * in solaris. We just warn the user
12603831d35Sstevel */
12703831d35Sstevel if (check_status(node, buf, pdip) != DDI_SUCCESS) {
12803831d35Sstevel SBDP_DBG_STATE("status failed skipping this node\n");
12903831d35Sstevel return;
13003831d35Sstevel }
13103831d35Sstevel
13203831d35Sstevel len = prom_getproplen(node, OBP_REG);
13303831d35Sstevel if (len <= 0) {
13403831d35Sstevel return;
13503831d35Sstevel }
13603831d35Sstevel
13703831d35Sstevel (void) prom_getprop(node, OBP_NAME, (caddr_t)name);
13803831d35Sstevel err = ndi_devi_alloc(pdip, name, node, &dip);
13903831d35Sstevel if (err != NDI_SUCCESS) {
14003831d35Sstevel return;
14103831d35Sstevel }
14203831d35Sstevel SBDP_DBG_STATE("attaching %s\n", name);
14303831d35Sstevel err = ndi_devi_online(dip, NDI_DEVI_BIND);
14403831d35Sstevel if (err != NDI_SUCCESS) {
145*07d06da5SSurya Prakki (void) ndi_devi_free(dip);
14603831d35Sstevel return;
14703831d35Sstevel }
14803831d35Sstevel child = prom_childnode(node);
14903831d35Sstevel if (child != OBP_NONODE) {
15003831d35Sstevel for (; child != OBP_NONODE;
15103831d35Sstevel child = prom_nextnode(child)) {
15203831d35Sstevel sbdp_attach_branch(dip, child, (void *)apktp);
15303831d35Sstevel }
15403831d35Sstevel }
15503831d35Sstevel #undef buf
15603831d35Sstevel }
15703831d35Sstevel
15803831d35Sstevel static int
sbdp_find_ssm_dip(dev_info_t * dip,void * arg)15903831d35Sstevel sbdp_find_ssm_dip(dev_info_t *dip, void *arg)
16003831d35Sstevel {
16103831d35Sstevel attach_pkt_t *apktp;
16203831d35Sstevel int node;
16303831d35Sstevel static fn_t f = "sbdp_find_ssm_dip";
16403831d35Sstevel
16503831d35Sstevel SBDP_DBG_FUNC("%s\n", f);
16603831d35Sstevel
16703831d35Sstevel apktp = (attach_pkt_t *)arg;
16803831d35Sstevel
16903831d35Sstevel if (apktp == NULL) {
17003831d35Sstevel SBDP_DBG_STATE("error on the argument\n");
17103831d35Sstevel return (DDI_WALK_CONTINUE);
17203831d35Sstevel }
17303831d35Sstevel
17403831d35Sstevel if ((node = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
17503831d35Sstevel "nodeid", -1)) == -1)
17603831d35Sstevel return (DDI_WALK_CONTINUE);
17703831d35Sstevel
17803831d35Sstevel if (node == apktp->node) {
17903831d35Sstevel ndi_hold_devi(dip);
18003831d35Sstevel apktp->top_node = dip;
18103831d35Sstevel return (DDI_WALK_TERMINATE);
18203831d35Sstevel }
18303831d35Sstevel return (DDI_WALK_CONTINUE);
18403831d35Sstevel }
18503831d35Sstevel
18603831d35Sstevel /*ARGSUSED*/
18703831d35Sstevel int
sbdp_select_top_nodes(pnode_t node,void * arg,uint_t flags)18803831d35Sstevel sbdp_select_top_nodes(pnode_t node, void *arg, uint_t flags)
18903831d35Sstevel {
19003831d35Sstevel int board, bd;
19103831d35Sstevel attach_pkt_t *apktp = (attach_pkt_t *)arg;
19203831d35Sstevel char devtype[OBP_MAXDRVNAME];
19303831d35Sstevel char devname[OBP_MAXDRVNAME];
19403831d35Sstevel int i;
19503831d35Sstevel sbd_devattr_t *sbdp_top_nodes;
19603831d35Sstevel int wnode;
19703831d35Sstevel static fn_t f = "sbdp_select_top_nodes";
19803831d35Sstevel
19903831d35Sstevel SBDP_DBG_FUNC("%s\n", f);
20003831d35Sstevel
20103831d35Sstevel if (apktp == NULL) {
20203831d35Sstevel SBDP_DBG_STATE("error on the argument\n");
20303831d35Sstevel return (DDI_FAILURE);
20403831d35Sstevel }
20503831d35Sstevel
20603831d35Sstevel board = apktp->board;
20703831d35Sstevel sbdp_top_nodes = sbdp_get_devattr();
20803831d35Sstevel
20903831d35Sstevel if (sbdp_get_bd_and_wnode_num(node, &bd, &wnode) < 0)
21003831d35Sstevel return (DDI_FAILURE);
21103831d35Sstevel
21203831d35Sstevel if (bd != board)
21303831d35Sstevel return (DDI_FAILURE);
21403831d35Sstevel
21503831d35Sstevel SBDP_DBG_MISC("%s: board is %d\n", f, bd);
21603831d35Sstevel
21703831d35Sstevel (void) prom_getprop(node, OBP_DEVICETYPE, (caddr_t)devtype);
21803831d35Sstevel (void) prom_getprop(node, OBP_NAME, (caddr_t)devname);
21903831d35Sstevel
22003831d35Sstevel if (strcmp(devname, "cmp") == 0) {
22103831d35Sstevel apktp->nodes[apktp->num_of_nodes] = node;
22203831d35Sstevel apktp->num_of_nodes++;
22303831d35Sstevel
22403831d35Sstevel /* We want this node */
22503831d35Sstevel return (DDI_SUCCESS);
22603831d35Sstevel }
22703831d35Sstevel
22803831d35Sstevel for (i = 0; sbdp_top_nodes[i].s_obp_type != NULL; i++) {
22903831d35Sstevel if (strcmp(devtype, sbdp_top_nodes[i].s_obp_type) == 0) {
23003831d35Sstevel if (strcmp(devtype, "cpu") == 0) {
23103831d35Sstevel int cpuid;
23203831d35Sstevel int impl;
23303831d35Sstevel
23403831d35Sstevel /*
23503831d35Sstevel * Check the status of the cpu
23603831d35Sstevel * If it is failed ignore it
23703831d35Sstevel */
23803831d35Sstevel if (sbdp_get_comp_status(node) != SBD_COND_OK)
23903831d35Sstevel return (DDI_FAILURE);
24003831d35Sstevel
24103831d35Sstevel if (prom_getprop(node, "cpuid",
24203831d35Sstevel (caddr_t)&cpuid) == -1) {
24303831d35Sstevel
24403831d35Sstevel if (prom_getprop(node, "portid",
24503831d35Sstevel (caddr_t)&cpuid) == -1) {
24603831d35Sstevel
24703831d35Sstevel return (DDI_WALK_TERMINATE);
24803831d35Sstevel }
24903831d35Sstevel }
25003831d35Sstevel
25103831d35Sstevel if (sbdp_set_cpu_present(wnode, bd,
25203831d35Sstevel SG_CPUID_TO_CPU_UNIT(cpuid)) == -1)
25303831d35Sstevel return (DDI_WALK_TERMINATE);
25403831d35Sstevel
25503831d35Sstevel (void) prom_getprop(node, "implementation#",
256*07d06da5SSurya Prakki (caddr_t)&impl);
25703831d35Sstevel /*
25803831d35Sstevel * If it is a CPU under CMP, don't save
25903831d35Sstevel * the node as we will be saving the CMP
26003831d35Sstevel * node.
26103831d35Sstevel */
26203831d35Sstevel if (CPU_IMPL_IS_CMP(impl))
26303831d35Sstevel return (DDI_FAILURE);
26403831d35Sstevel }
26503831d35Sstevel
26603831d35Sstevel /*
26703831d35Sstevel * Check to make sure we haven't run out of bounds
26803831d35Sstevel */
26903831d35Sstevel if (apktp->num_of_nodes >= SBDP_MAX_NODES)
27003831d35Sstevel return (DDI_FAILURE);
27103831d35Sstevel
27203831d35Sstevel /* Save node */
27303831d35Sstevel apktp->nodes[apktp->num_of_nodes] = node;
27403831d35Sstevel apktp->num_of_nodes++;
27503831d35Sstevel
27603831d35Sstevel /* We want this node */
27703831d35Sstevel return (DDI_SUCCESS);
27803831d35Sstevel }
27903831d35Sstevel }
28003831d35Sstevel
28103831d35Sstevel return (DDI_FAILURE);
28203831d35Sstevel }
28303831d35Sstevel
28403831d35Sstevel void
sbdp_attach_bd(int node,int board)28503831d35Sstevel sbdp_attach_bd(int node, int board)
28603831d35Sstevel {
28703831d35Sstevel devi_branch_t b = {0};
28803831d35Sstevel attach_pkt_t apkt, *apktp = &apkt;
28903831d35Sstevel static fn_t f = "sbdp_attach_bd";
29003831d35Sstevel
29103831d35Sstevel SBDP_DBG_FUNC("%s\n", f);
29203831d35Sstevel
29303831d35Sstevel apktp->node = node;
29403831d35Sstevel apktp->board = board;
29503831d35Sstevel apktp->num_of_nodes = 0;
29603831d35Sstevel apktp->flags = 0;
29703831d35Sstevel
29803831d35Sstevel apktp->top_node = NULL;
29903831d35Sstevel
30003831d35Sstevel /*
30103831d35Sstevel * Root node doesn't have to be held for ddi_walk_devs()
30203831d35Sstevel */
30303831d35Sstevel ddi_walk_devs(ddi_root_node(), sbdp_find_ssm_dip, (void *) apktp);
30403831d35Sstevel
30503831d35Sstevel if (apktp->top_node == NULL) {
30603831d35Sstevel SBDP_DBG_STATE("BAD Serengeti\n");
30703831d35Sstevel return;
30803831d35Sstevel }
30903831d35Sstevel
31003831d35Sstevel b.arg = (void *)apktp;
31103831d35Sstevel b.type = DEVI_BRANCH_PROM;
31203831d35Sstevel b.create.prom_branch_select = sbdp_select_top_nodes;
31303831d35Sstevel b.devi_branch_callback = NULL;
31403831d35Sstevel
31503831d35Sstevel (void) e_ddi_branch_create(apktp->top_node, &b, NULL, 0);
31603831d35Sstevel
31703831d35Sstevel /*
31803831d35Sstevel * Release hold acquired in sbdp_find_ssm_dip()
31903831d35Sstevel */
32003831d35Sstevel ndi_rele_devi(apktp->top_node);
32103831d35Sstevel
32203831d35Sstevel sbdp_cpu_in_reset(node, board, SBDP_ALL_CPUS, 1);
32303831d35Sstevel }
32403831d35Sstevel
32503831d35Sstevel int
sbdp_detach_bd(int node,int board,sbd_error_t * sep)32603831d35Sstevel sbdp_detach_bd(int node, int board, sbd_error_t *sep)
32703831d35Sstevel {
32803831d35Sstevel int rv;
32903831d35Sstevel attach_pkt_t apkt, *apktp = &apkt;
33003831d35Sstevel static fn_t f = "sbdp_detach_bd";
33103831d35Sstevel
33203831d35Sstevel SBDP_DBG_FUNC("%s\n", f);
33303831d35Sstevel
33403831d35Sstevel apktp->node = node;
33503831d35Sstevel apktp->board = board;
33603831d35Sstevel apktp->num_of_nodes = 0;
33703831d35Sstevel apktp->error = 0;
33803831d35Sstevel apktp->errstr = NULL;
33903831d35Sstevel sbdp_walk_prom_tree(prom_rootnode(), sbdp_select_top_nodes,
34003831d35Sstevel (void *) apktp);
34103831d35Sstevel
34203831d35Sstevel if (rv = sbdp_detach_nodes(apktp)) {
34303831d35Sstevel sbdp_set_err(sep, ESBD_IO, NULL);
34403831d35Sstevel return (rv);
34503831d35Sstevel }
34603831d35Sstevel
34703831d35Sstevel sbdp_cpu_in_reset(node, board, SBDP_ALL_CPUS, 1);
34803831d35Sstevel /*
34903831d35Sstevel * Clean up this board struct
35003831d35Sstevel */
35103831d35Sstevel sbdp_cleanup_bd(node, board);
35203831d35Sstevel
35303831d35Sstevel return (0);
35403831d35Sstevel }
35503831d35Sstevel
35603831d35Sstevel static int
sbdp_detach_nodes(attach_pkt_t * apktp)35703831d35Sstevel sbdp_detach_nodes(attach_pkt_t *apktp)
35803831d35Sstevel {
35903831d35Sstevel dev_info_t **dip;
36003831d35Sstevel dev_info_t **dev_list;
36103831d35Sstevel int dev_list_len = 0;
36203831d35Sstevel int i, rv = 0;
36303831d35Sstevel
36403831d35Sstevel dev_list = kmem_zalloc(sizeof (dev_info_t *) * SBDP_MAX_NODES,
36503831d35Sstevel KM_SLEEP);
36603831d35Sstevel
36703831d35Sstevel for (i = 0, dip = dev_list; i < apktp->num_of_nodes; i++) {
36803831d35Sstevel *dip = e_ddi_nodeid_to_dip(apktp->nodes[i]);
36903831d35Sstevel if (*dip != NULL) {
37003831d35Sstevel /*
37103831d35Sstevel * The branch rooted at dip should already be held,
37203831d35Sstevel * so release hold acquired in e_ddi_nodeid_to_dip()
37303831d35Sstevel */
37403831d35Sstevel ddi_release_devi(*dip);
37503831d35Sstevel dip++;
37603831d35Sstevel ++dev_list_len;
37703831d35Sstevel }
37803831d35Sstevel }
37903831d35Sstevel
38003831d35Sstevel for (i = dev_list_len, dip = &dev_list[i - 1]; i > 0; i--, dip--) {
38103831d35Sstevel dev_info_t *fdip = NULL;
38203831d35Sstevel
38303831d35Sstevel ASSERT(e_ddi_branch_held(*dip));
38403831d35Sstevel rv = e_ddi_branch_destroy(*dip, &fdip, 0);
38503831d35Sstevel if (rv) {
38603831d35Sstevel char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
38703831d35Sstevel
38803831d35Sstevel /*
38903831d35Sstevel * If non-NULL, fdip is held and must be released.
39003831d35Sstevel */
39103831d35Sstevel if (fdip != NULL) {
39203831d35Sstevel (void) ddi_pathname(fdip, path);
39303831d35Sstevel ddi_release_devi(fdip);
39403831d35Sstevel } else {
39503831d35Sstevel (void) ddi_pathname(*dip, path);
39603831d35Sstevel }
39703831d35Sstevel
39803831d35Sstevel cmn_err(CE_WARN, "failed to remove node %s (%p): %d",
39903831d35Sstevel path, fdip ? (void *)fdip : (void *)*dip, rv);
40003831d35Sstevel
40103831d35Sstevel kmem_free(path, MAXPATHLEN);
40203831d35Sstevel
40303831d35Sstevel apktp->error = apktp->error ? apktp->error : rv;
40403831d35Sstevel break;
40503831d35Sstevel }
40603831d35Sstevel }
40703831d35Sstevel
40803831d35Sstevel kmem_free(dev_list, sizeof (dev_info_t *) * SBDP_MAX_NODES);
40903831d35Sstevel
41003831d35Sstevel return (rv);
41103831d35Sstevel }
412