103831d3stevel/*
203831d3stevel * CDDL HEADER START
303831d3stevel *
403831d3stevel * The contents of this file are subject to the terms of the
503831d3stevel * Common Development and Distribution License, Version 1.0 only
603831d3stevel * (the "License").  You may not use this file except in compliance
703831d3stevel * with the License.
803831d3stevel *
903831d3stevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1003831d3stevel * or http://www.opensolaris.org/os/licensing.
1103831d3stevel * See the License for the specific language governing permissions
1203831d3stevel * and limitations under the License.
1303831d3stevel *
1403831d3stevel * When distributing Covered Code, include this CDDL HEADER in each
1503831d3stevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1603831d3stevel * If applicable, add the following below this CDDL HEADER, with the
1703831d3stevel * fields enclosed by brackets "[]" replaced with your own identifying
1803831d3stevel * information: Portions Copyright [yyyy] [name of copyright owner]
1903831d3stevel *
2003831d3stevel * CDDL HEADER END
2103831d3stevel */
2203831d3stevel/*
2303831d3stevel * Copyright 1999-2003 Sun Microsystems, Inc.  All rights reserved.
2403831d3stevel * Use is subject to license terms.
2503831d3stevel */
2603831d3stevel
2703831d3stevel#pragma ident	"%Z%%M%	%I%	%E% SMI"
2803831d3stevel
2903831d3stevel#include <stdio.h>
3003831d3stevel#include <stdlib.h>
3103831d3stevel#include <string.h>
3203831d3stevel#include <fcntl.h>
3303831d3stevel#include <dirent.h>
3403831d3stevel#include <varargs.h>
3503831d3stevel#include <errno.h>
3603831d3stevel#include <unistd.h>
3703831d3stevel#include <sys/systeminfo.h>
3803831d3stevel#include <sys/utsname.h>
3903831d3stevel#include <sys/openpromio.h>
4003831d3stevel#include <kstat.h>
4103831d3stevel#include <libintl.h>
4203831d3stevel#include "pdevinfo.h"
4303831d3stevel#include "pdevinfo_sun4u.h"
4403831d3stevel#include "display.h"
4503831d3stevel#include "display_sun4u.h"
4603831d3stevel#include "libprtdiag.h"
4703831d3stevel
4803831d3stevel#if !defined(TEXT_DOMAIN)
4903831d3stevel#define	TEXT_DOMAIN	"SYS_TEST"
5003831d3stevel#endif
5103831d3stevel
5203831d3stevel/*
5303831d3stevel * Global variables
5403831d3stevel */
5503831d3stevelchar	*progname;
5603831d3stevelchar	*promdev = "/dev/openprom";
5703831d3stevelint	print_flag = 1;
5803831d3stevelint	logging = 0;
5903831d3stevel
6003831d3stevel/*
6103831d3stevel * This file represents the splitting out of some functionality
6203831d3stevel * of prtdiag due to the port to the sun4u platform. The PROM
6303831d3stevel * tree-walking functions which contain sun4u specifics were moved
6403831d3stevel * into this module.
6503831d3stevel */
6603831d3stevel
6703831d3stevelextern int get_id(Prom_node *);
6803831d3stevel
6903831d3stevel/* Function prototypes */
7003831d3stevelProm_node	*walk(Sys_tree *, Prom_node *, int);
7103831d3stevel
7203831d3stevel/*
7303831d3stevel * do_prominfo() is called from main() in usr/src/cmd/prtdiag/main.c
7403831d3stevel *
7503831d3stevel * This is the starting point for all platforms. However, this function
7603831d3stevel * can be overlayed by writing a do_prominfo() function
7703831d3stevel * in the libprtdiag_psr for a particular platform.
7803831d3stevel *
7903831d3stevel */
8003831d3stevelint
8103831d3steveldo_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag)
8203831d3stevel{
8303831d3stevel	Sys_tree sys_tree;		/* system information */
8403831d3stevel	Prom_node *root_node;		/* root node of OBP device tree */
8503831d3stevel	struct system_kstat_data sys_kstat; /* kstats for non-OBP data */
8603831d3stevel
8703831d3stevel
8803831d3stevel	/* set the global flags */
8903831d3stevel	progname = pgname;
9003831d3stevel	logging = log_flag;
9103831d3stevel	print_flag = prt_flag;
9203831d3stevel
9303831d3stevel	/* set the the system tree fields */
9403831d3stevel	sys_tree.sys_mem = NULL;
9503831d3stevel	sys_tree.boards = NULL;
9603831d3stevel	sys_tree.bd_list = NULL;
9703831d3stevel	sys_tree.board_cnt = 0;
9803831d3stevel
9903831d3stevel	if (promopen(O_RDONLY))  {
10003831d3stevel		exit(_error(dgettext(TEXT_DOMAIN, "openeepr device "
10103831d3stevel			"open failed")));
10203831d3stevel	}
10303831d3stevel
10403831d3stevel	if (is_openprom() == 0)  {
10503831d3stevel		(void) fprintf(stderr, "%s",
10603831d3stevel			dgettext(TEXT_DOMAIN, "System architecture "
10703831d3stevel			    "does not support this option of this "
10803831d3stevel			    "command.\n"));
10903831d3stevel		return (2);
11003831d3stevel	}
11103831d3stevel
11203831d3stevel	if (next(0) == 0) {
11303831d3stevel		return (2);
11403831d3stevel	}
11503831d3stevel
11603831d3stevel	root_node = walk(&sys_tree, NULL, next(0));
11703831d3stevel	promclose();
11803831d3stevel
11903831d3stevel	/* resolve the board types now */
12003831d3stevel	resolve_board_types(&sys_tree);
12103831d3stevel
12203831d3stevel	read_sun4u_kstats(&sys_tree, &sys_kstat);
12303831d3stevel
12403831d3stevel	return (display(&sys_tree, root_node, &sys_kstat, syserrlog));
12503831d3stevel
12603831d3stevel}
12703831d3stevel
12803831d3stevelint
12903831d3stevelget_id(Prom_node *node)
13003831d3stevel{
13103831d3stevel	int *value;
13203831d3stevel
13303831d3stevel	/*
13403831d3stevel	 * check for upa-portid on UI and UII systems
13503831d3stevel	 */
13603831d3stevel	if ((value = (int *)get_prop_val(find_prop(node, "upa-portid")))
13703831d3stevel	    == NULL) {
13803831d3stevel		/*
13903831d3stevel		 * check for portid on UIII systems
14003831d3stevel		 */
14103831d3stevel		if ((value = (int *)get_prop_val(find_prop(node, "portid")))
14203831d3stevel		    == NULL) {
14303831d3stevel			return (-1);
14403831d3stevel		}
14503831d3stevel	}
14603831d3stevel	return (*value);
14703831d3stevel}
14803831d3stevel
14903831d3stevel
15003831d3stevel
15103831d3stevel/*
15203831d3stevel * Walk the PROM device tree and build the system tree and root tree.
15303831d3stevel * Nodes that have a board number property are placed in the board
15403831d3stevel * structures for easier processing later. Child nodes are placed
15503831d3stevel * under their parents. ffb (Fusion Frame Buffer) nodes are handled
15603831d3stevel * specially, because they do not contain board number properties.
15703831d3stevel * This was requested from OBP, but was not granted. So this code
15803831d3stevel * must parse the MID of the FFB to find the board#.
15903831d3stevel *
16003831d3stevel */
16103831d3stevelProm_node *
16203831d3stevelwalk(Sys_tree *tree, Prom_node *root, int id)
16303831d3stevel{
16403831d3stevel	register int curnode;
16503831d3stevel	Prom_node *pnode;
16603831d3stevel	char *name;
16703831d3stevel	char *type;
16803831d3stevel	char *model;
16903831d3stevel	int board_node = 0;
17003831d3stevel
17103831d3stevel	/* allocate a node for this level */
17203831d3stevel	if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) ==
17303831d3stevel	    NULL) {
17403831d3stevel		perror("malloc");
17503831d3stevel		exit(2);	/* program errors cause exit 2 */
17603831d3stevel	}
17703831d3stevel
17803831d3stevel	/* assign parent Prom_node */
17903831d3stevel	pnode->parent = root;
18003831d3stevel	pnode->sibling = NULL;
18103831d3stevel	pnode->child = NULL;
18203831d3stevel
18303831d3stevel	/* read properties for this node */
18403831d3stevel	dump_node(pnode);
18503831d3stevel
18603831d3stevel	/*
18703831d3stevel	 * Place a node in a 'board' if it has 'board'-ness. The definition
18803831d3stevel	 * is that all nodes that are children of root should have a
18903831d3stevel	 * board# property. But the PROM tree does not exactly follow
19003831d3stevel	 * this. This is where we start hacking. The name 'ffb' can
19103831d3stevel	 * change, so watch out for this.
19203831d3stevel	 *
19303831d3stevel	 * The UltraSPARC, sbus, pci and ffb nodes will exit in
19403831d3stevel	 * the desktops and will not have board# properties. These
19503831d3stevel	 * cases must be handled here.
19603831d3stevel	 *
19703831d3stevel	 * PCI to PCI bridges also have the name "pci", but with different
19803831d3stevel	 * model property values.  They should not be put under 'board'.
19903831d3stevel	 */
20003831d3stevel	name = get_node_name(pnode);
20103831d3stevel	type = get_node_type(pnode);
20203831d3stevel	model = (char *)get_prop_val(find_prop(pnode, "model"));
20303831d3stevel#ifdef DEBUG
20403831d3stevel	if (name != NULL)
20503831d3stevel		printf("name=%s ", name);
20603831d3stevel	if (type != NULL)
20703831d3stevel		printf("type=%s ", type);
20803831d3stevel	if (model != NULL)
20903831d3stevel		printf("model=%s", model);
21003831d3stevel	printf("\n");
21103831d3stevel#endif
21203831d3stevel	if (model == NULL)
21303831d3stevel		model = "";
21403831d3stevel	if (type == NULL)
21503831d3stevel		type = "";
21603831d3stevel	if (name != NULL) {
21703831d3stevel		if (has_board_num(pnode)) {
21803831d3stevel			add_node(tree, pnode);
21903831d3stevel			board_node = 1;
22003831d3stevel#ifdef DEBUG
22103831d3stevel			printf("ADDED BOARD name=%s type=%s model=%s\n",
22203831d3stevel				name, type, model);
22303831d3stevel#endif
22403831d3stevel		} else if ((strcmp(name, FFB_NAME)  == 0)	||
22503831d3stevel		    (strcmp(name, AFB_NAME) == 0)		||
22603831d3stevel		    (strcmp(type, "cpu") == 0)			||
22703831d3stevel
22803831d3stevel		    ((strcmp(type, "memory-controller") == 0) &&
22903831d3stevel			(strcmp(name, "ac") != 0))			||
23003831d3stevel
23103831d3stevel		    ((strcmp(name, "pci") == 0) &&
23203831d3stevel			(strcmp(model, "SUNW,psycho") == 0))		||
23303831d3stevel
23403831d3stevel		    ((strcmp(name, "pci") == 0) &&
23503831d3stevel			(strcmp(model, "SUNW,sabre") == 0))		||
23603831d3stevel
23703831d3stevel		    ((strcmp(name, "pci") == 0) &&
23803831d3stevel			(strcmp(model, "SUNW,schizo") == 0))		||
23903831d3stevel
24003831d3stevel		    ((strcmp(name, "pci") == 0) &&
24103831d3stevel			(strcmp(model, "SUNW,xmits") == 0))		||
24203831d3stevel
24303831d3stevel		    (strcmp(name, "counter-timer") == 0)		||
24403831d3stevel		    (strcmp(name, "sbus") == 0)) {
24503831d3stevel			add_node(tree, pnode);
24603831d3stevel			board_node = 1;
24703831d3stevel#ifdef DEBUG
24803831d3stevel			printf("ADDED BOARD name=%s type=%s model=%s\n",
24903831d3stevel				name, type, model);
25003831d3stevel#endif
25103831d3stevel		}
25203831d3stevel#ifdef DEBUG
25303831d3stevel		else
25403831d3stevel			printf("node not added: name=%s type=%s\n", name, type);
25503831d3stevel#endif
25603831d3stevel	}
25703831d3stevel
25803831d3stevel	if (curnode = child(id)) {
25903831d3stevel		pnode->child = walk(tree, pnode, curnode);
26003831d3stevel	}
26103831d3stevel
26203831d3stevel	if (curnode = next(id)) {
26303831d3stevel		if (board_node) {
26403831d3stevel			return (walk(tree, root, curnode));
26503831d3stevel		} else {
26603831d3stevel			pnode->sibling = walk(tree, root, curnode);
26703831d3stevel		}
26803831d3stevel	}
26903831d3stevel
27003831d3stevel	if (board_node) {
27103831d3stevel		return (NULL);
27203831d3stevel	} else {
27303831d3stevel		return (pnode);
27403831d3stevel	}
27503831d3stevel}
276