diff options
| author | stevel <none@none> | 2006-03-29 15:44:11 -0800 |
|---|---|---|
| committer | stevel <none@none> | 2006-03-29 15:44:11 -0800 |
| commit | 03831d35f7499c87d51205817c93e9a8d42c4bae (patch) | |
| tree | bffe92ec7d68bd67b0cd216c9186bf7a01118b8a /usr/src/lib | |
| parent | 1b01eaa0e4d0703606fba8c845845b4a7f0c0583 (diff) | |
| download | illumos-joyent-03831d35f7499c87d51205817c93e9a8d42c4bae.tar.gz | |
6392837 move sram to usr/src
6393316 move fcgp2 to usr/src
6393416 move gptwo_pci to usr/src
6393420 move gptwo_wci to usr/src
6393421 move sc_gptwocfg to usr/src
6393424 move axq to usr/src
6393425 move dman to usr/src
6393426 move iosram to usr/src
6393427 move mboxsc to usr/src
6393428 move sbdp to usr/src
6393429 move schpc to usr/src
6393432 move sckmdrv to usr/src
6393435 move scosmb to usr/src
6393437 move driver sgcn to usr/src
6393438 move sgenv to usr/src
6393439 move sgfru to usr/src
6393440 move driver sghsc to usr/src
6393441 move driver sgsbbc to usr/src
6393442 move driver ssm to usr/src
6393452 move pcf8574_nct to usr/src
6393453 move pcf8591_nct to usr/src
6393454 move acebus to usr/src
6393455 move scsb to usr/src
6393462 move lw8 to usr/src
6393463 move driver rmc_comm to usr/src
6393464 move driver rmcadm to usr/src
6393475 move todsg to usr/src
6393889 move libprtdiag to usr/src
6393890 move libprtdiag_psr to usr/src
6393896 move librsc to usr/src
6393898 move scadm to usr/src
6399766 move serengeti platform to usr/src
6399770 move starcat platform to usr/src
6400949 daktari platmod Makefile references incorrect platmod directory
Diffstat (limited to 'usr/src/lib')
79 files changed, 40729 insertions, 11 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index cfcba443d5..d17ca46109 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -218,12 +218,10 @@ SUBDIRS += \ sparc_SUBDIRS= .WAIT \ efcode \ - libc_psr .WAIT -$(CLOSED_BUILD)sparc_SUBDIRS += \ - $(CLOSED)/lib/libprtdiag .WAIT \ - $(CLOSED)/lib/libprtdiag_psr \ - $(CLOSED)/lib/librsc -sparc_SUBDIRS += \ + libc_psr .WAIT \ + libprtdiag .WAIT \ + libprtdiag_psr \ + librsc \ libfruutils .WAIT \ libfru \ libwrsmconf \ @@ -285,11 +283,9 @@ $(CLOSED_BUILD)MSGSUBDIRS += \ MSGSUBDIRS += \ $($(MACH)_MSGSUBDIRS) -sparc_MSGSUBDIRS= - -$(CLOSED_BUILD)sparc_MSGSUBDIRS += \ - $(CLOSED)/lib/libprtdiag \ - $(CLOSED)/lib/libprtdiag_psr +sparc_MSGSUBDIRS= \ + libprtdiag \ + libprtdiag_psr HDRSUBDIRS= libaio \ auditd_plugins \ diff --git a/usr/src/lib/libprtdiag/Makefile b/usr/src/lib/libprtdiag/Makefile new file mode 100644 index 0000000000..6a1f2a7e69 --- /dev/null +++ b/usr/src/lib/libprtdiag/Makefile @@ -0,0 +1,49 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag/Makefile +# + +include $(SRC)/lib/Makefile.lib + +SUBDIRS= $(MACH) + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber +lint := TARGET= lint +_msg := TARGET= _msg + +.KEEP_STATE: + +all install clean clobber lint _msg : $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/libprtdiag/Makefile.com b/usr/src/lib/libprtdiag/Makefile.com new file mode 100644 index 0000000000..0997c2f910 --- /dev/null +++ b/usr/src/lib/libprtdiag/Makefile.com @@ -0,0 +1,71 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# Create default so empty rules don't +# confuse make +# +CLASS = 32 +UTSBASE = $(SRC)/uts + +LIBRARY = libprtdiag.a +VERS = .1 + +include $(SRC)/lib/Makefile.lib +include $(SRC)/Makefile.psm + +LIBS = $(DYNLIB) $(LINTLIB) +IFLAGS = -I ../../inc -I $(USR_PSM_INCL_DIR) +IFLAGS += -I $(UTSBASE)/sun4u +IFLAGS += -I $(UTSBASE)/sun4u/sunfire +IFLAGS += -I $(UTSBASE)/sun4u/serengeti +CPPFLAGS = $(IFLAGS) $(CPPFLAGS.master) +CFLAGS += $(CCVERBOSE) +LDLIBS += -lc -lkstat +DYNFLAGS += -Wl,-f/usr/platform/\$$PLATFORM/lib/$(DYNLIBPSR) + +SRCDIR = ../../common +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) +USR_PSMLINT= $(LINTSRC:%=$(USR_PSM_LIB_DIR)/%) + +# +# install rule +# +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) ;\ + $(RM) -r $(USR_PSM_LIB_DIR)/libprtdiag.so ;\ + $(SYMLINK) ./libprtdiag.so$(VERS) $(USR_PSM_LIB_DIR)/libprtdiag.so + +$(USR_PSM_LIB_DIR)/%: $(SRCDIR)/% + $(INS.file) + +# +# build rules +# +objs/%.o pics/%.o: ../../common/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) diff --git a/usr/src/lib/libprtdiag/common/cpu.c b/usr/src/lib/libprtdiag/common/cpu.c new file mode 100644 index 0000000000..4248f3415c --- /dev/null +++ b/usr/src/lib/libprtdiag/common/cpu.c @@ -0,0 +1,246 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <sys/spitregs.h> +#include <sys/cheetahregs.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +/* + * Return the operating frequency of a processor in Hertz. This function + * requires as input a legal prom node pointer. If a NULL + * is passed in or the clock-frequency property does not exist, the + * function returns 0. + */ +int +get_cpu_freq(Prom_node *pnode) +{ + Prop *prop; + int *value; + + /* find the property */ + if ((prop = find_prop(pnode, "clock-frequency")) == NULL) { + return (0); + } + + if ((value = (int *)get_prop_val(prop)) == NULL) { + return (0); + } + + return (*value); +} + +/* + * returns the size of the given processors external cache in + * bytes. If the properties required to determine this are not + * present, then the function returns 0. + */ +int +get_ecache_size(Prom_node *node) +{ + int *cache_size_p; /* pointer to number of cache lines */ + + /* find the properties */ + if (cache_size_p = (int *)get_prop_val(find_prop(node, + "ecache-size"))) { + return (*cache_size_p); + } + if (cache_size_p = (int *)get_prop_val(find_prop(node, + "l3-cache-size"))) { + return (*cache_size_p); + } + if (cache_size_p = (int *)get_prop_val(find_prop(node, + "l2-cache-size"))) { + return (*cache_size_p); + } + + return (0); +} + + +/* + * This routine is the generic link into displaying CPU and memory info. + * It displays the table header, then calls the CPU and memory display + * routine for all boards. + */ +void +display_cpu_devices(Sys_tree *tree) +{ + Board_node *bnode; + + /* + * Display the table header for CPUs . Then display the CPU + * frequency, cache size, and processor revision of all cpus. + */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(" CPUs ", 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + log_printf(" Run Ecache " + " CPU CPU\n", 0); + log_printf("Brd CPU Module MHz MB " + "Impl. Mask\n", 0); + log_printf("--- --- ------- ----- ------ " + "------ ----\n", 0); + + /* Now display all of the cpus on each board */ + bnode = tree->bd_list; + while (bnode != NULL) { + display_cpus(bnode); + bnode = bnode->next; + } + + log_printf("\n", 0); +} + +/* + * Display the CPUs present on this board. + */ +void +display_cpus(Board_node *board) +{ + Prom_node *cpu; + + /* + * display the CPUs' operating frequency, cache size, impl. field + * and mask revision. + */ + for (cpu = dev_find_type(board->nodes, "cpu"); cpu != NULL; + cpu = dev_next_type(cpu, "cpu")) { + int freq; /* CPU clock frequency */ + int ecache_size; /* External cache size */ + int *mid; + int *impl; + int *mask, decoded_mask; + + mid = (int *)get_prop_val(find_prop(cpu, "upa-portid")); + if (mid == NULL) { + mid = (int *)get_prop_val(find_prop(cpu, "portid")); + } + + freq = (get_cpu_freq(cpu) + 500000) / 1000000; + ecache_size = get_ecache_size(cpu); + impl = (int *)get_prop_val(find_prop(cpu, "implementation#")); + mask = (int *)get_prop_val(find_prop(cpu, "mask#")); + + /* Do not display a failed CPU node */ + if ((freq != 0) && (node_failed(cpu) == 0)) { + /* Board number */ + display_boardnum(board->board_num); + + /* CPU MID */ + log_printf(" %2d ", *mid, 0); + + /* Module number */ + display_mid(*mid); + + /* Running frequency */ + log_printf(" %3d ", freq, 0); + + /* Ecache size */ + if (ecache_size == 0) + log_printf(" %3s ", "N/A", 0); + else + log_printf(" %4.1f ", + (float)ecache_size / (float)(1<<20), + 0); + + /* Implementation */ + if (impl == NULL) { + log_printf("%6s ", "N/A", 0); + } else { + switch (*impl) { + case SPITFIRE_IMPL: + log_printf("%-6s ", "US-I", 0); + break; + case BLACKBIRD_IMPL: + log_printf("%-6s ", "US-II", 0); + break; + case CHEETAH_IMPL: + log_printf("%-6s ", "US-III", 0); + break; + case CHEETAH_PLUS_IMPL: + log_printf("%-7s ", "US-III+", 0); + break; + case JAGUAR_IMPL: + log_printf("%-6s ", "US-IV", 0); + break; + default: + log_printf("%-6x ", *impl, 0); + break; + } + } + + /* CPU Mask */ + if (mask == NULL) { + log_printf(" %3s", "N/A", 0); + } else { + if ((impl) && IS_CHEETAH(*impl)) + decoded_mask = + REMAP_CHEETAH_MASK(*mask); + else + decoded_mask = *mask; + + log_printf(" %d.%d", (decoded_mask >> 4) & 0xf, + decoded_mask & 0xf, 0); + } + + log_printf("\n", 0); + } + } +} + +void +display_mid(int mid) +{ + log_printf(" %2d ", mid, 0); +} diff --git a/usr/src/lib/libprtdiag/common/display_funcs.c b/usr/src/lib/libprtdiag/common/display_funcs.c new file mode 100644 index 0000000000..80a9b8af1e --- /dev/null +++ b/usr/src/lib/libprtdiag/common/display_funcs.c @@ -0,0 +1,184 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <stdarg.h> +#include <syslog.h> +#include <sys/openpromio.h> +#include <libintl.h> +#include "pdevinfo.h" +#include "display.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +/* + * external data + */ +extern int print_flag; +extern int logging; + +/* + * The following macros for dealing with raw output from the Mostek 48T02 + * were borrowed from the kernel. Openboot passes the raw Mostek data + * thru the device tree, and there are no library routines to deal with + * this data. + */ + +/* + * Tables to convert a single byte from binary-coded decimal (BCD). + */ +static uchar_t bcd_to_byte[256] = { /* CSTYLED */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 0, 0, 0, 0, 0, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0, 0, 0, 0, 0, 0, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, 0, 0, 0, 0, 0, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0, 0, 0, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, +}; + +#define BCD_TO_BYTE(x) bcd_to_byte[(x) & 0xff] +#define YRBASE 68 + +static int days_thru_month[64] = { + 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 0, 0, + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0, + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0, + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0, +}; + +/* + * This function takes the raw Mostek data from the device tree translates + * it into UNIXC time (secs since Jan 1, 1970) and returns a string from + * ctime(3c). + */ +char * +get_time(uchar_t *mostek) +{ + time_t utc; + int sec, min, hour, day, month, year; + + year = BCD_TO_BYTE(mostek[6]) + YRBASE; + month = BCD_TO_BYTE(mostek[5] & 0x1f) + ((year & 3) << 4); + day = BCD_TO_BYTE(mostek[4] & 0x3f); + hour = BCD_TO_BYTE(mostek[2] & 0x3f); + min = BCD_TO_BYTE(mostek[1] & 0x7f); + sec = BCD_TO_BYTE(mostek[0] & 0x7f); + + utc = (year - 70); /* next 3 lines: utc = 365y + y/4 */ + utc += (utc << 3) + (utc << 6); + utc += (utc << 2) + ((year - 69) >> 2); + utc += days_thru_month[month] + day - 1; + utc = (utc << 3) + (utc << 4) + hour; /* 24 * day + hour */ + utc = (utc << 6) - (utc << 2) + min; /* 60 * hour + min */ + utc = (utc << 6) - (utc << 2) + sec; /* 60 * min + sec */ + + return (ctime((time_t *)&utc)); +} + +void +disp_powerfail(Prom_node *root) +{ + Prom_node *pnode; + char *option_str = "options"; + char *pf_str = "powerfail-time"; + char *value_str; + time_t value; + + pnode = dev_find_node(root, option_str); + if (pnode == NULL) { + return; + } + + value_str = get_prop_val(find_prop(pnode, pf_str)); + if (value_str == NULL) { + return; + } + + value = (time_t)atoi(value_str); + if (value == 0) + return; + + (void) log_printf( + dgettext(TEXT_DOMAIN, + "Most recent AC Power Failure:\n")); + (void) log_printf("=============================\n"); + (void) log_printf("%s", ctime(&value)); + (void) log_printf("\n"); +} + + +/*VARARGS1*/ +void +log_printf(char *fmt, ...) +{ + va_list ap; + int len; + static char bigbuf[4096]; + char buffer[1024]; + + if (print_flag == 0) { + return; + } + + va_start(ap, fmt); + if (logging != 0) { + len = vsprintf(buffer, fmt, ap); + (void) strcat(bigbuf, buffer); + + /* we only call to syslog when we get the entire line. */ + if (buffer[len-1] == '\n') { + syslog(LOG_DAEMON|LOG_NOTICE, bigbuf); + bigbuf[0] = 0; + } + + } else { + (void) vprintf(fmt, ap); + } + va_end(ap); +} + +void +print_header(int board) +{ + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Analysis for Board %d\n"), board, 0); + log_printf("--------------------\n", 0); +} diff --git a/usr/src/lib/libprtdiag/common/display_sun4u.c b/usr/src/lib/libprtdiag/common/display_sun4u.c new file mode 100644 index 0000000000..60638aa05d --- /dev/null +++ b/usr/src/lib/libprtdiag/common/display_sun4u.c @@ -0,0 +1,217 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +extern int sys_clk; + +int +display(Sys_tree *tree, + Prom_node *root, + struct system_kstat_data *kstats, + int syserrlog) +{ + int exit_code = 0; /* init to all OK */ + void *value; /* used for opaque PROM data */ + struct mem_total memory_total; /* Total memory in system */ + struct grp_info grps; /* Info on all groups in system */ + + sys_clk = -1; /* System clock freq. (in MHz) */ + + /* + * silently check for any types of machine errors + */ + exit_code = error_check(tree, kstats); + + /* + * Now display the machine's configuration. We do this if we + * are not logging or exit_code is set (machine is broke). + */ + if (!logging || exit_code) { + struct utsname uts_buf; + + /* + * Display system banner + */ + (void) uname(&uts_buf); + + log_printf( + dgettext(TEXT_DOMAIN, "System Configuration: " + "Sun Microsystems %s %s\n"), uts_buf.machine, + get_prop_val(find_prop(root, + "banner-name")), 0); + + /* display system clock frequency */ + value = get_prop_val(find_prop(root, "clock-frequency")); + if (value != NULL) { + sys_clk = ((*((int *)value)) + 500000) / 1000000; + log_printf(dgettext(TEXT_DOMAIN, "System clock " + "frequency: %d MHz\n"), sys_clk, 0); + } + + /* Display the Memory Size */ + display_memorysize(tree, kstats, &grps, &memory_total); + + /* Display platform specific configuration info */ + display_platform_specific_header(); + + /* Display the CPU devices */ + display_cpu_devices(tree); + + /* Display the Memory configuration */ + display_memoryconf(tree, &grps); + + /* Display all the IO cards. */ + (void) display_io_devices(tree); + + + /* + * Display any Hot plugged, disabled and failed board(s) + * where appropriate. + */ + display_hp_fail_fault(tree, kstats); + + display_diaginfo((syserrlog || (logging && exit_code)), + root, tree, kstats); + } + + return (exit_code); +} + + +int +error_check(Sys_tree *tree, struct system_kstat_data *kstats) +{ +#ifdef lint + tree = tree; + kstats = kstats; +#endif + /* + * This function is intentionally empty + */ + return (0); +} + +int +disp_fail_parts(Sys_tree *tree) +{ +#ifdef lint + tree = tree; +#endif + /* + * This function is intentionally empty + */ + return (0); +} + + +void +display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) +{ +#ifdef lint + tree = tree; + kstats = kstats; +#endif + /* + * This function is intentionally empty + */ +} + +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ +#ifdef lint + flag = flag; + root = root; + tree = tree; + kstats = kstats; +#endif + /* + * This function is intentionally empty + */ +} + + +void +resolve_board_types(Sys_tree *tree) +{ +#ifdef lint + tree = tree; +#endif + /* + * This function is intentionally empty + */ +} + +void +display_boardnum(int num) +{ + log_printf("%2d ", num, 0); +} + + +/* + * The various platforms can over-ride this function to + * return any platform specific configuration information + * they may wish to return in addition to the generic output. + */ +void +display_platform_specific_header(void) +{ + /* + * This function is intentionally empty + */ +} diff --git a/usr/src/lib/libprtdiag/common/display_sun4v.c b/usr/src/lib/libprtdiag/common/display_sun4v.c new file mode 100644 index 0000000000..42d998a12c --- /dev/null +++ b/usr/src/lib/libprtdiag/common/display_sun4v.c @@ -0,0 +1,330 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "display_sun4v.h" +#include "libprtdiag.h" + + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +extern int sys_clk; + +int +sun4v_display(Sys_tree *tree, Prom_node *root, int syserrlog, + picl_nodehdl_t plafh) +{ + int exit_code = 0; /* init to all OK */ + void *value; /* used for opaque PROM data */ + struct mem_total memory_total; /* Total memory in system */ + struct grp_info grps; /* Info on all groups in system */ + + sys_clk = -1; /* System clock freq. (in MHz) */ + + /* + * Now display the machine's configuration. We do this if we + * are not logging. + */ + if (!logging) { + struct utsname uts_buf; + + /* + * Display system banner + */ + (void) uname(&uts_buf); + + log_printf( + dgettext(TEXT_DOMAIN, "System Configuration: " + "Sun Microsystems %s %s\n"), uts_buf.machine, + get_prop_val(find_prop(root, + "banner-name")), 0); + + /* display system clock frequency */ + value = get_prop_val(find_prop(root, "clock-frequency")); + if (value != NULL) { + sys_clk = ((*((int *)value)) + 500000) / 1000000; + log_printf(dgettext(TEXT_DOMAIN, "System clock " + "frequency: %d MHz\n"), sys_clk, 0); + } + + /* Display the Memory Size */ + display_memorysize(tree, NULL, &grps, &memory_total); + + /* Display the CPU devices */ + sun4v_display_cpu_devices(plafh); + + /* Display the Memory configuration */ + sun4v_display_memoryconf(plafh); + + /* Display all the IO cards. */ + (void) sun4v_display_pci(plafh); + + sun4v_display_diaginfo((syserrlog || (logging)), root, plafh); + } + + return (exit_code); +} + +/* + * display_pci + * Display all the PCI IO cards on this board. + */ +void +sun4v_display_pci(picl_nodehdl_t plafh) +{ +#ifdef lint + plafh = plafh; +#endif + /* + * This function is intentionally empty + */ +} + +void +sun4v_display_memoryconf(picl_nodehdl_t plafh) +{ +#ifdef lint + plafh = plafh; +#endif + /* + * This function is intentionally empty + */ +} + +void +sun4v_display_cpu_devices(picl_nodehdl_t plafh) +{ + char *fmt = "%-12s %-5s %-8s %-19s %-5s"; + + /* + * Display the table header for CPUs . Then display the CPU + * frequency, cache size, and processor revision of all cpus. + */ + log_printf(dgettext(TEXT_DOMAIN, + "\n" + "=========================" + " CPUs " + "===============================================" + "\n" + "\n")); + log_printf(fmt, "", "", "", "CPU", "CPU", 0); + log_printf("\n"); + log_printf(fmt, "Location", "CPU", "Freq", + "Implementation", "Mask", 0); + log_printf("\n"); + log_printf(fmt, "------------", "-----", "--------", + "-------------------", "-----", 0); + log_printf("\n"); + + (void) picl_walk_tree_by_class(plafh, "cpu", "cpu", sun4v_display_cpus); + + log_printf("\n"); +} + +/* + * Display the CPUs present on this board. + */ +/*ARGSUSED*/ +int +sun4v_display_cpus(picl_nodehdl_t cpuh, void* args) +{ + int status; + picl_prophdl_t proph; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + picl_propinfo_t propinfo; + int *int_value; + uint64_t cpuid, mask_no; + char *comp_value; + char *no_prop_value = " "; + char freq_str[MAXSTRLEN]; + char fru_name[MAXSTRLEN]; + + /* + * Get cpuid property and print it and the NAC name + */ + status = picl_get_propinfo_by_name(cpuh, "cpuid", &propinfo, &proph); + if (status == PICL_SUCCESS) { + status = picl_get_propval(proph, &cpuid, sizeof (cpuid)); + if (status != PICL_SUCCESS) { + log_printf("%-12s", no_prop_value); + log_printf("%6s", no_prop_value); + } else { + (void) snprintf(fru_name, sizeof (fru_name), "%s%d", + CPU_STRAND_NAC, (int)cpuid); + log_printf("%-12s", fru_name); + log_printf("%6d", (int)cpuid); + } + } else { + log_printf("%-12s", no_prop_value); + log_printf("%6s", no_prop_value); + } + +clock_freq: + status = picl_get_propinfo_by_name(cpuh, "clock-frequency", &propinfo, + &proph); + if (status == PICL_SUCCESS) { + int_value = malloc(propinfo.size); + if (int_value == NULL) { + log_printf("%9s", no_prop_value); + goto compatible; + } + status = picl_get_propval(proph, int_value, propinfo.size); + if (status != PICL_SUCCESS) { + log_printf("%9s", no_prop_value); + } else { + /* Running frequency */ + (void) snprintf(freq_str, sizeof (freq_str), "%d MHz", + CLK_FREQ_TO_MHZ(*int_value)); + log_printf("%9s", freq_str); + } + free(int_value); + } else + log_printf("%9s", no_prop_value); + +compatible: + status = picl_get_propinfo_by_name(cpuh, "compatible", &propinfo, + &proph); + if (status == PICL_SUCCESS) { + if (propinfo.type == PICL_PTYPE_CHARSTRING) { + /* + * Compatible Property only has 1 value + */ + comp_value = malloc(propinfo.size); + if (comp_value == NULL) { + log_printf("%20s", no_prop_value, 0); + goto mask; + } + status = picl_get_propval(proph, comp_value, + propinfo.size); + if (status == PICL_SUCCESS) { + log_printf("%20s", no_prop_value, 0); + free(comp_value); + } + } else if (propinfo.type == PICL_PTYPE_TABLE) { + /* + * Compatible Property has multiple values + */ + status = picl_get_propval(proph, &tblh, propinfo.size); + if (status != PICL_SUCCESS) { + printf("Failed getting tblh\n"); + log_printf("%20s", no_prop_value, 0); + goto mask; + } + status = picl_get_next_by_row(tblh, &rowproph); + if (status != PICL_SUCCESS) { + printf("Failed getting next by row\n"); + log_printf("%20s", no_prop_value, 0); + goto mask; + } + + status = picl_get_propinfo(rowproph, &propinfo); + if (status != PICL_SUCCESS) { + printf("Failed getting prop for rowproph\n"); + log_printf("%20s", no_prop_value, 0); + goto mask; + } + + comp_value = malloc(propinfo.size); + if (comp_value == NULL) { + printf("Failed to get malloc value?\n"); + log_printf("%20s", no_prop_value, 0); + goto mask; + } + + status = picl_get_propval(rowproph, comp_value, + propinfo.size); + if (status != PICL_SUCCESS) { + printf("Failed geting rowproph\n"); + log_printf("%20s", no_prop_value, 0); + free(comp_value); + goto mask; + } else + log_printf("%20s", comp_value, 0); + free(comp_value); + } + } else + log_printf("%20s", no_prop_value, 0); + +mask: + status = picl_get_propinfo_by_name(cpuh, "mask#", &propinfo, &proph); + if (status == PICL_SUCCESS) { + status = picl_get_propval(proph, &mask_no, sizeof (mask_no)); + if (status != PICL_SUCCESS) { + log_printf("%9s", no_prop_value); + } else { + log_printf(dgettext(TEXT_DOMAIN, " %2d.%d"), + (mask_no>> 4) & 0xf, mask_no & 0xf); + } + } else + log_printf("%9s", no_prop_value); + +done: + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +void +sun4v_display_diaginfo(int flag, Prom_node *root, picl_nodehdl_t plafh) +{ +#ifdef lint + flag = flag; + root = root; + plafh = plafh; +#endif + /* + * This function is intentionally empty + */ +} + +void +display_boardnum(int num) +{ + log_printf("%2d ", num, 0); +} diff --git a/usr/src/lib/libprtdiag/common/io.c b/usr/src/lib/libprtdiag/common/io.c new file mode 100644 index 0000000000..f1bdb447cc --- /dev/null +++ b/usr/src/lib/libprtdiag/common/io.c @@ -0,0 +1,1067 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <sys/systeminfo.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +Prom_node * +find_pci_bus(Prom_node *node, int id, int bus) +{ + Prom_node *pnode; + + /* find the first pci node */ + pnode = dev_find_node(node, "pci"); + + while (pnode != NULL) { + int tmp_id; + int tmp_bus; + + tmp_id = get_id(pnode); + tmp_bus = get_pci_bus(pnode); + + if ((tmp_id == id) && + (tmp_bus == bus)) { + break; + } + + pnode = dev_next_node(pnode, "pci"); + } + return (pnode); +} + +/* + * get_pci_bus + * + * Determines the PCI bus, either A (0) or B (1). If the function cannot + * find the bus-ranges property, it returns -1. + */ +int +get_pci_bus(Prom_node *pnode) +{ + int *value; + + /* look up the bus-range property */ + if ((value = (int *)get_prop_val(find_prop(pnode, "bus-range"))) == + NULL) { + return (-1); + } + + if (*value == 0) { + return (1); /* B bus has a bus-range value = 0 */ + } else { + return (0); + } +} + + + +/* + * Find the PCI device number of this PCI device. If no device number can + * be determined, then return -1. + */ +int +get_pci_device(Prom_node *pnode) +{ + void *value; + + if ((value = get_prop_val(find_prop(pnode, "assigned-addresses"))) != + NULL) { + return (PCI_DEVICE(*(int *)value)); + } else { + return (-1); + } +} + +/* + * Find the PCI device number of this PCI device. If no device number can + * be determined, then return -1. + */ +int +get_pci_to_pci_device(Prom_node *pnode) +{ + void *value; + + if ((value = get_prop_val(find_prop(pnode, "reg"))) != + NULL) { + return (PCI_DEVICE(*(int *)value)); + } else { + return (-1); + } +} + +/* + * free_io_cards + * Frees the memory allocated for an io card list. + */ +void +free_io_cards(struct io_card *card_list) +{ + /* Free the list */ + if (card_list != NULL) { + struct io_card *p, *q; + + for (p = card_list, q = NULL; p != NULL; p = q) { + q = p->next; + free(p); + } + } +} + + +/* + * insert_io_card + * Inserts an io_card structure into the list. The list is maintained + * in order based on board number and slot number. Also, the storage + * for the "card" argument is assumed to be handled by the caller, + * so we won't touch it. + */ +struct io_card * +insert_io_card(struct io_card *list, struct io_card *card) +{ + struct io_card *newcard; + struct io_card *p, *q; + + if (card == NULL) + return (list); + + /* Copy the card to be added into new storage */ + newcard = (struct io_card *)malloc(sizeof (struct io_card)); + if (newcard == NULL) { + perror("malloc"); + exit(2); + } + (void) memcpy(newcard, card, sizeof (struct io_card)); + newcard->next = NULL; + + if (list == NULL) + return (newcard); + + /* Find the proper place in the list for the new card */ + for (p = list, q = NULL; p != NULL; q = p, p = p->next) { + if (newcard->board < p->board) + break; + if ((newcard->board == p->board) && (newcard->slot < p->slot)) + break; + } + + /* Insert the new card into the list */ + if (q == NULL) { + newcard->next = p; + return (newcard); + } else { + newcard->next = p; + q->next = newcard; + return (list); + } +} + + +char * +fmt_manf_id(unsigned int encoded_id, char *outbuf) +{ + union manuf manuf; + + /* + * Format the manufacturer's info. Note a small inconsistency we + * have to work around - Brooktree has it's part number in decimal, + * while Mitsubishi has it's part number in hex. + */ + manuf.encoded_id = encoded_id; + switch (manuf.fld.manf) { + case MANF_BROOKTREE: + (void) sprintf(outbuf, "%s %d, version %d", "Brooktree", + manuf.fld.partno, manuf.fld.version); + break; + + case MANF_MITSUBISHI: + (void) sprintf(outbuf, "%s %x, version %d", "Mitsubishi", + manuf.fld.partno, manuf.fld.version); + break; + + default: + (void) sprintf(outbuf, "JED code %d, Part num 0x%x, version %d", + manuf.fld.manf, manuf.fld.partno, manuf.fld.version); + } + return (outbuf); +} + + +/* + * Find the sbus slot number of this Sbus device. If no slot number can + * be determined, then return -1. + */ +int +get_sbus_slot(Prom_node *pnode) +{ + void *value; + + if ((value = get_prop_val(find_prop(pnode, "reg"))) != NULL) { + return (*(int *)value); + } else { + return (-1); + } +} + + +/* + * This routine is the generic link into displaying system IO + * configuration. It displays the table header, then displays + * all the SBus cards, then displays all fo the PCI IO cards. + */ +void +display_io_devices(Sys_tree *tree) +{ + Board_node *bnode; + + /* + * TRANSLATION_NOTE + * Following string is used as a table header. + * Please maintain the current alignment in + * translation. + */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " IO Cards "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + bnode = tree->bd_list; + while (bnode != NULL) { + display_sbus(bnode); + display_pci(bnode); + display_ffb(bnode, 1); + bnode = bnode->next; + } +} + +void +display_pci(Board_node *bnode) +{ +#ifdef lint + bnode = bnode; +#endif + /* + * This function is intentionally empty + */ +} + + +/* + * Print out all the io cards in the list. Also print the column + * headers if told to do so. + */ +void +display_io_cards(struct io_card *list) +{ + static int banner = 0; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + if (banner == 0) { + log_printf(" Bus Freq\n", 0); + log_printf("Brd Type MHz Slot " + "Name " + "Model", 0); + log_printf("\n", 0); + log_printf("--- ---- ---- ---------- " + "---------------------------- " + "--------------------", 0); + log_printf("\n", 0); + banner = 1; + } + + for (p = list; p != NULL; p = p -> next) { + log_printf("%2d ", p->board, 0); + log_printf("%-4s ", p->bus_type, 0); + log_printf("%3d ", p->freq, 0); + /* + * We check to see if it's an int or + * a char string to display for slot. + */ + if (p->slot == PCI_SLOT_IS_STRING) + log_printf("%10s ", p->slot_str, 0); + else + log_printf("%10d ", p->slot, 0); + + log_printf("%-28.28s", p->name, 0); + if (strlen(p->name) > 28) + log_printf("+ ", 0); + else + log_printf(" ", 0); + log_printf("%-19.19s", p->model, 0); + if (strlen(p->model) > 19) + log_printf("+", 0); + log_printf("\n", 0); + } +} + +/* + * Display all FFBs on this board. It can either be in tabular format, + * or a more verbose format. + */ +void +display_ffb(Board_node *board, int table) +{ + Prom_node *fb; + void *value; + struct io_card *card_list = NULL; + struct io_card card; + char *type; + char *label; + + if (board == NULL) + return; + + /* Fill in common information */ + card.display = 1; + card.board = board->board_num; + (void) sprintf(card.bus_type, BUS_TYPE); + card.freq = sys_clk; + + for (fb = dev_find_node_by_type(board->nodes, "device_type", "display"); + fb != NULL; + fb = dev_next_node_by_type(fb, "device_type", "display")) { + value = get_prop_val(find_prop(fb, "name")); + if (value != NULL) { + if ((strcmp(FFB_NAME, value)) == 0) { + type = FFB_NAME; + label = "FFB"; + } else if ((strcmp(AFB_NAME, value)) == 0) { + type = AFB_NAME; + label = "AFB"; + } else + continue; + } else + continue; + if (table == 1) { + /* Print out in table format */ + + /* XXX - Get the slot number (hack) */ + card.slot = get_id(fb); + + /* Find out if it's single or double buffered */ + (void) sprintf(card.name, "%s", label); + value = get_prop_val(find_prop(fb, "board_type")); + if (value != NULL) + if ((*(int *)value) & FFB_B_BUFF) + (void) sprintf(card.name, + "%s, Double Buffered", label); + else + (void) sprintf(card.name, + "%s, Single Buffered", label); + + /* + * Print model number only if board_type bit 2 + * is not set and it is not SUNW,XXX-XXXX. + */ + card.model[0] = '\0'; + + if (strcmp(type, AFB_NAME) == 0) { + if (((*(int *)value) & 0x4) != 0x4) { + value = get_prop_val(find_prop(fb, + "model")); + if ((value != NULL) && + (strcmp(value, + "SUNW,XXX-XXXX") != 0)) { + (void) sprintf(card.model, "%s", + (char *)value); + } + } + } else { + value = get_prop_val(find_prop(fb, "model")); + if (value != NULL) + (void) sprintf(card.model, "%s", + (char *)value); + } + + card_list = insert_io_card(card_list, &card); + } else { + /* print in long format */ + char device[MAXSTRLEN]; + int fd = -1; + struct dirent *direntp; + DIR *dirp; + union strap_un strap; + struct ffb_sys_info fsi; + + /* Find the device node using upa-portid/portid */ + value = get_prop_val(find_prop(fb, "upa-portid")); + if (value == NULL) + value = get_prop_val(find_prop(fb, "portid")); + + if (value == NULL) + continue; + + (void) sprintf(device, "%s@%x", type, + *(int *)value); + if ((dirp = opendir("/devices")) == NULL) + continue; + + while ((direntp = readdir(dirp)) != NULL) { + if (strstr(direntp->d_name, device) != NULL) { + (void) sprintf(device, "/devices/%s", + direntp->d_name); + fd = open(device, O_RDWR, 0666); + break; + } + } + (void) closedir(dirp); + + if (fd == -1) + continue; + + if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0) + continue; + + log_printf("%s Hardware Configuration:\n", label, 0); + log_printf("-----------------------------------\n", 0); + + strap.ffb_strap_bits = fsi.ffb_strap_bits; + log_printf("\tBoard rev: %d\n", + (int)strap.fld.board_rev, 0); + log_printf("\tFBC version: 0x%x\n", fsi.fbc_version, 0); + log_printf("\tDAC: %s\n", + fmt_manf_id(fsi.dac_version, device), 0); + log_printf("\t3DRAM: %s\n", + fmt_manf_id(fsi.fbram_version, device), 0); + log_printf("\n", 0); + } + + } + display_io_cards(card_list); + free_io_cards(card_list); +} + + +/* + * Display all the SBus IO cards on this board. + */ +void +display_sbus(Board_node *board) +{ + struct io_card card; + struct io_card *card_list = NULL; + int freq; + int card_num; + void *value; + Prom_node *sbus; + Prom_node *card_node; + + if (board == NULL) + return; + + for (sbus = dev_find_node(board->nodes, SBUS_NAME); sbus != NULL; + sbus = dev_next_node(sbus, SBUS_NAME)) { + + /* Skip failed nodes for now */ + if (node_failed(sbus)) + continue; + + /* Calculate SBus frequency in MHz */ + value = get_prop_val(find_prop(sbus, "clock-frequency")); + if (value != NULL) + freq = ((*(int *)value) + 500000) / 1000000; + else + freq = -1; + + for (card_node = sbus->child; card_node != NULL; + card_node = card_node->sibling) { + char *model; + char *name; + char *child_name; + + card_num = get_sbus_slot(card_node); + if (card_num == -1) + continue; + + /* Fill in card information */ + card.display = 1; + card.freq = freq; + card.board = board->board_num; + (void) sprintf(card.bus_type, "SBus"); + card.slot = card_num; + card.status[0] = '\0'; + + /* Try and get card status */ + value = get_prop_val(find_prop(card_node, "status")); + if (value != NULL) + (void) strncpy(card.status, (char *)value, + MAXSTRLEN); + + /* XXX - For now, don't display failed cards */ + if (strstr(card.status, "fail") != NULL) + continue; + + /* Now gather all of the node names for that card */ + model = (char *)get_prop_val(find_prop(card_node, + "model")); + name = get_node_name(card_node); + + if (name == NULL) + continue; + + card.name[0] = '\0'; + card.model[0] = '\0'; + + /* Figure out how we want to display the name */ + child_name = get_node_name(card_node->child); + if ((card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(card_node->child, + "device_type")); + if (value != NULL) + (void) sprintf(card.name, "%s/%s (%s)", + name, child_name, + (char *)value); + else + (void) sprintf(card.name, "%s/%s", name, + child_name); + } else { + (void) strncpy(card.name, name, MAXSTRLEN); + } + + if (model != NULL) + (void) strncpy(card.model, model, MAXSTRLEN); + + card_list = insert_io_card(card_list, &card); + } + } + + /* We're all done gathering card info, now print it out */ + display_io_cards(card_list); + free_io_cards(card_list); +} + + +/* + * Get slot-names properties from parent node and + * store them in an array. + */ +int +populate_slot_name_arr(Prom_node *pci, int *slot_name_bits, + char **slot_name_arr, int num_slots) +{ + int i, j, bit_mask; + char *value; + + value = (char *)get_prop_val(find_prop(pci, "slot-names")); + D_PRINTF("\n populate_slot_name_arr: value = [0x%x]\n", value); + + if (value != NULL) { + char *strings_arr[MAX_SLOTS_PER_IO_BD]; + bit_mask = *slot_name_bits = *(int *)value; + D_PRINTF("\nslot_names 1st integer = [0x%x]", *slot_name_bits); + + /* array starts after first int */ + strings_arr[0] = value + sizeof (int); + + /* + * break the array out into num_slots number of strings + */ + for (i = 1; i < num_slots; i++) { + strings_arr[i] = (char *)strings_arr[i - 1] + + strlen(strings_arr[i - 1]) + 1; + } + + /* + * process array of slot_names to remove blanks + */ + j = 0; + for (i = 0; i < num_slots; i++) { + if ((bit_mask >> i) & 0x1) + slot_name_arr[i] = strings_arr[j++]; + else + slot_name_arr[i] = ""; + + D_PRINTF("\nslot_name_arr[%d] = [%s]", i, + slot_name_arr[i]); + } + return (0); + } else { + D_PRINTF("\n populate_slot_name_arr: - psycho with no " + "slot-names\n"); + return (0); + } +} + +int +get_card_frequency(Prom_node *pci) +{ + char *value = get_prop_val(find_prop(pci, "clock-frequency")); + + if (value == NULL) + return (-1); + else + return (int)(((*(int *)value) + 500000) / 1000000); + +} + +void +get_dev_func_num(Prom_node *card_node, int *dev_no, int *func_no) +{ + + void *value = get_prop_val(find_prop(card_node, "reg")); + + if (value != NULL) { + int int_val = *(int *)value; + *dev_no = PCI_REG_TO_DEV(int_val); + *func_no = PCI_REG_TO_FUNC(int_val); + } else { + *dev_no = -1; + *func_no = -1; + } +} + +void +get_pci_class_codes(Prom_node *card_node, int *class_code, int *subclass_code) +{ + int class_code_reg = get_pci_class_code_reg(card_node); + + *class_code = CLASS_REG_TO_CLASS(class_code_reg); + *subclass_code = CLASS_REG_TO_SUBCLASS(class_code_reg); +} + +int +is_pci_bridge(Prom_node *card_node, char *name) +{ + int class_code, subclass_code; + + if (card_node == NULL) + return (FALSE); + + get_pci_class_codes(card_node, &class_code, &subclass_code); + + if ((strncmp(name, "pci", 3) == 0) && + (class_code == PCI_BRIDGE_CLASS) && + (subclass_code == PCI_PCI_BRIDGE_SUBCLASS)) + return (TRUE); + else + return (FALSE); +} + +int +is_pci_bridge_other(Prom_node *card_node, char *name) +{ + int class_code, subclass_code; + + if (card_node == NULL) + return (FALSE); + + get_pci_class_codes(card_node, &class_code, &subclass_code); + + if ((strncmp(name, "pci", 3) == 0) && + (class_code == PCI_BRIDGE_CLASS) && + (subclass_code == PCI_SUBCLASS_OTHER)) + return (TRUE); + else + return (FALSE); +} +void +get_pci_card_model(Prom_node *card_node, char *model) +{ + char *name = get_prop_val(find_prop(card_node, "name")); + char *value = get_prop_val(find_prop(card_node, "model")); + int pci_bridge = is_pci_bridge(card_node, name); + + if (value == NULL) + model[0] = '\0'; + else + (void) sprintf(model, "%s", + (char *)value); + + if (pci_bridge) { + if (strlen(model) == 0) + (void) sprintf(model, + "%s", "pci-bridge"); + else + (void) sprintf(model, + "%s/pci-bridge", model); + } +} + +void +create_io_card_name(Prom_node *card_node, char *name, char *card_name) +{ + char *value = get_prop_val(find_prop(card_node, "compatible")); + char *child_name; + char buf[MAXSTRLEN]; + + if (value != NULL) { + (void) sprintf(buf, "%s-%s", name, + (char *)value); + } else + (void) sprintf(buf, "%s", name); + + name = buf; + + child_name = (char *)get_node_name(card_node->child); + + if ((card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(card_node->child, + "device_type")); + if (value != NULL) + (void) sprintf(card_name, "%s/%s (%s)", + name, child_name, + (char *)value); + else + (void) sprintf(card_name, "%s/%s", name, + child_name); + } else { + (void) sprintf(card_name, "%s", (char *)name); + } +} + + +/* + * Desktop display_psycho_pci + * Display all the psycho based PCI IO cards on this board. + */ + +/* ARGSUSED */ +void +display_psycho_pci(Board_node *board) +{ + struct io_card *card_list = NULL; + struct io_card card; + void *value; + + Prom_node *pci, *card_node, *pci_bridge_node = NULL; + char *name; + int slot_name_bits, pci_bridge_dev_no, + class_code, subclass_code, + pci_pci_bridge; + char *slot_name_arr[MAX_SLOTS_PER_IO_BD]; + + if (board == NULL) + return; + + /* Initialize all the common information */ + card.display = 1; + card.board = board->board_num; + (void) sprintf(card.bus_type, "PCI"); + + for (pci = dev_find_node_by_type(board->nodes, "model", "SUNW,psycho"); + pci != NULL; + pci = dev_next_node_by_type(pci, "model", "SUNW,psycho")) { + + /* + * If we have reached a pci-to-pci bridge node, + * we are one level below the 'pci' nodes level + * in the device tree. To get back to that level, + * the search should continue with the sibling of + * the parent or else the remaining 'pci' cards + * will not show up in the output. + */ + if (find_prop(pci, "upa-portid") == NULL) { + if ((pci->parent->sibling != NULL) && + (strcmp(get_prop_val( + find_prop(pci->parent->sibling, + "name")), PCI_NAME) == 0)) + pci = pci->parent->sibling; + else { + pci = pci->parent->sibling; + continue; + } + } + + D_PRINTF("\n\n------->Looking at device [%s][%d] - [%s]\n", + PCI_NAME, *((int *)get_prop_val(find_prop( + pci, "upa-portid"))), + get_prop_val(find_prop(pci, "model"))); + + /* Skip all failed nodes for now */ + if (node_failed(pci)) + continue; + + /* Fill in frequency */ + card.freq = get_card_frequency(pci); + + /* + * Each PSYCHO device has a slot-names property that can be + * used to determine the slot-name string for each IO + * device under this node. We get this array now and use + * it later when looking at the children of this PSYCHO. + */ + if ((populate_slot_name_arr(pci, &slot_name_bits, + (char **)&slot_name_arr, MAX_SLOTS_PER_IO_BD)) != 0) + goto next_card; + + /* Walk through the PSYCHO children */ + card_node = pci->child; + while (card_node != NULL) { + + pci_pci_bridge = FALSE; + + /* If it doesn't have a name, skip it */ + name = (char *)get_prop_val( + find_prop(card_node, "name")); + if (name == NULL) + goto next_card; + + /* get dev# and func# for this card. */ + get_dev_func_num(card_node, &card.dev_no, + &card.func_no); + + /* get class/subclass code for this card. */ + get_pci_class_codes(card_node, &class_code, + &subclass_code); + + D_PRINTF("\nName [%s] - ", name); + D_PRINTF("device no [%d] - ", card.dev_no); + D_PRINTF("class_code [%d] subclass_code [%d] - ", + class_code, subclass_code); + + /* + * Weed out PCI Bridge, subclass 'other' and + * ebus nodes. + */ + if (((class_code == PCI_BRIDGE_CLASS) && + (subclass_code == PCI_SUBCLASS_OTHER)) || + (strstr(name, "ebus"))) { + D_PRINTF("\nSkip ebus/class-other nodes [%s]", + name); + goto next_card; + } + + /* + * If this is a PCI bridge, then we store it's dev_no + * so that it's children can use it for getting at + * the slot_name. + */ + if (is_pci_bridge(card_node, name)) { + pci_bridge_dev_no = card.dev_no; + pci_bridge_node = card_node; + pci_pci_bridge = TRUE; + D_PRINTF("\nPCI Bridge detected\n"); + } + + /* + * If we are the child of a pci_bridge we use the + * dev# of the pci_bridge as an index to get + * the slot number. We know that we are a child of + * a pci-bridge if our parent is the same as the last + * pci_bridge node found above. + */ + if (card_node->parent == pci_bridge_node) + card.dev_no = pci_bridge_dev_no; + + /* Get slot-names property from slot_names_arr. */ + get_slot_number_str(&card, (char **)slot_name_arr, + slot_name_bits); + + if (slot_name_bits) + D_PRINTF("\nIO Card [%s] dev_no [%d] SlotStr " + "[%s] slot [%s]", name, card.dev_no, + slot_name_arr[card.dev_no], + card.slot_str); + + /* XXX - Don't know how to get status for PCI cards */ + card.status[0] = '\0'; + + /* Get the model of this card */ + get_pci_card_model(card_node, (char *)&card.model); + + /* + * If we haven't figured out the frequency yet, + * try and get it from the card. + */ + value = get_prop_val(find_prop(pci, "clock-frequency")); + if (value != NULL && card.freq == -1) + card.freq = ((*(int *)value) + 500000) + / 1000000; + + + /* Figure out how we want to display the name */ + create_io_card_name(card_node, name, + (char *)&card.name); + + if (card.freq != -1) + card_list = insert_io_card(card_list, &card); + +next_card: + /* + * If we are done with the children of the pci bridge, + * we must continue with the remaining siblings of + * the pci-to-pci bridge - otherwise we move onto our + * own sibling. + */ + if (pci_pci_bridge) { + if (card_node->child != NULL) + card_node = card_node->child; + else + card_node = card_node->sibling; + } else { + if ((card_node->parent == pci_bridge_node) && + (card_node->sibling == NULL)) + card_node = pci_bridge_node->sibling; + else + card_node = card_node->sibling; + } + } /* end-while */ + } /* end-for */ + + D_PRINTF("\n\n"); + + display_io_cards(card_list); + free_io_cards(card_list); +} + +void +get_slot_number_str(struct io_card *card, char **slot_name_arr, + int slot_name_bits) +{ + if (card->dev_no != -1) { + char *slot; + /* + * slot_name_bits is a mask of the plug-in slots so if our + * dev_no does not appear in this mask we must be an + * on_board device so set the slot to 'On-Board' + */ + if (slot_name_bits & (1 << card->dev_no)) { + /* we are a plug-in card */ + slot = slot_name_arr[card->dev_no]; + if (strlen(slot) != 0) { + (void) sprintf(card->slot_str, "%s", + slot); + } else + (void) sprintf(card->slot_str, "-"); + } else { + /* this is an on-board dev. */ + sprintf(card->slot_str, "On-Board"); + } + + } else { + (void) sprintf(card->slot_str, "%c", '-'); + } + + /* Informs display_io_cards to print slot_str instead of slot */ + card->slot = PCI_SLOT_IS_STRING; +} + + +/* + * The output of a number of I/O cards are identical so we need to + * differentiate between them. + * + * This function is called by the platform specific code and it decides + * if the card needs further processing. + * + * It can be extended in the future if card types other than QLC have + * the same problems. + */ +void +distinguish_identical_io_cards(char *name, Prom_node *node, + struct io_card *card) +{ + if ((name == NULL) || (node == NULL)) + return; + + if (strcmp(name, "SUNW,qlc") == 0) + decode_qlc_card_model_prop(node, card); +} + + +/* + * The name/model properties for a number of the QLC FCAL PCI cards are + * identical (*), so we need to distinguish them using the subsystem-id + * and modify the model string to be more informative. + * + * (*) Currently the problem cards are: + * Amber + * Crystal+ + */ +void +decode_qlc_card_model_prop(Prom_node *card_node, struct io_card *card) +{ + void *value = NULL; + + if (card_node == NULL) + return; + + value = get_prop_val(find_prop(card_node, "subsystem-id")); + if (value != NULL) { + int id = *(int *)value; + + switch (id) { + case AMBER_SUBSYSTEM_ID: + (void) snprintf(card->model, MAX_QLC_MODEL_LEN, "%s", + AMBER_CARD_NAME); + break; + + case CRYSTAL_SUBSYSTEM_ID: + (void) snprintf(card->model, MAX_QLC_MODEL_LEN, "%s", + CRYSTAL_CARD_NAME); + break; + + default: + /* + * If information has been saved into the model field + * before this function was called we will keep it as + * it probably will be more meaningful that the + * subsystem-id, otherwise we save the subsystem-id in + * the hope that it will distinguish the cards. + */ + if (strcmp(card->model, "") == 0) { + (void) snprintf(card->model, MAX_QLC_MODEL_LEN, + "0x%x", id); + } + break; + } + } +} diff --git a/usr/src/lib/libprtdiag/common/kstat.c b/usr/src/lib/libprtdiag/common/kstat.c new file mode 100644 index 0000000000..1c29e96642 --- /dev/null +++ b/usr/src/lib/libprtdiag/common/kstat.c @@ -0,0 +1,428 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +/* + * This module does the reading and interpreting of sun4u system + * kstats. These kstats are created by the following drivers: + * fhc, environ, sysctrl. Each board in the tree should have + * kstats created for it. There are also system wide kstats that + * are created. + */ +void +read_platform_kstats(Sys_tree *tree, struct system_kstat_data *sys_kstat, + struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep) +{ + Board_node *bnode; + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *knp; + int i; + struct hp_info *hp; + +#ifdef lint + ep = ep; +#endif + if ((kc = kstat_open()) == NULL) { + return; + } + + /* For each board in the system, read the kstats for it. */ + for (bnode = tree->bd_list; bnode != NULL; bnode = bnode->next) { + int board; + + /* + * Kstat instances numbers are set by fhc, ac, simmstat, + * and environ drivers based on their board# property. + */ + board = bnode->board_num; + bdp = &sys_kstat->bd_ksp_list[board]; + + /* Try to find an FHC instance for this board number */ + ksp = kstat_lookup(kc, UNIX, board, FHC_KSTAT_NAME); + + /* Atempt to read the FHC kstat */ + if ((ksp != NULL) && (kstat_read(kc, ksp, NULL) == -1)) { + ksp = NULL; + } + + /* Now read out the data if the kstat read OK */ + if (ksp != NULL) { + /* + * We set the kstats_ok flag to good here. If we + * fail one of the data reads, we set it to bad. + */ + bdp->fhc_kstats_ok = 1; + + /* + * For each data value, If the Kstat named struct + * is found, then get the data out. + */ + knp = kstat_data_lookup(ksp, CSR_KSTAT_NAMED); + if (knp != NULL) { + bdp->fhc_csr = knp->value.ul; + } else { + bdp->fhc_kstats_ok = 0; + } + knp = kstat_data_lookup(ksp, BSR_KSTAT_NAMED); + if (knp != NULL) { + bdp->fhc_bsr = knp->value.ul; + } else { + bdp->fhc_kstats_ok = 0; + } + } + + /* Try to find an AC instance for this board number */ + ksp = kstat_lookup(kc, UNIX, board, AC_KSTAT_NAME); + + /* Attempt to read the AC kstat. */ + if ((ksp != NULL) && (kstat_read(kc, ksp, NULL) == -1)) { + ksp = NULL; + } + + /* If the AC kstat exists, try to read the data from it. */ + if (ksp != NULL) { + /* + * We set the kstats_ok flag to good here. If we + * fail one of the data reads, we set it to bad. + */ + bdp->ac_kstats_ok = 1; + bdp->ac_memstat_ok = 1; + + /* + * For each data value, If the Kstat named struct + * is found, then get the data out. + */ + + knp = kstat_data_lookup(ksp, MEMCTL_KSTAT_NAMED); + if (knp != NULL) { + bdp->ac_memctl = knp->value.ull; + } else { + bdp->ac_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, MEMDECODE0_KSTAT_NAMED); + if (knp != NULL) { + bdp->ac_memdecode[0] = knp->value.ull; + } else { + bdp->ac_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, MEMDECODE1_KSTAT_NAMED); + if (knp != NULL) { + bdp->ac_memdecode[1] = knp->value.ull; + } else { + bdp->ac_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, BANK_0_KSTAT_NAMED); + if (knp != NULL) { + bdp->mem_stat[0].status = knp->value.c[0]; + bdp->mem_stat[0].condition = knp->value.c[1]; + } else { + bdp->ac_memstat_ok = 0; + } + + knp = kstat_data_lookup(ksp, BANK_1_KSTAT_NAMED); + if (knp != NULL) { + bdp->mem_stat[1].status = knp->value.c[0]; + bdp->mem_stat[1].condition = knp->value.c[1]; + } else { + bdp->ac_memstat_ok = 0; + } + + } + + /* Try to find an simmstat instance for this board number */ + ksp = kstat_lookup(kc, UNIX, board, SIMMSTAT_KSTAT_NAME); + + if (ksp != NULL) { + if (kstat_read(kc, ksp, NULL) == -1) { + bdp->simmstat_kstats_ok = 0; + } else { + bdp->simmstat_kstats_ok = 1; + (void) memcpy(&bdp->simm_status, ksp->ks_data, + sizeof (bdp->simm_status)); + } + } + + /* Try to find an overtemp kstat instance for this board */ + ksp = kstat_lookup(kc, UNIX, board, OVERTEMP_KSTAT_NAME); + + if (ksp != NULL) { + if (kstat_read(kc, ksp, NULL) == -1) { + bdp->temp_kstat_ok = 0; + } else { + bdp->temp_kstat_ok = 1; + (void) memcpy(&bdp->tempstat, ksp->ks_data, + sizeof (bdp->tempstat)); + /* XXX - this is for 2.5.1 testing. remove */ + if (sizeof (bdp->tempstat) > ksp->ks_data_size) + bdp->tempstat.trend = TREND_UNKNOWN; + } + } + } + + /* Read the kstats for the system control board */ + ksp = kstat_lookup(kc, UNIX, 0, SYSCTRL_KSTAT_NAME); + + if ((ksp != NULL) && (kstat_read(kc, ksp, NULL) == -1)) { + sys_kstat->sys_kstats_ok = 0; + ksp = NULL; + } + + if (ksp != NULL) { + sys_kstat->sys_kstats_ok = 1; + + knp = kstat_data_lookup(ksp, CSR_KSTAT_NAMED); + if (knp != NULL) { + sys_kstat->sysctrl = knp->value.c[0]; + } else { + sys_kstat->sys_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, STAT1_KSTAT_NAMED); + if (knp != NULL) { + sys_kstat->sysstat1 = knp->value.c[0]; + } else { + sys_kstat->sys_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, STAT2_KSTAT_NAMED); + if (knp != NULL) { + sys_kstat->sysstat2 = knp->value.c[0]; + } else { + sys_kstat->sys_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, CLK_FREQ2_KSTAT_NAMED); + if (knp != NULL) { + sys_kstat->clk_freq2 = knp->value.c[0]; + } else { + sys_kstat->sys_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, FAN_KSTAT_NAMED); + if (knp != NULL) { + sys_kstat->fan_status = knp->value.c[0]; + } else { + sys_kstat->sys_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, KEY_KSTAT_NAMED); + if (knp != NULL) { + sys_kstat->keysw_status = knp->value.c[0]; + } else { + sys_kstat->sys_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, POWER_KSTAT_NAMED); + if (knp != NULL) { + sys_kstat->power_state = + (enum power_state)knp->value.l; + } else { + sys_kstat->sys_kstats_ok = 0; + } + + knp = kstat_data_lookup(ksp, CLK_VER_KSTAT_NAME); + if (knp != NULL) { + sys_kstat->clk_ver = knp->value.c[0]; + } else { + /* + * the clock version register only appears + * on new clock boards + */ + sys_kstat->clk_ver = 0; + } + + } + + /* Read the kstats for the power supply stats */ + ksp = kstat_lookup(kc, UNIX, 0, PSSHAD_KSTAT_NAME); + + if ((ksp != NULL) && (kstat_read(kc, ksp, NULL) != -1)) { + sys_kstat->psstat_kstat_ok = 1; + (void) memcpy(&sys_kstat->ps_shadow[0], ksp->ks_data, + sizeof (sys_kstat->ps_shadow)); + } else { + sys_kstat->psstat_kstat_ok = 0; + } + + /* read the overtemp kstat for the system control board */ + /* Try to find an overtemp kstat instance for this board */ + ksp = kstat_lookup(kc, UNIX, CLOCK_BOARD_INDEX, OVERTEMP_KSTAT_NAME); + + if (ksp != NULL) { + if (kstat_read(kc, ksp, NULL) == -1) { + sys_kstat->temp_kstat_ok = 0; + } else { + sys_kstat->temp_kstat_ok = 1; + (void) memcpy(&sys_kstat->tempstat, ksp->ks_data, + sizeof (sys_kstat->tempstat)); + /* XXX - this is for 2.5.1 testing. remove */ + if (sizeof (sys_kstat->tempstat) > ksp->ks_data_size) + sys_kstat->tempstat.trend = TREND_UNKNOWN; + } + } + + /* Read the reset-info kstat from one of the boards. */ + ksp = kstat_lookup(kc, UNIX, 0, RESETINFO_KSTAT_NAME); + + if (ksp == NULL) { + sys_kstat->reset_kstats_ok = 0; + } else if (kstat_read(kc, ksp, NULL) == -1) { + sys_kstat->reset_kstats_ok = 0; + } else { + sys_kstat->reset_kstats_ok = 1; + (void) memcpy(&sys_kstat->reset_info, ksp->ks_data, + sizeof (sys_kstat->reset_info)); + } + + /* read kstats for hotplugged boards */ + for (i = 0, hp = &sys_kstat->hp_info[0]; i < MAX_BOARDS; i++, hp++) { + ksp = kstat_lookup(kc, UNIX, i, BDLIST_KSTAT_NAME); + + if (ksp == NULL) { + continue; + } + + if (kstat_read(kc, ksp, NULL) == -1) { + hp->kstat_ok = 0; + } else { + hp->kstat_ok = 1; + (void) memcpy(&hp->bd_info, ksp->ks_data, + sizeof (hp->bd_info)); + } + } + + /* read in the kstat for the fault list. */ + ksp = kstat_lookup(kc, UNIX, 0, FT_LIST_KSTAT_NAME); + + if (ksp == NULL) { + sys_kstat->ft_kstat_ok = 0; + } else { + if (kstat_read(kc, ksp, NULL) == -1) { + perror("kstat read"); + sys_kstat->ft_kstat_ok = 0; + return; + } + + sys_kstat->nfaults = ksp->ks_data_size / + sizeof (struct ft_list); + + sys_kstat->ft_array = + (struct ft_list *)malloc(ksp->ks_data_size); + + if (sys_kstat->ft_array == NULL) { + perror("Malloc"); + exit(2); + } + sys_kstat->ft_kstat_ok = 1; + (void) memcpy(sys_kstat->ft_array, ksp->ks_data, + ksp->ks_data_size); + } +} + +/* + * This function does the reading and interpreting of sun4u system + * kstats. These kstats are created by the following drivers: + * fhc, environ, sysctrl. Each board in the tree should have + * kstats created for it. There are also system wide kstats that + * are created. + */ +void +read_sun4u_kstats(Sys_tree *tree, struct system_kstat_data *sys_kstat) +{ +#if 0 + Board_node *bnode; + kstat_t *ksp; + kstat_named_t *knp; + struct hp_info *hp; + struct envctrltwo_kstat_data *ecp; +#endif + kstat_ctl_t *kc; + int i; + struct bd_kstat_data *bdp; + struct envctrl_kstat_data *ep; + + if ((kc = kstat_open()) == NULL) { + return; + } +#ifdef lint + kc = kc; +#endif + + /* Initialize the kstats structure */ + sys_kstat->sys_kstats_ok = 0; + sys_kstat->temp_kstat_ok = 0; + sys_kstat->reset_kstats_ok = 0; + sys_kstat->ft_kstat_ok = 0; + sys_kstat->envctrl_kstat_ok = 0; + for (i = 0; i < MAX_BOARDS; i++) { + bdp = &sys_kstat->bd_ksp_list[i]; + bdp->ac_kstats_ok = 0; + bdp->fhc_kstats_ok = 0; + bdp->simmstat_kstats_ok = 0; + bdp->temp_kstat_ok = 0; + + sys_kstat->hp_info[i].kstat_ok = 0; + } + for (i = 0; i < MAX_DEVS; i++) { + ep = &sys_kstat->env_data; + ep->ps_kstats[i].instance = I2C_NODEV; + ep->fan_kstats[i].instance = I2C_NODEV; + ep->encl_kstats[i].instance = I2C_NODEV; + } + + read_platform_kstats(tree, sys_kstat, bdp, ep); +} diff --git a/usr/src/lib/libprtdiag/common/libdevinfo_sun4u.c b/usr/src/lib/libprtdiag/common/libdevinfo_sun4u.c new file mode 100644 index 0000000000..c7d73f5445 --- /dev/null +++ b/usr/src/lib/libprtdiag/common/libdevinfo_sun4u.c @@ -0,0 +1,479 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/systeminfo.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <libdevinfo.h> + +#include "pdevinfo.h" +#include "pdevinfo_sun4u.h" +#include "display.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +/* + * This file contains the functions that are to be called when + * a platform wants to use libdevinfo for it's device information + * instead of OBP. This will allow prtdiag to support hot-plug + * events on platforms whose OBP doesn't get updated to reflect + * the hot-plug changes to the system. + */ + +int do_devinfo(int syserrlog, char *pgname, int log_flag, + int prt_flag); +static void dump_di_node(Prom_node *pnode, di_node_t di_node); +static Prom_node *walk_di_tree(Sys_tree *tree, Prom_node *root, + di_node_t di_node); +static int match_compatible_name(char *, int, char *); + +/* + * Global variables + */ +di_prom_handle_t ph; /* Handle for using di_prom interface */ +extern char *progname; + + + +/* + * Used instead of the walk() function when a platform wants to + * walk libdevinfo's device tree instead of walking OBP's + * device tree. + */ +static Prom_node* +walk_di_tree(Sys_tree *tree, Prom_node *root, di_node_t di_node) +{ + di_node_t curnode; + Prom_node *pnode; + char *name, *type, *model, *compatible_array; + int board_node = 0; + int *int_val; + int portid; + int is_schizo = 0, n_names; + + /* allocate a node for this level */ + if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) == + NULL) { + perror("malloc"); + exit(2); + } + + /* assign parent Prom_node */ + pnode->parent = root; + pnode->sibling = NULL; + pnode->child = NULL; + + /* read properties for this node */ + dump_di_node(pnode, di_node); + + name = get_node_name(pnode); + type = get_node_type(pnode); + if (type == NULL) + type = ""; + model = (char *)get_prop_val(find_prop(pnode, "model")); + if (model == NULL) + model = ""; + + /* + * For identifying Schizo nodes we need to check if the + * compatible property contains the string 'pci108e,8001'. + * This property contains an array of strings so we need + * search all strings. + */ + if ((n_names = di_compatible_names(di_node, &compatible_array)) > 0) { + if (match_compatible_name(compatible_array, n_names, + "pci108e,8001")) + is_schizo = 1; + } + + if (int_val = (int *)get_prop_val(find_prop(pnode, "portid"))) + portid = *int_val; + else if ((strcmp(type, "cpu") == 0) && + (int_val = (int *)get_prop_val(find_prop(pnode->parent, "portid")))) + portid = *int_val; + else + portid = -1; + +#ifdef DEBUG + if (name != NULL) + printf("name=%s\n", name); + if (type != NULL) + printf("type=%s\n", type); + if (model != NULL) + printf("model=%s\n", model); + printf("portid=%d\n", portid); +#endif + + if (name != NULL) { + if (has_board_num(pnode)) { + add_node(tree, pnode); + board_node = 1; + D_PRINTF("\n---\nnodename = %s [ %s ] \n", + di_node_name(di_node), di_devfs_path(di_node)); + D_PRINTF("ADDED BOARD name=%s type=%s model=%s " + "portid =%d\n", name, type, model, portid); + } else if ((strcmp(name, FFB_NAME) == 0) || + (strcmp(type, "cpu") == 0) || + + ((strcmp(type, "memory-controller") == 0) && + (strcmp(name, "ac") != 0)) || + + ((strcmp(name, "pci") == 0) && + (strcmp(model, "SUNW,psycho") == 0)) || + + ((strcmp(name, "pci") == 0) && + (strcmp(model, "SUNW,sabre") == 0)) || + + ((strcmp(name, "pci") == 0) && (is_schizo)) || + + (strcmp(name, "counter-timer") == 0) || + (strcmp(name, "sbus") == 0)) { + add_node(tree, pnode); + board_node = 1; + D_PRINTF("\n---\nnodename = %s [ %s ] \n", + di_node_name(di_node), di_devfs_path(di_node)); + D_PRINTF("ADDED BOARD name=%s type=%s model=%s\n", + name, type, model); + } + } else { + D_PRINTF("node not added: type=%s portid =%d\n", type, portid); + } + + if (curnode = di_child_node(di_node)) { + pnode->child = walk_di_tree(tree, pnode, curnode); + } + + if (curnode = di_sibling_node(di_node)) { + if (board_node) { + return (walk_di_tree(tree, root, curnode)); + } else { + pnode->sibling = walk_di_tree(tree, root, curnode); + } + } + + /* + * This check is needed in case the "board node" occurs at the + * end of the sibling chain as opposed to the middle or front. + */ + if (board_node) + return (NULL); + + return (pnode); +} + +/* + * Dump all the devinfo properties and then the obp properties for + * the specified devinfo node into the Prom_node structure. + */ +static void +dump_di_node(Prom_node *pnode, di_node_t di_node) +{ + Prop *prop = NULL; /* tail of properties list */ + + Prop *temp; /* newly allocated property */ + di_prop_t di_prop; + di_prom_prop_t p_prop; + int retval = 0; + int i; + + /* clear out pointers in pnode */ + pnode->props = NULL; + + D_PRINTF("\n\n ------- Dumping devinfo properties for node ------\n"); + + /* + * get all the devinfo properties first + */ + for (di_prop = di_prop_next(di_node, DI_PROP_NIL); + di_prop != DI_PROP_NIL; + di_prop = di_prop_next(di_node, di_prop)) { + + char *di_name; + void *di_data; + int di_ptype; + + di_name = di_prop_name(di_prop); + if (di_name == (char *)NULL) + continue; + + di_ptype = di_prop_type(di_prop); + D_PRINTF("DEVINFO Properties %s: ", di_name); + + switch (di_ptype) { + int *int_val; + char *char_val; + case DI_PROP_TYPE_INT: + retval = di_prop_lookup_ints(DDI_DEV_T_ANY, + di_node, di_name, (int **)&di_data); + if (retval > 0) { + int_val = (int *)di_data; + D_PRINTF("0x%x\n", *int_val); + } + break; + case DI_PROP_TYPE_STRING: + retval = di_prop_lookup_strings(DDI_DEV_T_ANY, + di_node, di_name, (char **)&di_data); + if (retval > 0) { + char_val = (char *)di_data; + D_PRINTF("%s\n", char_val); + } + break; + case DI_PROP_TYPE_BYTE: + retval = di_prop_lookup_bytes(DDI_DEV_T_ANY, + di_node, di_name, (uchar_t **)&di_data); + if (retval > 0) { + char_val = (char *)di_data; + D_PRINTF("%s\n", char_val); + } + break; + case DI_PROP_TYPE_UNKNOWN: + retval = di_prop_lookup_bytes(DDI_DEV_T_ANY, + di_node, di_name, (uchar_t **)&di_data); + if (retval > 0) { + char_val = (char *)di_data; + D_PRINTF("%s\n", char_val); + } + break; + case DI_PROP_TYPE_BOOLEAN: + di_data = NULL; + retval = 1; + break; + default: + D_PRINTF(" Skipping property\n"); + retval = -1; + } + + if (retval <= 0) + continue; + + /* allocate space for the property */ + if ((temp = (Prop *) malloc(sizeof (Prop))) == NULL) { + perror("malloc"); + exit(1); + } + + /* + * Given that we're using libdevinfo rather than OBP, + * the chances are that future accesses to di_name and + * di_data will be via temp->name.val_ptr and + * temp->value.val_ptr respectively. However, this may + * not be the case, so we have to suitably fill in + * temp->name.opp and temp->value.opp. + * + * di_name is char * and non-NULL if we've made it to + * here, so we can simply point + * temp->name.opp.oprom_array to temp->name.val_ptr. + * + * di_data could be NULL, char * or int * at this point. + * If it's non-NULL, a 1st char of '\0' indicates int *. + * We thus set temp->value.opp.oprom_node[] (although + * interest in any element other than 0 is rare, all + * elements must be set to ensure compatibility with + * OBP), and holds_array is set to 0. + * + * If di_data is NULL, or the 1st char is not '\0', we set + * temp->value.opp.oprom_array. If di_ptype is + * DI_PROP_TYPE_BOOLEAN, holds_array is set to 0, else it + * is set to 1. + */ + temp->name.val_ptr = (void *)di_name; + temp->name.opp.oprom_array = temp->name.val_ptr; + temp->name.opp.holds_array = 1; + + temp->value.val_ptr = (void *)di_data; + if ((di_data != NULL) && (*((char *)di_data) == '\0')) { + for (i = 0; i < OPROM_NODE_SIZE; i++) + temp->value.opp.oprom_node[i] = *((int *)di_data+i); + + temp->value.opp.holds_array = 0; + } else { + temp->value.opp.oprom_array = temp->value.val_ptr; + if (di_ptype == DI_PROP_TYPE_BOOLEAN) + temp->value.opp.holds_array = 0; + else + temp->value.opp.holds_array = 1; + } + + temp->size = retval; + + /* everything worked so link the property list */ + if (pnode->props == NULL) + pnode->props = temp; + else if (prop != NULL) + prop->next = temp; + prop = temp; + prop->next = NULL; + } + + /* + * Then get all the OBP properties. + */ + for (p_prop = di_prom_prop_next(ph, di_node, DI_PROM_PROP_NIL); + p_prop != DI_PROM_PROP_NIL; + p_prop = di_prom_prop_next(ph, di_node, p_prop)) { + + char *p_name; + unsigned char *p_data; + + p_name = di_prom_prop_name(p_prop); + if (p_name == (char *)NULL) + retval = -1; + else + retval = di_prom_prop_data(p_prop, &p_data); + + if (retval <= 0) + continue; + + /* allocate space for the property */ + if ((temp = (Prop *) malloc(sizeof (Prop))) == NULL) { + perror("malloc"); + exit(1); + } + + /* + * As above, p_name is char * and non-NULL if we've made + * it to here, so we can simply point + * temp->name.opp.oprom_array to temp->name.val_ptr. + * + * p_data could be NULL, a character or a number at this + * point. If it's non-NULL, a 1st char of '\0' indicates a + * number, so we set temp->value.opp.oprom_node[] (again + * setting every element to ensure OBP compatibility). + * These assignments create a lint error, hence the LINTED + * comment. + * + * If p_data is NULL, or the 1st char is not '\0', we set + * temp->value.opp.oprom_array. + */ + temp->name.val_ptr = (void *)p_name; + temp->name.opp.oprom_array = temp->name.val_ptr; + temp->name.opp.holds_array = 1; + + temp->value.val_ptr = (void *)p_data; + if ((p_data != NULL) && (*p_data == '\0')) { + for (i = 0; i < OPROM_NODE_SIZE; i++) + /* LINTED */ + temp->value.opp.oprom_node[i] = *((int *)p_data+i); + + temp->value.opp.holds_array = 0; + } else { + temp->value.opp.oprom_array = temp->value.val_ptr; + temp->value.opp.holds_array = 1; + } + + temp->size = retval; + + /* everything worked so link the property list */ + if (pnode->props == NULL) { + pnode->props = temp; + } else if (prop != NULL) { + prop->next = temp; + } + prop = temp; + prop->next = NULL; + } +} + +/* + * Used in place of do_prominfo() when a platform wants to use + * libdevinfo for getting the device tree instead of OBP. + */ +int +do_devinfo(int syserrlog, char *pgname, int log_flag, int prt_flag) +{ + Sys_tree sys_tree; /* system information */ + Prom_node *root_node; /* root node of OBP device tree */ + di_node_t di_root_node; /* root of the devinfo tree */ + struct system_kstat_data sys_kstat; /* kstats for non-OBP data */ + int retval = -1; + + /* set the global flags */ + progname = pgname; + logging = log_flag; + print_flag = prt_flag; + + /* set the the system tree fields */ + sys_tree.sys_mem = NULL; + sys_tree.boards = NULL; + sys_tree.bd_list = NULL; + sys_tree.board_cnt = 0; + + /* + * create a snapshot of the kernel device tree + * and return a handle to it. + */ + if ((di_root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { + exit(_error("di_init() failed")); + } + + /* + * create a handle to the PROM device tree. + */ + if ((ph = di_prom_init()) == NULL) { + exit(_error("di_prom_init() failed")); + } + + /* + * walk the devinfo tree and build up a list of all + * nodes and properties. + */ + root_node = walk_di_tree(&sys_tree, NULL, di_root_node); + + /* resolve the board types now */ + resolve_board_types(&sys_tree); + + read_sun4u_kstats(&sys_tree, &sys_kstat); + retval = display(&sys_tree, root_node, &sys_kstat, syserrlog); + + di_fini(di_root_node); + di_prom_fini(ph); + return (retval); +} + +/* + * check to see if the name shows up in the compatible array + */ +static int +match_compatible_name(char *compatible_array, int n_names, char *name) +{ + int i, ret = 0; + + /* parse the compatible list */ + for (i = 0; i < n_names; i++) { + if (strcmp(compatible_array, name) == 0) { + ret = 1; + break; + } + compatible_array += strlen(compatible_array) + 1; + } + return (ret); +} diff --git a/usr/src/lib/libprtdiag/common/llib-lprtdiag b/usr/src/lib/libprtdiag/common/llib-lprtdiag new file mode 100644 index 0000000000..5c7ac4ce3b --- /dev/null +++ b/usr/src/lib/libprtdiag/common/llib-lprtdiag @@ -0,0 +1,33 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <sys/types.h> +#include "libprtdiag.h" diff --git a/usr/src/lib/libprtdiag/common/memory.c b/usr/src/lib/libprtdiag/common/memory.c new file mode 100644 index 0000000000..1e1aaff425 --- /dev/null +++ b/usr/src/lib/libprtdiag/common/memory.c @@ -0,0 +1,541 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include <sys/sbd_ioctl.h> +#include <sys/sbdp_mem.h> +#include <sys/serengeti.h> +#include <sys/mc.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +#define KBYTE 1024 +#define MBYTE (KBYTE * KBYTE) + +#define MEM_UK_SIZE_MASK 0x3FF + +/* + * Global variables. + */ +static memory_bank_t *bank_head; +static memory_bank_t *bank_tail; +static memory_seg_t *seg_head; + +/* + * Local functions. + */ +static void add_bank_node(uint64_t mc_decode, int portid, char *bank_status); +static void add_seg_node(void); +static memory_seg_t *match_seg(uint64_t); + + +/* + * Used for US-I and US-II systems + */ +/*ARGSUSED0*/ +void +display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats, + struct grp_info *grps, struct mem_total *memory_total) +{ + log_printf(dgettext(TEXT_DOMAIN, "Memory size: "), 0); + + if (sysconf(_SC_PAGESIZE) == -1 || sysconf(_SC_PHYS_PAGES) == -1) + log_printf(dgettext(TEXT_DOMAIN, "unable to determine\n"), 0); + else { + uint64_t mem_size; + + mem_size = + (uint64_t)sysconf(_SC_PAGESIZE) * \ + (uint64_t)sysconf(_SC_PHYS_PAGES); + + if (mem_size >= MBYTE) + log_printf(dgettext(TEXT_DOMAIN, "%d Megabytes\n"), + (int)((mem_size+MBYTE-1) / MBYTE), 0); + else + log_printf(dgettext(TEXT_DOMAIN, "%d Kilobytes\n"), + (int)((mem_size+KBYTE-1) / KBYTE), 0); + } +} + +/*ARGSUSED0*/ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + /* + * This function is intentionally blank + */ +} + +/* + * The following functions are for use by any US-III based systems. + * All they need to do is to call get_us3_mem_regs() + * and then display_us3_banks(). Each platform then needs to decide how + * to format this data by over-riding the generic function + * print_us3_memory_line(). + */ +int +get_us3_mem_regs(Board_node *bnode) +{ + Prom_node *pnode; + int portid; + uint64_t *ma_reg_arr; + uint64_t madr[NUM_MBANKS_PER_MC]; + void *bank_status_array; + char *bank_status; + int i, status_offset; + + for (pnode = dev_find_node(bnode->nodes, "memory-controller"); + pnode != NULL; + pnode = dev_next_node(pnode, "memory-controller")) { + + /* Get portid of this mc from libdevinfo. */ + portid = (*(int *)get_prop_val(find_prop(pnode, "portid"))); + + /* read the logical_bank_ma_regs property for this mc node. */ + ma_reg_arr = (uint64_t *)get_prop_val( + find_prop(pnode, MEM_CFG_PROP_NAME)); + + /* + * There are situations where a memory-controller node + * will not have the logical_bank_ma_regs property and + * we need to allow for these cases. They include: + * - Excalibur/Littleneck systems that only + * support memory on one of their CPUs. + * - Systems that support DR where a cpu board + * can be unconfigured but still connected. + * It is up to the caller of this function to ensure + * that the bank_head and seg_head pointers are not + * NULL after processing all memory-controllers in the + * system. This would indicate a situation where no + * memory-controllers in the system have a logical_bank_ma_regs + * property which should never happen. + */ + if (ma_reg_arr == NULL) + continue; + + /* + * The first NUM_MBANKS_PER_MC of uint64_t's in the + * logical_bank_ma_regs property are the madr values. + */ + for (i = 0; i < NUM_MBANKS_PER_MC; i++) { + madr[i] = *ma_reg_arr++; + } + + /* + * Get the bank_status property for this mem controller from + * OBP. This contains the bank-status for each logical bank. + */ + bank_status_array = (void *)get_prop_val( + find_prop(pnode, "bank-status")); + status_offset = 0; + + /* + * process each logical bank + */ + for (i = 0; i < NUM_MBANKS_PER_MC; i++) { + /* + * Get the bank-status string for this bank + * from the bank_status_array we just retrieved + * from OBP. If the prop was not found, we + * malloc a bank_status and set it to "no_status". + */ + if (bank_status_array) { + bank_status = ((char *)bank_status_array + + status_offset); + + /* Move offset to next bank_status string */ + status_offset += (strlen(bank_status) + 1); + } else { + bank_status = malloc(strlen("no_status")); + strcpy(bank_status, "no_status"); + } + + /* + * create a bank_node for this bank + * and add it to the list. + */ + add_bank_node(madr[i], portid, bank_status); + + /* + * find the segment to which this bank + * belongs. If it doesn't already exist + * then create it. If it exists, add to it. + */ + add_seg_node(); + } + } + return (0); +} + +static void +add_bank_node(uint64_t mc_decode, int portid, char *bank_status) +{ + static int id = 0; + memory_bank_t *new, *bank; + uint32_t ifactor = MC_INTLV(mc_decode); + uint64_t seg_size; + + if ((new = malloc(sizeof (memory_bank_t))) == NULL) { + perror("malloc"); + exit(1); + } + + new->portid = portid; + new->id = id++; + new->valid = (mc_decode >> 63); + new->uk = MC_UK(mc_decode); + new->um = MC_UM(mc_decode); + new->lk = MC_LK(mc_decode); + new->lm = MC_LM(mc_decode); + + seg_size = ((((uint64_t)new->uk & MEM_UK_SIZE_MASK) + 1) << 26); + new->bank_size = seg_size / ifactor; + new->bank_status = bank_status; + + new->next = NULL; + new->seg_next = NULL; + + /* Handle the first bank found */ + if (bank_head == NULL) { + bank_head = new; + bank_tail = new; + return; + } + + /* find last bank in list */ + bank = bank_head; + while (bank->next) + bank = bank->next; + + /* insert this bank into the list */ + bank->next = new; + bank_tail = new; +} + +void +display_us3_banks(void) +{ + uint64_t base, bank_size; + uint32_t intlv; + memory_bank_t *bank, *tmp_bank; + memory_seg_t *seg; + int mcid; + uint64_t dimm_size; + uint64_t total_bank_size = 0; + uint64_t total_sys_mem; + static uint64_t bank0_size, bank1_size, bank2_size, bank3_size; + + if ((bank_head == NULL) || (seg_head == NULL)) { + log_printf("\nCannot find any memory bank/segment info.\n"); + return; + } + + for (bank = bank_head; bank; bank = bank->next) { + /* + * Interleave factor is determined from the + * lk bits in the Mem Addr Decode register. + * + * The Base Address of the memory segment in which this + * bank belongs is determined from the um abd uk bits + * of the Mem Addr Decode register. + * + * See section 9.1.5 of Cheetah Programmer's reference + * manual. + */ + intlv = ((bank->lk ^ 0xF) + 1); + base = bank->um & ~(bank->uk); + + mcid = SG_PORTID_TO_SAFARI_ID(bank->portid); + + /* If bank is not valid, set size to zero incase it's garbage */ + if (bank->valid) + bank_size = ((bank->bank_size) / MBYTE); + else + bank_size = 0; + + /* + * Keep track of all banks found so we can check later + * that this value matches the total memory in the + * system using the pagesize and number of pages. + */ + total_bank_size += bank_size; + + /* Find the matching segment for this bank. */ + seg = match_seg(base); + + /* + * Find the Dimm size by adding banks 0 + 2 and divide by 4 + * and then adding banks 1 + 3 and divide by 4. We divide + * by 2 if one of the logical banks size is zero. + */ + switch ((bank->id) % 4) { + case 0: + /* have bank0_size, need bank2_size */ + bank0_size = bank_size; + bank2_size = 0; + + tmp_bank = bank->next; + while (tmp_bank) { + if (tmp_bank->valid == 0) { + tmp_bank = tmp_bank->next; + continue; + } + /* Is next bank on the same mc ? */ + if (mcid != SG_PORTID_TO_SAFARI_ID( + tmp_bank->portid)) { + break; + } + if ((tmp_bank->id) % 4 == 2) { + bank2_size = + (tmp_bank->bank_size / MBYTE); + break; + } + tmp_bank = tmp_bank->next; + } + if (bank2_size) + dimm_size = (bank0_size + bank2_size) / 4; + else + dimm_size = bank0_size / 2; + break; + case 1: + /* have bank1_size, need bank3_size */ + bank1_size = bank_size; + bank3_size = 0; + + tmp_bank = bank->next; + while (tmp_bank) { + if (tmp_bank->valid == 0) { + tmp_bank = tmp_bank->next; + continue; + } + /* Is next bank on the same mc ? */ + if (mcid != SG_PORTID_TO_SAFARI_ID( + tmp_bank->portid)) { + break; + } + if ((tmp_bank->id) % 4 == 3) { + bank3_size = + (tmp_bank->bank_size / MBYTE); + break; + } + tmp_bank = tmp_bank->next; + } + if (bank3_size) + dimm_size = (bank1_size + bank3_size) / 4; + else + dimm_size = bank1_size / 2; + break; + case 2: + /* have bank0_size and bank2_size */ + bank2_size = bank_size; + if (bank0_size) + dimm_size = (bank0_size + bank2_size) / 4; + else + dimm_size = bank2_size / 2; + break; + case 3: + /* have bank1_size and bank3_size */ + bank3_size = bank_size; + if (bank1_size) + dimm_size = (bank1_size + bank3_size) / 4; + else + dimm_size = bank3_size / 4; + break; + } + + if (bank->valid == 0) + continue; + + /* + * Call platform specific code for formatting memory + * information. + */ + print_us3_memory_line(bank->portid, bank->id, bank_size, + bank->bank_status, dimm_size, intlv, seg->id); + } + + printf("\n"); + + /* + * Sanity check to ensure that the total amount of system + * memory matches the total number of memory banks that + * we find here. Scream if there is a mis-match. + */ + total_sys_mem = (((uint64_t)sysconf(_SC_PAGESIZE) * \ + (uint64_t)sysconf(_SC_PHYS_PAGES)) / MBYTE); + + if (total_bank_size != total_sys_mem) { + log_printf(dgettext(TEXT_DOMAIN, + "\nError: total bank size [%lldMB] does not match total " + "system memory [%lldMB]\n"), total_bank_size, + total_sys_mem, 0); + } + +} + +static void +add_seg_node(void) +{ + uint64_t base; + memory_seg_t *new; + static int id = 0; + memory_bank_t *bank = bank_tail; + + if (bank->valid != 1) + return; + + base = bank->um & ~(bank->uk); + + if ((new = match_seg(base)) == NULL) { + /* + * This bank is part of a new segment, so create + * a struct for it and added to the list of segments + */ + if ((new = malloc(sizeof (memory_seg_t))) == NULL) { + perror("malloc"); + exit(1); + } + new->id = id++; + new->base = base; + new->size = (((uint64_t)bank->uk +1) << 26); + new->intlv = ((bank->lk ^ 0xF) + 1); + + /* + * add to the seg list + */ + new->next = seg_head; + seg_head = new; + } + + new->nbanks++; + /* + * add bank into segs bank list. Note we add at the head + */ + bank->seg_next = new->banks; + new->banks = bank; +} + +static memory_seg_t * +match_seg(uint64_t base) +{ + memory_seg_t *cur_seg; + + for (cur_seg = seg_head; cur_seg; cur_seg = cur_seg->next) { + if (cur_seg-> base == base) + break; + } + return (cur_seg); +} + +/*ARGSUSED0*/ +void +print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, + char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id) +{ + log_printf(dgettext(TEXT_DOMAIN, + "\n No print_us3_memory_line() function specified for" + " this platform\n"), 0); +} + +int +display_us3_failed_banks(int system_failed) +{ + memory_bank_t *bank; + int found_failed_bank = 0; + + if ((bank_head == NULL) || (seg_head == NULL)) { + log_printf("\nCannot find any memory bank/segment info.\n"); + return (1); + } + + for (bank = bank_head; bank; bank = bank->next) { + /* + * check to see if the bank is invalid and also + * check if the bank_status is unpopulated. Unpopulated + * means the bank is empty. + */ + + if ((bank->valid == 0) && + (strcmp(bank->bank_status, "unpopulated"))) { + if (!system_failed && !found_failed_bank) { + found_failed_bank = TRUE; + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, + "Failed Field Replaceable Units (FRU) in " + "System:\n"), 0); + log_printf("==========================" + "====================\n", 0); + } + /* + * Call platform specific code for formatting memory + * information. + */ + print_us3_failed_memory_line(bank->portid, bank->id, + bank->bank_status); + } + } + if (found_failed_bank) + return (1); + else + return (0); +} + +/*ARGSUSED0*/ +void +print_us3_failed_memory_line(int portid, int bank_id, char *bank_status) +{ + log_printf(dgettext(TEXT_DOMAIN, + "\n No print_us3_failed_memory_line() function specified for" + " this platform\n"), 0); +} diff --git a/usr/src/lib/libprtdiag/common/pdevinfo_funcs.c b/usr/src/lib/libprtdiag/common/pdevinfo_funcs.c new file mode 100644 index 0000000000..ce52634405 --- /dev/null +++ b/usr/src/lib/libprtdiag/common/pdevinfo_funcs.c @@ -0,0 +1,833 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <stdarg.h> +#include <errno.h> +#include <unistd.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <libintl.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" + +/* + * For machines that support the openprom, fetch and print the list + * of devices that the kernel has fetched from the prom or conjured up. + * + */ + + +static int prom_fd; +extern char *progname; +extern char *promdev; +extern void getppdata(); +extern void printppdata(); + +/* + * Define DPRINT for run-time debugging printf's... + * #define DPRINT 1 + */ + +#ifdef DPRINT +static char vdebug_flag = 1; +#define dprintf if (vdebug_flag) printf +static void dprint_dev_info(caddr_t, dev_info_t *); +#endif /* DPRINT */ + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +/*VARARGS1*/ +int +_error(char *fmt, ...) +{ + int saved_errno; + va_list ap; + extern int errno; + saved_errno = errno; + + if (progname) + (void) fprintf(stderr, "%s: ", progname); + + va_start(ap, fmt); + + (void) vfprintf(stderr, fmt, ap); + + va_end(ap); + + (void) fprintf(stderr, ": "); + errno = saved_errno; + perror(""); + + return (2); +} + +int +is_openprom(void) +{ + Oppbuf oppbuf; + register struct openpromio *opp = &(oppbuf.opp); + register unsigned int i; + + opp->oprom_size = MAXVALSIZE; + if (ioctl(prom_fd, OPROMGETCONS, opp) < 0) + exit(_error("OPROMGETCONS")); + + i = (unsigned int)((unsigned char)opp->oprom_array[0]); + return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM); +} + +/* + * Read all properties and values from nodes. + * Copy the properties read into the prom_node passsed in. + */ +void +dump_node(Prom_node *node) +{ + Oppbuf oppbuf; + register struct openpromio *opp = &oppbuf.opp; + Prop *prop = NULL; /* tail of properties list */ + StaticProp *temp; + + /* clear out pointers in pnode */ + node->props = NULL; + + /* get first prop by asking for null string */ + (void) memset((void *) oppbuf.buf, 0, BUFSIZE); + + /* allocate space for the property */ + if ((temp = malloc(sizeof (StaticProp))) == NULL) { + perror("malloc"); + exit(1); + } + + opp->oprom_size = MAXPROPSIZE; + while (opp->oprom_size != 0) { + Prop *new; + int i; + char *tempp, *newp; + + /* + * get property + */ + opp->oprom_size = MAXPROPSIZE; + + if (ioctl(prom_fd, OPROMNXTPROP, opp) < 0) + exit(_error("OPROMNXTPROP")); + + if (opp->oprom_size != 0) { + temp->name.opp.oprom_size = opp->oprom_size; + (void) strcpy(temp->name.opp.oprom_array, + opp->oprom_array); + + (void) strcpy(temp->value.opp.oprom_array, + temp->name.opp.oprom_array); + getpropval(&temp->value.opp); + temp->size = temp->value.opp.oprom_size; + + /* Now copy over temp's data to new. */ + if ((new = malloc(sizeof (Prop))) == NULL) { + perror("malloc"); + exit(1); + } + + /* + * First copy over temp->name's data. The + * temp->name.opp.opio_u union always contains char[] + * (as opposed to an int or int []). + */ + new->name.opp.oprom_size = temp->name.opp.oprom_size; + + if ((new->name.opp.oprom_array = + malloc(new->name.opp.oprom_size)) == NULL) { + perror("malloc"); + exit(1); + } + (void) strcpy(new->name.opp.oprom_array, + temp->name.opp.oprom_array); + + new->name.opp.holds_array = 1; + + /* + * Then copy over temp->value's data. + * temp->value.opp.opio_u could contain char[], int or + * int []. If *(temp->value.opp.oprom_array) is '\0', + * this indicates int or int []. int is the norm, but + * to be safe we assume int [] and copy over + * OPROM_NODE_SIZE int elements. + */ + new->value.opp.oprom_size = temp->value.opp.oprom_size; + + if (*(temp->value.opp.oprom_array) == '\0') { + for (i = 0; i < OPROM_NODE_SIZE; i++) + new->value.opp.oprom_node[i] = + *(&temp->value.opp.oprom_node+i); + + new->value.opp.holds_array = 0; + } else { + if ((new->value.opp.oprom_array = + malloc(new->value.opp.oprom_size)) + == NULL) { + perror("malloc"); + exit(1); + } + + /* + * temp->value.opp.oprom_array can contain one + * or more embedded NULLs. These trip-up the + * standard string copying functions, so we do + * the copy by hand. temp->value.opp.oprom_array + * will be NULL-terminated. oprom_size includes + * this terminating NULL. + */ + newp = new->value.opp.oprom_array; + tempp = temp->value.opp.oprom_array; + for (i = new->value.opp.oprom_size; i > 0; i--) + *newp++ = *tempp++; + + new->value.opp.holds_array = 1; + } + + new->size = temp->size; + + /* everything worked so link the property list */ + if (node->props == NULL) + node->props = new; + else if (prop != NULL) + prop->next = new; + prop = new; + prop->next = NULL; + } + } + free(temp); +} + +int +promopen(int oflag) +{ + /*CONSTCOND*/ + while (1) { + if ((prom_fd = open(promdev, oflag)) < 0) { + if (errno == EAGAIN) { + (void) sleep(5); + continue; + } + if (errno == ENXIO) + return (-1); + exit(_error(dgettext(TEXT_DOMAIN, "cannot open %s"), + promdev)); + } else + return (0); + } + /*NOTREACHED*/ +} + +void +promclose(void) +{ + if (close(prom_fd) < 0) + exit(_error(dgettext(TEXT_DOMAIN, "close error on %s"), + promdev)); +} + +/* + * Read the value of the property from the PROM device tree + */ +void +getpropval(struct openpromio *opp) +{ + opp->oprom_size = MAXVALSIZE; + + if (ioctl(prom_fd, OPROMGETPROP, opp) < 0) + exit(_error("OPROMGETPROP")); +} + +int +next(int id) +{ + Oppbuf oppbuf; + register struct openpromio *opp = &(oppbuf.opp); + /* LINTED */ + int *ip = (int *)(opp->oprom_array); + + (void) memset((void *) oppbuf.buf, 0, BUFSIZE); + + opp->oprom_size = MAXVALSIZE; + *ip = id; + if (ioctl(prom_fd, OPROMNEXT, opp) < 0) + return (_error("OPROMNEXT")); + /* LINTED */ + return (*(int *)opp->oprom_array); +} + +int +child(int id) +{ + Oppbuf oppbuf; + register struct openpromio *opp = &(oppbuf.opp); + /* LINTED */ + int *ip = (int *)(opp->oprom_array); + + (void) memset((void *) oppbuf.buf, 0, BUFSIZE); + opp->oprom_size = MAXVALSIZE; + *ip = id; + if (ioctl(prom_fd, OPROMCHILD, opp) < 0) + return (_error("OPROMCHILD")); + /* LINTED */ + return (*(int *)opp->oprom_array); +} + +/* + * Check if the Prom node passed in contains a property called + * "board#". + */ +int +has_board_num(Prom_node *node) +{ + Prop *prop = node->props; + + /* + * walk thru all properties in this PROM node and look for + * board# prop + */ + while (prop != NULL) { + if (strcmp(prop->name.opp.oprom_array, "board#") == 0) + return (1); + + prop = prop->next; + } + + return (0); +} /* end of has_board_num() */ + +/* + * Retrieve the value of the board number property from this Prom + * node. It has the type of int. + */ +int +get_board_num(Prom_node *node) +{ + Prop *prop = node->props; + + /* + * walk thru all properties in this PROM node and look for + * board# prop + */ + while (prop != NULL) { + if (strcmp(prop->name.opp.oprom_array, "board#") == 0) + return (prop->value.opp.oprom_node[0]); + + prop = prop->next; + } + + return (-1); +} /* end of get_board_num() */ + +/* + * Find the requested board struct in the system device tree. + */ +Board_node * +find_board(Sys_tree *root, int board) +{ + Board_node *bnode = root->bd_list; + + while ((bnode != NULL) && (board != bnode->board_num)) + bnode = bnode->next; + + return (bnode); +} /* end of find_board() */ + +/* + * Add a board to the system list in order. Initialize all pointer + * fields to NULL. + */ +Board_node * +insert_board(Sys_tree *root, int board) +{ + Board_node *bnode; + Board_node *temp = root->bd_list; + + if ((bnode = (Board_node *) malloc(sizeof (Board_node))) == NULL) { + perror("malloc"); + exit(1); + } + bnode->nodes = NULL; + bnode->next = NULL; + bnode->board_num = board; + + if (temp == NULL) + root->bd_list = bnode; + else if (temp->board_num > board) { + bnode->next = temp; + root->bd_list = bnode; + } else { + while ((temp->next != NULL) && (board > temp->next->board_num)) + temp = temp->next; + bnode->next = temp->next; + temp->next = bnode; + } + root->board_cnt++; + + return (bnode); +} /* end of insert_board() */ + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the name property. + */ +char * +get_node_name(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("name", prop->name.opp.oprom_array) == 0) + return (prop->value.opp.oprom_array); + prop = prop->next; + } + return (NULL); +} /* end of get_node_name() */ + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the name property. + */ +char * +get_node_type(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("device_type", prop->name.opp.oprom_array) == 0) + return (prop->value.opp.oprom_array); + prop = prop->next; + } + return (NULL); +} /* end of get_node_type() */ + +/* + * Do a depth-first walk of a device tree and + * return the first node with the name matching. + */ + +Prom_node * +dev_find_node(Prom_node *root, char *name) +{ + Prom_node *node; + + node = dev_find_node_by_type(root, "name", name); + + return (node); +} + +Prom_node * +dev_next_node(Prom_node *root, char *name) +{ + Prom_node *node; + + node = dev_next_node_by_type(root, "name", name); + + return (node); +} + +/* + * Search for and return a node of the required type. If no node is found, + * then return NULL. + */ +Prom_node * +dev_find_type(Prom_node *root, char *type) +{ + Prom_node *node; + + node = dev_find_node_by_type(root, "device_type", type); + + return (node); /* not found */ +} + +/* + * Start from the current node and return the next node besides the + * current one which has the requested type property. + */ +Prom_node * +dev_next_type(Prom_node *root, char *type) +{ + Prom_node *node; + + node = dev_next_node_by_type(root, "device_type", type); + + return (node); /* not found */ +} + +/* + * Search a device tree and return the first failed node that is found. + * (has a 'status' property) + */ +Prom_node * +find_failed_node(Prom_node * root) +{ + Prom_node *pnode; + + if (root == NULL) + return (NULL); + + if (node_failed(root)) { + return (root); + } + + /* search the child */ + if ((pnode = find_failed_node(root->child)) != NULL) + return (pnode); + + /* search the siblings */ + if ((pnode = find_failed_node(root->sibling)) != NULL) + return (pnode); + + return (NULL); +} /* end of find_failed_node() */ + +/* + * Start from the current node and return the next node besides + * the current one which is failed. (has a 'status' property) + */ +Prom_node * +next_failed_node(Prom_node * root) +{ + Prom_node *pnode; + Prom_node *parent; + + if (root == NULL) + return (NULL); + + /* search the child */ + if ((pnode = find_failed_node(root->child)) != NULL) { + return (pnode); + } + + /* search the siblings */ + if ((pnode = find_failed_node(root->sibling)) != NULL) { + return (pnode); + } + + /* backtracking the search up through parents' siblings */ + parent = root->parent; + while (parent != NULL) { + if ((pnode = find_failed_node(parent->sibling)) != NULL) + return (pnode); + else + parent = parent->parent; + } + + return (NULL); +} /* end of find_failed_node() */ + +/* + * node_failed + * + * This function determines if the current Prom node is failed. This + * is defined by having a status property containing the token 'fail'. + */ +int +node_failed(Prom_node *node) +{ + return (node_status(node, "fail")); +} + +int +node_status(Prom_node *node, char *status) +{ + void *value; + + if (status == NULL) + return (0); + + /* search the local node */ + if ((value = get_prop_val(find_prop(node, "status"))) != NULL) { + if ((value != NULL) && strstr((char *)value, status)) + return (1); + } + return (0); +} + +/* + * Get a property's value. Must be void * since the property can + * be any data type. Caller must know the *PROPER* way to use this + * data. + */ +void * +get_prop_val(Prop *prop) +{ + if (prop == NULL) + return (NULL); + + if (prop->value.opp.holds_array) + return ((void *)(prop->value.opp.oprom_array)); + else + return ((void *)(&prop->value.opp.oprom_node[0])); +} /* end of get_prop_val() */ + +/* + * Search a Prom node and retrieve the property with the correct + * name. + */ +Prop * +find_prop(Prom_node *pnode, char *name) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + if (pnode->props == NULL) { + (void) printf("%s", dgettext(TEXT_DOMAIN, "Prom node has " + "no properties\n")); + return (NULL); + } + + prop = pnode->props; + while ((prop != NULL) && (strcmp(prop->name.opp.oprom_array, name))) + prop = prop->next; + + return (prop); +} + +/* + * This function adds a board node to the board structure where that + * that node's physical component lives. + */ +void +add_node(Sys_tree *root, Prom_node *pnode) +{ + int board; + Board_node *bnode; + Prom_node *p; + + /* add this node to the Board list of the appropriate board */ + if ((board = get_board_num(pnode)) == -1) { + /* board is 0 if not on Sunfire */ + board = 0; + } + + /* find the node with the same board number */ + if ((bnode = find_board(root, board)) == NULL) { + bnode = insert_board(root, board); + bnode->board_type = UNKNOWN_BOARD; + } + + /* now attach this prom node to the board list */ + /* Insert this node at the end of the list */ + pnode->sibling = NULL; + if (bnode->nodes == NULL) + bnode->nodes = pnode; + else { + p = bnode->nodes; + while (p->sibling != NULL) + p = p->sibling; + p->sibling = pnode; + } + +} + +/* + * Find the device on the current board with the requested device ID + * and name. If this rountine is passed a NULL pointer, it simply returns + * NULL. + */ +Prom_node * +find_device(Board_node *board, int id, char *name) +{ + Prom_node *pnode; + int mask; + + /* find the first cpu node */ + pnode = dev_find_node(board->nodes, name); + + mask = 0x1F; + while (pnode != NULL) { + if ((get_id(pnode) & mask) == id) + return (pnode); + + pnode = dev_next_node(pnode, name); + } + return (NULL); +} + +Prom_node * +dev_find_node_by_type(Prom_node *root, char *type, char *property) +{ + Prom_node *node; + char *type_prop; + + if (root == NULL || property == NULL) + return (NULL); + + type_prop = (char *)get_prop_val(find_prop(root, type)); + + if (type_prop != NULL) { + if (strcmp(type_prop, property) == 0) { + return (root); + } + } + + /* look at your children first */ + if ((node = dev_find_node_by_type(root->child, type, + property)) != NULL) + return (node); + + /* now look at your siblings */ + if ((node = dev_find_node_by_type(root->sibling, type, + property)) != NULL) + return (node); + + return (NULL); /* not found */ +} + +Prom_node * +dev_next_node_by_type(Prom_node *root, char *type, char *property) +{ + Prom_node *node; + + if (root == NULL || property == NULL) + return (NULL); + + /* look at your children first */ + if ((node = dev_find_node_by_type(root->child, type, + property)) != NULL) + return (node); + + /* now look at your siblings */ + if ((node = dev_find_node_by_type(root->sibling, type, + property)) != NULL) + return (node); + + /* now look at papa's siblings */ + if ((node = dev_find_node_by_type(root->parent->sibling, + type, property)) != NULL) + return (node); + + return (NULL); /* not found */ +} + +/* + * Do a depth-first walk of a device tree and + * return the first node with the matching compatible. + */ +Prom_node * +dev_find_node_by_compatible(Prom_node *root, char *compatible) +{ + Prom_node *node; + Prop *prop; + char *compatible_array; + int size, nbytes; + + if (root == NULL || compatible == NULL) + return (NULL); + + if ((prop = find_prop(root, "compatible")) != NULL && + (compatible_array = (char *)get_prop_val(prop)) != NULL) { + /* + * The Prop structure returned by find_prop() is supposed + * to contain an indication of how big the value of the + * compatible property is. Since it is an array of strings + * this is our only means of determining just how many + * strings might be in this property. However, this size + * is often left as zero even though there is at least one + * string present. When this is the case, all we can do + * is examine the first string in the compatible property. + */ + + for (size = prop->size; size >= 0; size -= nbytes) { + if (strcmp(compatible_array, compatible) == 0) + return (root); /* found a match */ + + nbytes = strlen(compatible_array) + 1; + compatible_array += nbytes; + } + } + + node = dev_find_node_by_compatible(root->child, compatible); + if (node != NULL) + return (node); + + /* + * Note the very deliberate use of tail recursion here. A good + * compiler (such as Sun's) will recognize this and generate code + * that does not allocate another stack frame. Instead, it will + * overlay the existing stack frame with the new one, the only change + * having been to replace the original root with its sibling. + * This has the potential to create some confusion for anyone + * trying to debug this code from a core dump, since the stack + * trace will not reveal recursion on siblings, only on children. + */ + + return (dev_find_node_by_compatible(root->sibling, compatible)); +} + +/* + * Start from the current node and return the next node besides + * the current one which has the requested compatible property. + */ +Prom_node * +dev_next_node_by_compatible(Prom_node *root, char *compatible) +{ + Prom_node *node; + + if (root == NULL || compatible == NULL) + return (NULL); + + node = dev_find_node_by_compatible(root->child, compatible); + if (node != NULL) + return (node); + + /* + * More tail recursion. Even though it is a different function, + * this will overlay the current stack frame. Caveat exterminator. + */ + + node = dev_find_node_by_compatible(root->sibling, compatible); + if (node != NULL) + return (node); + + return (dev_find_node_by_compatible(root->parent->sibling, compatible)); +} diff --git a/usr/src/lib/libprtdiag/common/pdevinfo_sun4u.c b/usr/src/lib/libprtdiag/common/pdevinfo_sun4u.c new file mode 100644 index 0000000000..6a464321a5 --- /dev/null +++ b/usr/src/lib/libprtdiag/common/pdevinfo_sun4u.c @@ -0,0 +1,275 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <varargs.h> +#include <errno.h> +#include <unistd.h> +#include <sys/systeminfo.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <kstat.h> +#include <libintl.h> +#include "pdevinfo.h" +#include "pdevinfo_sun4u.h" +#include "display.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +/* + * Global variables + */ +char *progname; +char *promdev = "/dev/openprom"; +int print_flag = 1; +int logging = 0; + +/* + * This file represents the splitting out of some functionality + * of prtdiag due to the port to the sun4u platform. The PROM + * tree-walking functions which contain sun4u specifics were moved + * into this module. + */ + +extern int get_id(Prom_node *); + +/* Function prototypes */ +Prom_node *walk(Sys_tree *, Prom_node *, int); + +/* + * do_prominfo() is called from main() in usr/src/cmd/prtdiag/main.c + * + * This is the starting point for all platforms. However, this function + * can be overlayed by writing a do_prominfo() function + * in the libprtdiag_psr for a particular platform. + * + */ +int +do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag) +{ + Sys_tree sys_tree; /* system information */ + Prom_node *root_node; /* root node of OBP device tree */ + struct system_kstat_data sys_kstat; /* kstats for non-OBP data */ + + + /* set the global flags */ + progname = pgname; + logging = log_flag; + print_flag = prt_flag; + + /* set the the system tree fields */ + sys_tree.sys_mem = NULL; + sys_tree.boards = NULL; + sys_tree.bd_list = NULL; + sys_tree.board_cnt = 0; + + if (promopen(O_RDONLY)) { + exit(_error(dgettext(TEXT_DOMAIN, "openeepr device " + "open failed"))); + } + + if (is_openprom() == 0) { + (void) fprintf(stderr, "%s", + dgettext(TEXT_DOMAIN, "System architecture " + "does not support this option of this " + "command.\n")); + return (2); + } + + if (next(0) == 0) { + return (2); + } + + root_node = walk(&sys_tree, NULL, next(0)); + promclose(); + + /* resolve the board types now */ + resolve_board_types(&sys_tree); + + read_sun4u_kstats(&sys_tree, &sys_kstat); + + return (display(&sys_tree, root_node, &sys_kstat, syserrlog)); + +} + +int +get_id(Prom_node *node) +{ + int *value; + + /* + * check for upa-portid on UI and UII systems + */ + if ((value = (int *)get_prop_val(find_prop(node, "upa-portid"))) + == NULL) { + /* + * check for portid on UIII systems + */ + if ((value = (int *)get_prop_val(find_prop(node, "portid"))) + == NULL) { + return (-1); + } + } + return (*value); +} + + + +/* + * Walk the PROM device tree and build the system tree and root tree. + * Nodes that have a board number property are placed in the board + * structures for easier processing later. Child nodes are placed + * under their parents. ffb (Fusion Frame Buffer) nodes are handled + * specially, because they do not contain board number properties. + * This was requested from OBP, but was not granted. So this code + * must parse the MID of the FFB to find the board#. + * + */ +Prom_node * +walk(Sys_tree *tree, Prom_node *root, int id) +{ + register int curnode; + Prom_node *pnode; + char *name; + char *type; + char *model; + int board_node = 0; + + /* allocate a node for this level */ + if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) == + NULL) { + perror("malloc"); + exit(2); /* program errors cause exit 2 */ + } + + /* assign parent Prom_node */ + pnode->parent = root; + pnode->sibling = NULL; + pnode->child = NULL; + + /* read properties for this node */ + dump_node(pnode); + + /* + * Place a node in a 'board' if it has 'board'-ness. The definition + * is that all nodes that are children of root should have a + * board# property. But the PROM tree does not exactly follow + * this. This is where we start hacking. The name 'ffb' can + * change, so watch out for this. + * + * The UltraSPARC, sbus, pci and ffb nodes will exit in + * the desktops and will not have board# properties. These + * cases must be handled here. + * + * PCI to PCI bridges also have the name "pci", but with different + * model property values. They should not be put under 'board'. + */ + name = get_node_name(pnode); + type = get_node_type(pnode); + model = (char *)get_prop_val(find_prop(pnode, "model")); +#ifdef DEBUG + if (name != NULL) + printf("name=%s ", name); + if (type != NULL) + printf("type=%s ", type); + if (model != NULL) + printf("model=%s", model); + printf("\n"); +#endif + if (model == NULL) + model = ""; + if (type == NULL) + type = ""; + if (name != NULL) { + if (has_board_num(pnode)) { + add_node(tree, pnode); + board_node = 1; +#ifdef DEBUG + printf("ADDED BOARD name=%s type=%s model=%s\n", + name, type, model); +#endif + } else if ((strcmp(name, FFB_NAME) == 0) || + (strcmp(name, AFB_NAME) == 0) || + (strcmp(type, "cpu") == 0) || + + ((strcmp(type, "memory-controller") == 0) && + (strcmp(name, "ac") != 0)) || + + ((strcmp(name, "pci") == 0) && + (strcmp(model, "SUNW,psycho") == 0)) || + + ((strcmp(name, "pci") == 0) && + (strcmp(model, "SUNW,sabre") == 0)) || + + ((strcmp(name, "pci") == 0) && + (strcmp(model, "SUNW,schizo") == 0)) || + + ((strcmp(name, "pci") == 0) && + (strcmp(model, "SUNW,xmits") == 0)) || + + (strcmp(name, "counter-timer") == 0) || + (strcmp(name, "sbus") == 0)) { + add_node(tree, pnode); + board_node = 1; +#ifdef DEBUG + printf("ADDED BOARD name=%s type=%s model=%s\n", + name, type, model); +#endif + } +#ifdef DEBUG + else + printf("node not added: name=%s type=%s\n", name, type); +#endif + } + + if (curnode = child(id)) { + pnode->child = walk(tree, pnode, curnode); + } + + if (curnode = next(id)) { + if (board_node) { + return (walk(tree, root, curnode)); + } else { + pnode->sibling = walk(tree, root, curnode); + } + } + + if (board_node) { + return (NULL); + } else { + return (pnode); + } +} diff --git a/usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c b/usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c new file mode 100644 index 0000000000..8b1f1914d2 --- /dev/null +++ b/usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c @@ -0,0 +1,288 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <varargs.h> +#include <errno.h> +#include <unistd.h> +#include <alloca.h> +#include <sys/systeminfo.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <kstat.h> +#include <libintl.h> +#include "pdevinfo.h" +#include "display.h" +#include "display_sun4v.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +/* + * Global variables + */ +char *progname; +char *promdev = "/dev/openprom"; +int print_flag = 1; +int logging = 0; + +/* + * This file represents the splitting out of some functionality + * of prtdiag due to the port to the sun4v platform. The PROM + * tree-walking functions which contain sun4v specifics were moved + * into this module. + */ + +extern int get_id(Prom_node *); + +/* Function prototypes */ +Prom_node *sun4v_walk(Sys_tree *, Prom_node *, int); +int sun4v_get_node_by_name(picl_nodehdl_t rooth, char *name, + picl_nodehdl_t *nodeh); +/* + * do_prominfo() is called from main() in usr/src/cmd/prtdiag/main.c + * + * This is the starting point for all platforms. However, this function + * can be overlayed by writing a do_prominfo() function + * in the libprtdiag_psr for a particular platform. + * + */ +int +do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag) +{ + Sys_tree sys_tree; /* system information */ + Prom_node *root_node; /* root node of OBP device tree */ + picl_nodehdl_t rooth; /* root PICL node for IO display */ + picl_nodehdl_t plafh; /* Platform PICL node for IO display */ + + int err; + + err = picl_initialize(); + if (err != PICL_SUCCESS) { + (void) fprintf(stderr, EM_INIT_FAIL, picl_strerror(err)); + exit(1); + } + + /* set the global flags */ + progname = pgname; + logging = log_flag; + print_flag = prt_flag; + + /* set the the system tree fields */ + sys_tree.sys_mem = NULL; + sys_tree.boards = NULL; + sys_tree.bd_list = NULL; + sys_tree.board_cnt = 0; + + if (promopen(O_RDONLY)) { + exit(_error(dgettext(TEXT_DOMAIN, "openeepr device " + "open failed"))); + } + + if (is_openprom() == 0) { + (void) fprintf(stderr, "%s", + dgettext(TEXT_DOMAIN, "System architecture " + "does not support this option of this " + "command.\n")); + return (2); + } + + if (next(0) == 0) { + return (2); + } + + root_node = sun4v_walk(&sys_tree, NULL, next(0)); + promclose(); + + err = picl_get_root(&rooth); + if (err != PICL_SUCCESS) { + (void) fprintf(stderr, EM_GET_ROOT_FAIL, picl_strerror(err)); + exit(1); + } + + err = sun4v_get_node_by_name(rooth, PICL_NODE_PLATFORM, &plafh); + if (err != PICL_SUCCESS) + return (err); + + return (sun4v_display(&sys_tree, root_node, syserrlog, plafh)); + +} + +/* + * sun4v_Walk the PROM device tree and build the system tree and root tree. + * Nodes that have a board number property are placed in the board + * structures for easier processing later. Child nodes are placed + * under their parents. + */ +Prom_node * +sun4v_walk(Sys_tree *tree, Prom_node *root, int id) +{ + register int curnode; + Prom_node *pnode; + char *name; + char *type; + char *compatible; + int board_node = 0; + + /* allocate a node for this level */ + if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) == + NULL) { + perror("malloc"); + exit(2); /* program errors cause exit 2 */ + } + + /* assign parent Prom_node */ + pnode->parent = root; + pnode->sibling = NULL; + pnode->child = NULL; + + /* read properties for this node */ + dump_node(pnode); + + /* + * Place a node in a 'board' if it has 'board'-ness. The definition + * is that all nodes that are children of root should have a + * board# property. But the PROM tree does not exactly follow + * this. This is where we start hacking. + * + * PCI to PCI bridges also have the name "pci", but with different + * model property values. They should not be put under 'board'. + */ + name = get_node_name(pnode); + type = get_node_type(pnode); + compatible = (char *)get_prop_val(find_prop(pnode, "compatible")); + +#ifdef DEBUG + if (name != NULL) + printf("name=%s ", name); + if (type != NULL) + printf("type=%s ", type); + printf("\n"); +#endif + if (compatible == NULL) + compatible = ""; + if (type == NULL) + type = ""; + if (name != NULL) { + if (has_board_num(pnode)) { + add_node(tree, pnode); + board_node = 1; +#ifdef DEBUG + printf("ADDED BOARD name=%s type=%s compatible=%s\n", + name, type, compatible); +#endif + } else if (strcmp(type, "cpu") == 0) { + add_node(tree, pnode); + board_node = 1; +#ifdef DEBUG + printf("ADDED BOARD name=%s type=%s compatible=%s\n", + name, type, compatible); +#endif + } +#ifdef DEBUG + else + printf("node not added: name=%s type=%s\n", name, type); +#endif + } + + if (curnode = child(id)) { + pnode->child = sun4v_walk(tree, pnode, curnode); + } + + if (curnode = next(id)) { + if (board_node) { + return (sun4v_walk(tree, root, curnode)); + } else { + pnode->sibling = sun4v_walk(tree, root, curnode); + } + } + + if (board_node) { + return (NULL); + } else { + return (pnode); + } +} + +/* + * search children to get the node by the nodename + */ +int +sun4v_get_node_by_name(picl_nodehdl_t rooth, char *name, + picl_nodehdl_t *nodeh) +{ + picl_nodehdl_t childh; + int err; + char *nodename; + + nodename = alloca(strlen(name) + 1); + if (nodename == NULL) + return (PICL_FAILURE); + + err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_NAME, + nodename, (strlen(name) + 1)); + if (err != PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + continue; + } + + if (strcmp(nodename, name) == 0) { + *nodeh = childh; + return (PICL_SUCCESS); + } + + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + } + + return (err); +} + +int +get_id(Prom_node *node) +{ +#ifdef lint + node = node; +#endif + + /* + * This function is intentionally empty + */ + return (0); +} diff --git a/usr/src/lib/libprtdiag/common/prom.c b/usr/src/lib/libprtdiag/common/prom.c new file mode 100644 index 0000000000..bd0e711ca8 --- /dev/null +++ b/usr/src/lib/libprtdiag/common/prom.c @@ -0,0 +1,134 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + + +void +disp_prom_version(Prom_node *flashprom) +{ + Prop *version; + char *vers; /* OBP version */ + char *temp; + + /* Look version */ + version = find_prop(flashprom, "version"); + + vers = (char *)get_prop_val(version); + + if (vers != NULL) { + log_printf(" %s ", vers, 0); + + /* + * POST string follows the NULL terminated OBP + * version string. Do not attempt to print POST + * string unless node size is larger than the + * length of the OBP version string. + */ + if ((strlen(vers) + 1) < version->size) { + temp = vers + strlen(vers) + 1; + log_printf("%s", temp, 0); + } + } + + log_printf("\n", 0); +} + + +void +platform_disp_prom_version(Sys_tree *tree) +{ + Board_node *bnode; + Prom_node *pnode; + + bnode = tree->bd_list; + + /* Display Prom revision header */ + log_printf(dgettext(TEXT_DOMAIN, "System PROM " + "revisions:\n"), 0); + log_printf("----------------------\n", 0); + + if ((pnode = find_device(bnode, 0x1F, SBUS_NAME)) == NULL) { + pnode = find_pci_bus(bnode->nodes, 0x1F, 1); + } + + /* + * in case of platforms with multiple flashproms, find and + * display all proms with a "version"(OBP) property. bug 4187301 + */ + for (pnode = dev_find_node(pnode, "flashprom"); pnode != NULL; + pnode = dev_next_node(pnode, "flashprom")) { + if (find_prop(pnode, "version") != NULL) { + disp_prom_version(pnode); + } + } +} + +int +get_pci_class_code_reg(Prom_node *card_node) +{ + void *value; + + /* + * Get the class-code of this node and return it + * if it exists. Otherwise return (-1). + */ + value = get_prop_val(find_prop(card_node, "class-code")); + if (value != NULL) + return (*(int *)value); + else + return (-1); +} diff --git a/usr/src/lib/libprtdiag/inc/display.h b/usr/src/lib/libprtdiag/inc/display.h new file mode 100644 index 0000000000..d90f5dc219 --- /dev/null +++ b/usr/src/lib/libprtdiag/inc/display.h @@ -0,0 +1,62 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#ifndef _DISPLAY_H +#define _DISPLAY_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int logging; +extern int print_flag; + +#define NOPRINT 0 +#define PRINT 1 +#define MX_SBUS_SLOTS 4 + +/* + * Define a structure to contain both the DRAM SIMM and NVRAM + * SIMM memory totals in MBytes. + */ +struct mem_total { + int dram; + int nvsimm; +}; + +/* Functions in common display.c module */ +void disp_powerfail(Prom_node *); +void log_printf(char *, ...); +char *get_time(uchar_t *); +void print_header(int); + +#ifdef __cplusplus +} +#endif + +#endif /* _DISPLAY_H */ diff --git a/usr/src/lib/libprtdiag/inc/display_sun4u.h b/usr/src/lib/libprtdiag/inc/display_sun4u.h new file mode 100644 index 0000000000..f9c7ed4f8e --- /dev/null +++ b/usr/src/lib/libprtdiag/inc/display_sun4u.h @@ -0,0 +1,96 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#ifndef _DISPLAY_SUN4U_H +#define _DISPLAY_SUN4U_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <pdevinfo_sun4u.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Define the memory decode bits for easier reading. These are from + * the Sunfire Programmer's Manual. + */ +#define MEM_SIZE_64M 0x4 +#define MEM_SIZE_256M 0xb +#define MEM_SIZE_1G 0xf +#define MEM_SIZE_2G 0x2 + +#define MEM_SPEED_50ns 0x0 +#define MEM_SPEED_60ns 0x3 +#define MEM_SPEED_70ns 0x2 +#define MEM_SPEED_80ns 0x1 + +/* + * If a QLC card is present in the system, the following values are needed + * to decode what type of a QLC card it is. + */ +#define AMBER_SUBSYSTEM_ID 0x4082 +#define CRYSTAL_SUBSYSTEM_ID 0x4083 + +#define AMBER_CARD_NAME "Amber" +#define CRYSTAL_CARD_NAME "Crystal+" + +#define MAX_QLC_MODEL_LEN 10 + +/* + * Define strings in this structure as arrays instead of pointers so + * that copying is easier. + */ +struct io_card { + int display; /* Should we display this card? */ + int node_id; /* Node ID */ + int board; /* Board number */ + char bus_type[MAXSTRLEN]; /* Type of bus this IO card is on */ + int schizo_portid; /* portid of the Schizo for this card */ + char pci_bus; /* PCI bus A or B */ + int slot; /* Slot number */ + char slot_str[MAXSTRLEN]; /* Slot description string */ + int freq; /* Frequency (in MHz) */ + char status[MAXSTRLEN]; /* Card status */ + char name[MAXSTRLEN]; /* Card name */ + char model[MAXSTRLEN]; /* Card model */ + int dev_no; /* device number */ + int func_no; /* function number */ + char notes[MAXSTRLEN]; /* notes */ + struct io_card *next; +}; + +/* used to determine whether slot (int) or slot_str(char*) should be used */ +#define PCI_SLOT_IS_STRING (-99) + +int display(Sys_tree *, Prom_node *, struct system_kstat_data *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _DISPLAY_SUN4U_H */ diff --git a/usr/src/lib/libprtdiag/inc/display_sun4v.h b/usr/src/lib/libprtdiag/inc/display_sun4v.h new file mode 100644 index 0000000000..19a4c3d295 --- /dev/null +++ b/usr/src/lib/libprtdiag/inc/display_sun4v.h @@ -0,0 +1,60 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DISPLAY_SUN4V_H +#define _DISPLAY_SUN4V_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <picl.h> + +#define CLK_FREQ_TO_MHZ(x) (((x) + 500000) / 1000000) +#define MAXSTRLEN 256 +#define CPU_STRAND_NAC "MB/CMP0/P" +#define H20_IMPL 0x5678 +#define IS_H20(impl) ((impl) == H20_IMPL) + +#define EM_INIT_FAIL dgettext(TEXT_DOMAIN,\ + "picl_initialize failed: %s\n") +#define EM_GET_ROOT_FAIL dgettext(TEXT_DOMAIN,\ + "Getting root node failed: %s\n") + +void sun4v_display_pci(picl_nodehdl_t plafh); +void sun4v_display_memoryconf(); +void sun4v_display_cpu_devices(picl_nodehdl_t plafh); +int sun4v_display_cpus(picl_nodehdl_t cpuh, void* args); +void sun4v_display_diaginfo(int flag, Prom_node *root, picl_nodehdl_t plafh); +int sun4v_display(Sys_tree *, Prom_node *, int, picl_nodehdl_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _DISPLAY_SUN4V_H */ diff --git a/usr/src/lib/libprtdiag/inc/libprtdiag.h b/usr/src/lib/libprtdiag/inc/libprtdiag.h new file mode 100644 index 0000000000..0354aea6a3 --- /dev/null +++ b/usr/src/lib/libprtdiag/inc/libprtdiag.h @@ -0,0 +1,202 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_LIBPRTDIAG_H +#define _SYS_LIBPRTDIAG_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/openpromio.h> +#include <sys/cheetahregs.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" + +#ifdef DEBUG +#define D_PRINTF printf +#else +#define D_PRINTF +#endif + +#define EXIT_MSG(msg, err) \ + { printf("\n%s failed with %d\n", msg, err); exit(err); } + +/* global data */ +#define PCI_DEVICE(x) ((x >> 11) & 0x1f) +#define PCI_REG_TO_DEV(x) ((x & 0xf800) >> 11) +#define PCI_REG_TO_FUNC(x) ((x & 0x700) >> 8) +#define BUS_TYPE "UPA" +#define MAX_SLOTS_PER_IO_BD 8 + + +int sys_clk; /* System clock freq. (in MHz) */ + +/* + * Defines for identifying PCI devices + */ +#define PCI_BRIDGE_CLASS 0x6 +#define PCI_CLASS_SHIFT 0x10 +#define PCI_PCI_BRIDGE_SUBCLASS 0x4 +#define PCI_SUBCLASS_SHIFT 0x8 +#define PCI_SUBCLASS_MASK 0xFF00 +#define PCI_SUBCLASS_OTHER 0x80 + +#define CLASS_REG_TO_SUBCLASS(class) (((class) & PCI_SUBCLASS_MASK) \ + >> PCI_SUBCLASS_SHIFT) +#define CLASS_REG_TO_CLASS(class) ((class) >> PCI_CLASS_SHIFT) + +/* + * display functions + */ +int error_check(Sys_tree *tree, struct system_kstat_data *kstats); +int disp_fail_parts(Sys_tree *tree); +void display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void resolve_board_types(Sys_tree *); +void display_boardnum(int num); +void display_platform_specific_header(void); + +/* + * cpu functions + */ +void display_cpu_devices(Sys_tree *); +void display_cpus(Board_node *); +void display_mid(int mid); +int get_cpu_freq(Prom_node *); +int get_ecache_size(Prom_node *); + +/* + * io functions + */ +Prom_node *find_pci_bus(Prom_node *, int, int); +int get_pci_bus(Prom_node *); +int get_pci_device(Prom_node *); +int get_pci_to_pci_device(Prom_node *); +void free_io_cards(struct io_card *); +struct io_card *insert_io_card(struct io_card *, struct io_card *); +char *fmt_manf_id(unsigned int, char *); +int get_sbus_slot(Prom_node *); +void display_io_devices(Sys_tree *tree); +void display_pci(Board_node *bnode); +void display_io_cards(struct io_card *); +void display_ffb(Board_node *, int); +void display_sbus(Board_node *); +int populate_slot_name_arr(Prom_node *pci, int *slot_name_bits, + char **slot_name_arr, int num_slots); +int get_card_frequency(Prom_node *pci); +void get_dev_func_num(Prom_node *card_node, int *dev_no, int *func_no); +void get_pci_class_codes(Prom_node *card_node, int *class_code, + int *subclass_code); +int is_pci_bridge(Prom_node *card_node, char *name); +int is_pci_bridge_other(Prom_node *card_node, char *name); +void get_pci_card_model(Prom_node *card_node, char *model); +void create_io_card_name(Prom_node *card_node, char *name, + char *card_name); +void display_psycho_pci(Board_node *bnode); +void get_slot_number_str(struct io_card *card, char **slot_name_arr, + int slot_name_bits); +void distinguish_identical_io_cards(char *name, Prom_node *node, + struct io_card *card); +void decode_qlc_card_model_prop(Prom_node *card_node, struct io_card *card); + +/* + * kstat functions + */ +void read_platform_kstats(Sys_tree *tree, + struct system_kstat_data *sys_kstat, + struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep); +void read_sun4u_kstats(Sys_tree *, struct system_kstat_data *); + +/* + * memory functions + */ +void display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats, + struct grp_info *grps, struct mem_total *memory_total); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); + +/* + * prom functions + */ +void platform_disp_prom_version(Sys_tree *); +void disp_prom_version(Prom_node *); +void add_node(Sys_tree *, Prom_node *); +Prom_node *find_device(Board_node *, int, char *); +Prom_node *walk(Sys_tree *, Prom_node *, int); +int get_pci_class_code_reg(Prom_node *); + +/* + * libdevinfo functions + */ +int do_devinfo(int, char *, int, int); + +/* + * mc-us3 memory functions and structs + */ +typedef struct memory_bank { + int id; + int portid; + ushort_t valid; + ushort_t uk; + uint_t um; + uchar_t lk; + uchar_t lm; + uint64_t bank_size; + char *bank_status; + struct memory_bank *next; /* mc in the devtree */ + struct memory_bank *seg_next; /* in the segment */ +} memory_bank_t; + +typedef struct memory_seg { + int id; + int intlv; /* interleave for this segment */ + uint64_t base; /* base address for this segment */ + uint64_t size; /* size of this segment */ + int nbanks; /* number of banks in this segment */ + memory_bank_t *banks; /* pointer to the banks of this seg */ + struct memory_seg *next; +} memory_seg_t; + +#define NUM_MBANKS_PER_MC 4 + +int get_us3_mem_regs(Board_node *bnode); +void display_us3_banks(void); +int display_us3_failed_banks(int system_failed); +void print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, + char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id); +void print_us3_failed_memory_line(int portid, int bank_id, + char *bank_status); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LIBPRTDIAG_H */ diff --git a/usr/src/lib/libprtdiag/inc/pdevinfo.h b/usr/src/lib/libprtdiag/inc/pdevinfo.h new file mode 100644 index 0000000000..fc6e1bc8e7 --- /dev/null +++ b/usr/src/lib/libprtdiag/inc/pdevinfo.h @@ -0,0 +1,192 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PDEVINFO_H +#define _PDEVINFO_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* structures necessary to hold Openprom data */ + +/* + * 128 is the size of the largest (currently) property name + * 4096 - MAXPROPSIZE - sizeof (int) is the size of the largest + * (currently) property value that is allowed. + * the sizeof (u_int) is from struct openpromio + */ +#define MAXPROPSIZE 128 +#define MAXVALSIZE (4096 - MAXPROPSIZE - sizeof (uint_t)) +#define BUFSIZE (MAXPROPSIZE + MAXVALSIZE + sizeof (uint_t)) +typedef union { + char buf[BUFSIZE]; + struct openpromio opp; + void *val_ptr; +} Oppbuf; + +/* + * The prop structures associated with a Prom_node were formerly statically + * sized - via the buf element of the Oppbuf union. This was highly memory + * inefficient, so dynamic sizing capabilities have been introduced. + * + * This has been achieved via the creation of dynopenpromio and dynOppbuf + * structs, and altering the prop structure. The prop structure's name and value + * elements are now typed as dynOppbuf instead of Oppbuf. + * + * For legacy purposes, static_prop has been created. It is essentially the same + * as the former prop structure, but the *next element now points to a + * static_prop structure instead of a prop structure. + */ +typedef struct static_prop StaticProp; +struct static_prop { + StaticProp *next; + Oppbuf name; + Oppbuf value; + int size; /* size of data in bytes */ +}; + +/* + * dynopenpromio structs are similar to openpromio structs, but with 2 major + * differences. The first is that the opio_u.b element is char * instead of + * char [], which allows for dynamic sizing. + * + * The second regards opio_u.i, which was an int, but is now int []. In almost + * all cases, only opio_u.i (opio_u.i[0]) will be referenced. However, certain + * platforms rely on the fact that Prop structures formerly contained Oppbuf + * unions, the buf element of which was statically sized at 4k. In theory, this + * enabled those platforms to validly reference any part of the union up to 4k + * from the start. In reality, no element greater than opio_u.i[4] is currently + * referenced, hence OPROM_NODE_SIZE (named because opio_u.i is usually + * referenced as oprom_node) being set to 5. + * + * A minor difference is that the holds_array element has been added, which + * affords an easy way to determine whether opio_u contains char * or int. + */ +#define OPROM_NODE_SIZE 5 +struct dynopenpromio { + uint_t oprom_size; + union { + char *b; + int i[OPROM_NODE_SIZE]; + } opio_u; + uint_t holds_array; +}; + +/* + * dynOppbuf structs are a dynamic alternative to Oppbuf unions. The statically + * sized Oppbuf.buf element has been removed, and the opp element common to both + * is of type struct dynopenpromio instead of struct openpromio. This allows us + * to take advantage of dynopenpromio's dynamic sizing capabilities. + */ +typedef struct dynoppbuf dynOppbuf; +struct dynoppbuf { + struct dynopenpromio opp; + char *val_ptr; +}; + +typedef struct prop Prop; +struct prop { + Prop *next; + dynOppbuf name; + dynOppbuf value; + int size; /* size of data in bytes */ +}; + +typedef struct prom_node Prom_node; +struct prom_node { + Prom_node *parent; /* points to parent node */ + Prom_node *child; /* points to child PROM node */ + Prom_node *sibling; /* point to next sibling */ + Prop *props; /* points to list of properties */ +}; + +/* + * Defines for board types. + */ + +typedef struct board_node Board_node; +struct board_node { + int node_id; + int board_num; + int board_type; + Prom_node *nodes; + Board_node *next; /* link for list */ +}; + +typedef struct system_tree Sys_tree; +struct system_tree { + Prom_node *sys_mem; /* System memory node */ + Prom_node *boards; /* boards node holds bif info if present */ + Board_node *bd_list; /* node holds list of boards */ + int board_cnt; /* number of boards in the system */ +}; + +int do_prominfo(int, char *, int, int); +int is_openprom(void); +void promclose(void); +int promopen(int); +extern char *badarchmsg; +int _error(char *fmt, ...); + +/* Functions for building the user copy of the device tree. */ +Board_node *find_board(Sys_tree *, int); +Board_node *insert_board(Sys_tree *, int); + +/* functions for searching for Prom nodes */ +char *get_node_name(Prom_node *); +char *get_node_type(Prom_node *); +Prom_node *dev_find_node(Prom_node *, char *); +Prom_node *dev_next_node(Prom_node *, char *); +Prom_node *dev_find_node_by_type(Prom_node *root, char *type, char *property); +Prom_node *dev_next_node_by_type(Prom_node *root, char *type, char *property); +Prom_node *dev_find_type(Prom_node *, char *); +Prom_node *dev_next_type(Prom_node *, char *); +Prom_node *sys_find_node(Sys_tree *, int, char *); +Prom_node *find_failed_node(Prom_node *); +Prom_node *next_failed_node(Prom_node *); +Prom_node *dev_find_node_by_compatible(Prom_node *root, char *compat); +Prom_node *dev_next_node_by_compatible(Prom_node *root, char *compat); +int node_failed(Prom_node *); +int node_status(Prom_node *node, char *status); +void dump_node(Prom_node *); +int next(int); +int has_board_num(Prom_node *); +int get_board_num(Prom_node *); +int child(int); + +/* functions for searching for properties, extracting data from them */ +void *get_prop_val(Prop *); +void getpropval(struct openpromio *); +Prop *find_prop(Prom_node *, char *); + +#ifdef __cplusplus +} +#endif + +#endif /* _PDEVINFO_H */ diff --git a/usr/src/lib/libprtdiag/inc/pdevinfo_sun4u.h b/usr/src/lib/libprtdiag/inc/pdevinfo_sun4u.h new file mode 100644 index 0000000000..7273d62b8d --- /dev/null +++ b/usr/src/lib/libprtdiag/inc/pdevinfo_sun4u.h @@ -0,0 +1,233 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#ifndef _PDEVINFO_SUN4U_H +#define _PDEVINFO_SUN4U_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/obpdefs.h> +#include <sys/fhc.h> +#include <sys/sysctrl.h> +#include <sys/environ.h> +#include <sys/envctrl_gen.h> +#include <sys/envctrl_ue250.h> +#include <sys/envctrl_ue450.h> +#include <sys/simmstat.h> +#include <sys/ac.h> +#include <sys/sram.h> +#include <reset_info.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define UNIX "unix" + +/* Define names of nodes to search for */ +#define CPU_NAME "SUNW,UltraSPARC" +#define SBUS_NAME "sbus" +#define PCI_NAME "pci" +#define FFB_NAME "SUNW,ffb" +#define AFB_NAME "SUNW,afb" + +struct mem_stat_data { + enum ac_bank_status status; /* bank status values */ + enum ac_bank_condition condition; /* bank conditions */ +}; + +struct bd_kstat_data { + u_longlong_t ac_memctl; /* Memctl register contents */ + u_longlong_t ac_memdecode[2]; /* memory decode registers . */ + int ac_kstats_ok; /* successful kstat read occurred */ + uint_t fhc_bsr; /* FHC Board Status Register */ + uint_t fhc_csr; /* FHC Control Status Register */ + int fhc_kstats_ok; /* successful kstat read occurred */ + uchar_t simm_status[SIMM_COUNT]; /* SIMM status */ + int simmstat_kstats_ok; /* successful read occurred */ + struct temp_stats tempstat; + int temp_kstat_ok; + struct mem_stat_data mem_stat[2]; /* raw kstat bank information */ + int ac_memstat_ok; /* successful read of memory status */ +}; + +/* + * Hot plug info structure. If a hotplug kstat is found, the bd_info + * structure from the kstat is filled in the the hp_info structure + * is marked OK. + */ +struct hp_info { + struct bd_info bd_info; + int kstat_ok; +}; + +/* Environmental info for Tazmo */ +struct envctrl_kstat_data { + envctrl_ps_t ps_kstats[MAX_DEVS]; /* kstats for powersupplies */ + envctrl_fan_t fan_kstats[MAX_DEVS]; /* kstats for fans */ + envctrl_encl_t encl_kstats[MAX_DEVS]; /* kstats for enclosure */ +}; + +/* Environmental info for Javelin */ +struct envctrltwo_kstat_data { + envctrl_ps2_t ps_kstats[MAX_DEVS]; /* kstats for powersupplies */ + int num_ps_kstats; + envctrl_fan_t fan_kstats[MAX_DEVS]; /* kstats for fans */ + int num_fan_kstats; + envctrl_encl_t encl_kstats[MAX_DEVS]; /* kstats for enclosure */ + int num_encl_kstats; + envctrl_temp_t temp_kstats[MAX_DEVS]; /* kstats for temperatures */ + int num_temp_kstats; + envctrl_disk_t disk_kstats[MAX_DEVS]; /* kstats for disks */ + int num_disk_kstats; +}; + +struct system_kstat_data { + uchar_t sysctrl; /* sysctrl register contents */ + uchar_t sysstat1; /* system status1 register contents. */ + uchar_t sysstat2; /* system status2 register contents. */ + uchar_t ps_shadow[SYS_PS_COUNT]; /* power supply shadow */ + int psstat_kstat_ok; + uchar_t clk_freq2; /* clock frequency register 2 contents */ + uchar_t fan_status; /* shadow fan status */ + uchar_t keysw_status; /* status of the key switch */ + enum power_state power_state; /* redundant power state */ + uchar_t clk_ver; /* clock version register */ + int sys_kstats_ok; /* successful kstat read occurred */ + struct temp_stats tempstat; + int temp_kstat_ok; + struct reset_info reset_info; + int reset_kstats_ok; /* kstat read OK */ + struct bd_kstat_data bd_ksp_list[MAX_BOARDS]; + struct hp_info hp_info[MAX_BOARDS]; + struct ft_list *ft_array; /* fault array */ + int nfaults; /* number of faults in fault array */ + int ft_kstat_ok; /* Fault kstats OK */ + struct envctrl_kstat_data env_data; /* environment data for Tazmo */ + int envctrl_kstat_ok; + struct envctrltwo_kstat_data envc_data; /* environ data for Javelin */ + int envctrltwo_kstat_ok; +}; + +/* Description of a single memory group */ +struct grp { + int valid; /* active memory group present */ + u_longlong_t base; /* Phyiscal base of group */ + uint_t size; /* size in bytes */ + uint_t curr_size; /* current size in bytes */ + int board; /* board number */ + enum board_type type; /* board type */ + int group; /* group # on board (0 or 1) */ + int factor; /* interleave factor (0,2,4,8,16) */ + int speed; /* Memory speed (in ns) */ + char groupid; /* Alpha tag for group ID */ + enum ac_bank_status status; /* bank status values */ + enum ac_bank_condition condition; /* bank conditions */ +}; + +#define MAX_GROUPS 32 +#define MAXSTRLEN 256 + +/* Array of all possible groups in the system. */ +struct grp_info { + struct grp grp[MAX_GROUPS]; +}; + +/* A memory interleave structure */ +struct inter_grp { + u_longlong_t base; /* Physical base of group */ + int valid; + int count; + char groupid; +}; + +/* Array of all possible memory interleave structures */ +struct mem_inter { + struct inter_grp i_grp[MAX_GROUPS]; +}; + +/* FFB info structure */ +struct ffbinfo { + int board; + int upa_id; + char *dev; + struct ffbinfo *next; +}; + +/* FFB strap reg union */ +union strap_un { + struct { + uint_t unused:24; + uint_t afb_flag:1; + uint_t major_rev:2; + uint_t board_rev:2; + uint_t board_mem:1; + uint_t cbuf:1; + uint_t bbuf:1; + } fld; + uint_t ffb_strap_bits; +}; + +/* known values for manufacturer's JED code */ +#define MANF_BROOKTREE 214 +#define MANF_MITSUBISHI 28 + +/* FFB mnufacturer union */ +union manuf { + struct { + uint_t version:4; /* version of part number */ + uint_t partno:16; /* part number */ + uint_t manf:11; /* manufacturer's JED code */ + uint_t one:1; /* always set to '1' */ + } fld; + uint_t encoded_id; +}; + +#define FFBIOC ('F' << 8) +#define FFB_SYS_INFO (FFBIOC| 80) + +struct ffb_sys_info { + unsigned int ffb_strap_bits; /* ffb_strapping register */ +#define FFB_B_BUFF 0x01 /* B buffer present */ +#define FFB_C_BUFF 0x02 /* C buffer present */ +#define FB_TYPE_AFB 0x80 /* AFB or FFB */ + unsigned int fbc_version; /* revision of FBC chip */ + unsigned int dac_version; /* revision of DAC chip */ + unsigned int fbram_version; /* revision of FBRAMs chip */ + unsigned int flags; /* miscellaneous flags */ +#define FFB_KSIM 0x00000001 /* kernel simulator */ +#define FFB_PAGE_FILL_BUG 0x00000002 /* FBRAM has page fill bug */ + unsigned int afb_nfloats; /* no. of Float asics in AFB */ + unsigned int pad[58]; /* padding for AFB chips & misc. */ +}; + +int get_id(Prom_node *); + +#ifdef __cplusplus +} +#endif + +#endif /* _PDEVINFO_SUN4U_H */ diff --git a/usr/src/lib/libprtdiag/inc/reset_info.h b/usr/src/lib/libprtdiag/inc/reset_info.h new file mode 100644 index 0000000000..8f94813a6f --- /dev/null +++ b/usr/src/lib/libprtdiag/inc/reset_info.h @@ -0,0 +1,108 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#ifndef _RESET_INFO_H +#define _RESET_INFO_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * All of the following data structures and defines come from sun4u server + * POST. If the data in POST changes, then these structures must reflect + * those changes. + */ + +#include <sys/fhc.h> /* To get MAX_BOARDS constant */ + +/* BDA bit assignments */ +#define BOARD_PRESENT (1<<0) +#define BOARD_OK (1<<1) +#define BOARD_TYPE_MSK (7<<2) +#define BOARD_TYPE(x) (((x) & BOARD_TYPE_MSK) >> 2) + +/* Board state mask and defines */ +#define BD_STATE_MASK 0x3 +#define BD_LPM_FZN 0 +#define BD_ONLINE_FAIL 1 +#define BD_NOT_PRESENT 2 +#define BD_ONLINE_NORMAL 3 + +/* define CPU 0 fields */ +#define CPU0_PRESENT (1<<8) +#define CPU0_OK (1<<9) +#define CPU0_FAIL_CODE_MSK (7<<10) + +/* define CPU 1 fields */ +#define CPU1_PRESENT (1<<16) +#define CPU1_OK (1<<17) +#define CPU1_FAIL_CODE_MSK (7<<18) + +/* supported board types */ +#define CPU_TYPE 0 +#define MEM_TYPE 1 /* CPU/MEM board with only memory */ +#define IO_TYPE1 2 +#define IO_TYPE2 3 +#define IO_TYPE3 4 +#define IO_TYPE4 5 /* same as IO TYPE 1 but no HM or PHY chip */ +#define CLOCK_TYPE 7 + +/* for CPU type UPA ports */ +typedef struct { + u_longlong_t afsr; /* Fault status register for CPU */ + u_longlong_t afar; /* Fault address register for CPU */ +} cpu_reset_state; + +/* For the clock board */ +typedef struct { + unsigned long clk_ssr_1; /* reset status for the clock board */ +} clock_reset_state; + +struct board_info { + u_longlong_t board_desc; + cpu_reset_state cpu[2]; /* could be a CPU */ + u_longlong_t ac_error_status; + u_longlong_t dc_shadow_chain; + uint_t fhc_csr; + uint_t fhc_rcsr; +}; + +struct reset_info { + int length; /* size of the structure */ + int version; /* Version of the structure */ + struct board_info bd_reset_info[MAX_BOARDS]; + clock_reset_state clk; /* one clock board */ + unsigned char tod_timestamp[7]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _RESET_INFO_H */ diff --git a/usr/src/lib/libprtdiag/sparc/Makefile b/usr/src/lib/libprtdiag/sparc/Makefile new file mode 100644 index 0000000000..71383e8afa --- /dev/null +++ b/usr/src/lib/libprtdiag/sparc/Makefile @@ -0,0 +1,46 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# lib/libprtdiag/sparc/Makefile +# +#ident "%Z%%M% %I% %E% SMI" +# +SUBDIRS= sun4u sun4v + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber +lint := TARGET= lint +_msg := TARGET= _msg + +.KEEP_STATE: + +all install clean clobber lint _msg : $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/libprtdiag/sparc/sun4u/Makefile b/usr/src/lib/libprtdiag/sparc/sun4u/Makefile new file mode 100644 index 0000000000..b67715c346 --- /dev/null +++ b/usr/src/lib/libprtdiag/sparc/sun4u/Makefile @@ -0,0 +1,80 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag/sparc/sun4u/Makefile +# +# Platform specific Makefile for libprtdiag. +# +# PLATFORM is the target for the binary installation. +# + +PLATFORM = sun4u + +OBJECTS = io.o cpu.o memory.o kstat.o prom.o \ + pdevinfo_funcs.o display_funcs.o \ + pdevinfo_sun4u.o display_sun4u.o \ + libdevinfo_sun4u.o + +include ../../Makefile.com + +SRCS = $(OBJECTS:%.o=../../common/%.c) +LDLIBS += -ldevinfo + +# +# used for creating message catalogue files +# +TEXT_DOMAIN= SUNW_OST_OSLIB +SED= sed +GREP= grep +CP= cp + +.KEEP_STATE: + +all: $(LIBS) + +install: all $(USR_PSM_LIBS) $(USR_PSMLINT) + +lint: lintcheck + +include $(SRC)/lib/Makefile.targ + +POFILE= libprtdiag_sun4u.po +POFILES= generic.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../../common/*.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag/sparc/sun4v/Makefile b/usr/src/lib/libprtdiag/sparc/sun4v/Makefile new file mode 100644 index 0000000000..bbd5bb119e --- /dev/null +++ b/usr/src/lib/libprtdiag/sparc/sun4v/Makefile @@ -0,0 +1,79 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag/sparc/sun4v/Makefile +# +# Platform specific Makefile for libprtdiag. +# +# PLATFORM is the target for the binary installation. +# + +PLATFORM = sun4v + +OBJECTS = io.o cpu.o memory.o kstat.o prom.o \ + pdevinfo_funcs.o display_funcs.o \ + pdevinfo_sun4v.o display_sun4v.o + +include ../../Makefile.com + +SRCS = $(OBJECTS:%.o=../../common/%.c) +LDLIBS += -lpicl + +# +# used for creating message catalogue files +# +TEXT_DOMAIN= SUNW_OST_OSLIB +SED= sed +GREP= grep +CP= cp + +.KEEP_STATE: + +all: $(LIBS) + +install: all $(USR_PSM_LIBS) + +lint: lintcheck + +include $(SRC)/lib/Makefile.targ + +POFILE= libprtdiag_sun4v.po +POFILES= generic.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../../common/*.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/Makefile b/usr/src/lib/libprtdiag_psr/Makefile new file mode 100644 index 0000000000..d52784045d --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/Makefile @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/Makefile + +include $(SRC)/Makefile.master + +SUBDIRS= $(MACH) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +delete := TARGET= delete +install := TARGET= install +lint := TARGET= lint +package := TARGET= package +_msg := TARGET= _msg + +.KEEP_STATE: + +all clean clobber delete install lint package _msg: $(SUBDIRS) + +$(MACH) $(MACH64): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/libprtdiag_psr/sparc/Makefile b/usr/src/lib/libprtdiag_psr/sparc/Makefile new file mode 100644 index 0000000000..e9d36c0d44 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/Makefile @@ -0,0 +1,49 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/Makefile + +PRTDIAG_PLATFORMS= desktop tazmo javelin sunfire starfire serengeti \ + montecarlo littleneck starcat daktari cherrystone \ + lw8 snowbird ontario schumacher + +all := TARGET= all +lint := TARGET= lint +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +_msg := TARGET= _msg + +.KEEP_STATE: + +all lint clean clobber install _msg : $(PRTDIAG_PLATFORMS) + +$(PRTDIAG_PLATFORMS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + diff --git a/usr/src/lib/libprtdiag_psr/sparc/Makefile.com b/usr/src/lib/libprtdiag_psr/sparc/Makefile.com new file mode 100644 index 0000000000..18df32a634 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/Makefile.com @@ -0,0 +1,71 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY= libprtdiag_psr.a +VERS= .1 + +# +# PSR_MACH is defined sun4u so previous sun4u platforms can still continue +# to use sun4u libraries but sun4v platforms can override it to use sun4v +# libraries. +# +PSR_MACH= sun4u +# +# PLATFORM_OBJECTS is defined in ./desktop ./wgs ./sunfire ./starfire Makefiles +# +OBJECTS= $(PLATFORM_OBJECTS) + +# include library definitions +include $(SRC)/lib/Makefile.lib +include $(SRC)/Makefile.psm + +SRCS= $(OBJECTS:%.o=./common/%.c) + +LIBS = $(DYNLIB) + +CFLAGS += $(CCVERBOSE) +IFLAGS += -I $(UTSBASE)/sun4u +IFLAGS += -I $(UTSCLOSED)/sun4u +IFLAGS += -I $(UTSCLOSED)/sun4u/sunfire -I $(UTSBASE)/sun4u/sunfire +CPPFLAGS = $(IFLAGS) $(CPPFLAGS.master) +LDLIBS += -L $(ROOT)/usr/platform/$(PSR_MACH)/lib -lprtdiag -lc +DYNFLAGS += -R /usr/platform/$(PSR_MACH)/lib +INS.slink6= $(RM) -r $@; $(SYMLINK) ../../$(PLATFORM)/lib/libprtdiag_psr.so.1 $@ $(CHOWNLINK) $(CHGRPLINK) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +# include library targets +include $(SRC)/lib/Makefile.targ + +objs/%.o pics/%.o: ./common/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) diff --git a/usr/src/lib/libprtdiag_psr/sparc/cherrystone/Makefile b/usr/src/lib/libprtdiag_psr/sparc/cherrystone/Makefile new file mode 100644 index 0000000000..c5b46abb4e --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/cherrystone/Makefile @@ -0,0 +1,107 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/cherrystone/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= cherrystone.o + +include ../Makefile.com + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../libprtdiag/inc +IFLAGS += -I $(SRC)/cmd/picl/plugins/sun4u/psvc/psvcobj +IFLAGS += -I$(UTSBASE)/sun4u + +LDLIBS += -lpicl + +LINTFLAGS += $(IFLAGS) + +# +# links in /usr/platform +# +LINKED_PLATFORMS = SUNW,Sun-Fire-V490 + +LINKED_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%) +LINKED_LIB_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib) +LINKED_PRTDIAG_DIRS = \ + $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib/libprtdiag_psr.so.1) + +# +# Workgroup Server platform library should install into +# SUNW,Ultra-4. All other desktop platforms can +# link to /usr/platform/SUNW,Ultra-4/lib/libprtdiag_psr.so +# +PLATFORM=SUNW,Sun-Fire-480R + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) $(LINKED_PRTDIAG_DIRS) + +# +# install rule +# + +$(PLATLIBS): + $(INS.dir) + +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/cherrystone; pwd ; $(MAKE) $(USR_PSM_LIB_DIR) + +$(LINKED_DIRS): $(USR_PLAT_DIR) + -$(INS.dir.root.sys) + +$(LINKED_LIB_DIRS): $(LINKED_DIRS) + -$(INS.dir.root.sys) + +$(LINKED_PRTDIAG_DIRS): $(USR_PLAT_DIR) + -$(INS.slink6) + +# +# used for message files +# +POFILE= libprtdiag_psr_cherrystone.po +POFILES= cherrystone.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/*.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po + diff --git a/usr/src/lib/libprtdiag_psr/sparc/cherrystone/common/cherrystone.c b/usr/src/lib/libprtdiag_psr/sparc/cherrystone/common/cherrystone.c new file mode 100644 index 0000000000..6f5f957591 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/cherrystone/common/cherrystone.c @@ -0,0 +1,919 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * + * Cherrystone platform-specific functions + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <kstat.h> +#include <string.h> +#include <assert.h> +#include <libintl.h> +#include <note.h> +#include <syslog.h> + +#include <sys/openpromio.h> +#include <sys/sysmacros.h> + +#include <pdevinfo.h> +#include <display.h> +#include <pdevinfo_sun4u.h> +#include <display_sun4u.h> + +#include <picl.h> + +#include <sys/cheetahregs.h> +#include <sys/cherrystone.h> +#include "workfile.c" + +#define SCHIZO_COMPAT_PROP "pci108e,8001" + +#define MULTIPLE_BITS_SET(x) ((x)&((x)-1)) + +#define MAX_PS 2 +#define MAX_PS_SENSORS 3 +#define MAX_DISKS 2 +#define MAX_FANS 5 +#define NUM_PCI_SLOTS 5 + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (workgroup server systems only) + */ +void display_cpu_devices(Sys_tree *tree); +void display_pci(Board_node *board); +void display_io_cards(struct io_card *list); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void display_ffb(Board_node *board, int table); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); + +/* local functions */ +static void disp_envc_status(void); +static int print_temps(picl_nodehdl_t); +static int print_keyswitch(picl_nodehdl_t); +static int print_FSP_LEDS(picl_nodehdl_t); +static int print_disk(picl_nodehdl_t); +static int print_fans(picl_nodehdl_t); +static int print_ps(picl_nodehdl_t); + +static void display_hw_revisions(Prom_node *root, + Board_node *bnode); +static void display_schizo_revisions(Board_node *bdlist); + + +void +display_cpu_devices(Sys_tree *tree) +{ + Board_node *bnode; + + log_printf(dgettext(TEXT_DOMAIN, + "\n========================= CPUs " + "===============================================\n\n" + " Run E$ CPU CPU \n" + "Brd CPU MHz MB Impl. Mask \n" + "--- ----- ---- ---- ------- ---- \n")); + + bnode = tree->bd_list; + while (bnode != NULL) { + display_cpus(bnode); + bnode = bnode->next; + } + + log_printf("\n"); +} +void +display_cpus(Board_node *board) +{ + Prom_node *cpu; + int freq; + int ecache_size; + int *l3_shares; + int *mid; + int *impl; + int *mask; + int *coreid; + char fru_prev = 'X'; /* Valid frus are 'A','B' */ + int mid_prev; + int ecache_size_prev = 0; + char fru_name; + + /* + * display the CPUs' operating frequency, cache size, impl. field + * and mask revision. + */ + + for (cpu = dev_find_type(board->nodes, "cpu"); cpu != NULL; + cpu = dev_next_type(cpu, "cpu")) { + + mid = (int *)get_prop_val(find_prop(cpu, "portid")); + if (mid == NULL) + mid = (int *)get_prop_val(find_prop(cpu, "cpuid")); + freq = HZ_TO_MHZ(get_cpu_freq(cpu)); + ecache_size = get_ecache_size(cpu); + impl = (int *)get_prop_val(find_prop(cpu, "implementation#")); + mask = (int *)get_prop_val(find_prop(cpu, "mask#")); + l3_shares = + (int *)get_prop_val(find_prop(cpu, "l3-cache-sharing")); + + /* Do not display a failed CPU node */ + if ((impl == NULL) || (freq == 0) || (node_failed(cpu))) + continue; + + fru_name = CHERRYSTONE_GETSLOT_LABEL(*mid); + if (CPU_IMPL_IS_CMP(*impl)) { + coreid = (int *)get_prop_val(find_prop(cpu, "reg")); + if (coreid == NULL) { + continue; + } + if ((fru_prev == 'X') || + ((fru_prev != 'X') && + (fru_name != fru_prev))) { + fru_prev = fru_name; + mid_prev = *mid; + ecache_size_prev = ecache_size; + continue; + } else { + /* + * Some CMP chips have a split E$, + * so the size for both cores is added + * together to get the total size for + * the chip. + * + * Still, other CMP chips have E$ (L3) + * which is logically shared, so the + * total size is equal to the core size. + */ + if ((l3_shares == NULL) || + ((l3_shares != NULL) && + MULTIPLE_BITS_SET(*l3_shares))) { + ecache_size += ecache_size_prev; + } + ecache_size_prev = 0; + fru_prev = 'X'; + } + } + + log_printf(" %c", fru_name); + + /* CPU Module ID */ + if (CPU_IMPL_IS_CMP(*impl)) { + log_printf("%3d,%3d ", mid_prev, *mid, 0); + } else + log_printf(" %2d ", *mid); + + /* Running frequency */ + log_printf("%4d", freq); + + if (ecache_size == 0) + log_printf(" N/A "); + else + log_printf(" %4.1f ", + (float)ecache_size / (float)(1<<20)); + /* Implementation */ + if (impl == NULL) { + log_printf(dgettext(TEXT_DOMAIN, " N/A ")); + } else { + if (IS_CHEETAH(*impl)) + log_printf(dgettext(TEXT_DOMAIN, + "US-III ")); + else if (IS_CHEETAH_PLUS(*impl)) + log_printf(dgettext(TEXT_DOMAIN, + "US-III+ ")); + else if (IS_JAGUAR(*impl)) + log_printf(dgettext(TEXT_DOMAIN, + "US-IV ")); + else if (IS_PANTHER(*impl)) + log_printf(dgettext(TEXT_DOMAIN, + "US-IV+ ")); + else + log_printf("%-6x ", *impl); + } + + /* CPU Mask */ + if (mask == NULL) { + log_printf(dgettext(TEXT_DOMAIN, " N/A\n")); + } else { + log_printf(dgettext(TEXT_DOMAIN, " %d.%d\n"), + (*mask >> 4) & 0xf, *mask & 0xf); + } + } +} + +/*ARGSUSED0*/ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + Board_node *bnode = tree->bd_list; + + log_printf(dgettext(TEXT_DOMAIN, + "========================= Memory Configuration" + " ===============================\n\n" + " Logical Logical Logical\n" + " MC Bank Bank Bank DIMM " + "Interleave Interleaved\n" + "Brd ID num size Status Size " + "Factor with\n" + "--- --- ---- ------ ----------- ------ " + "---------- -----------")); + + while (bnode != NULL) { + if (get_us3_mem_regs(bnode)) { + log_printf(dgettext(TEXT_DOMAIN, + "\nFailed to get memory information.\n")); + return; + } + bnode = bnode->next; + } + + /* Display what we have found */ + display_us3_banks(); +} + +/*ARGSUSED3*/ +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + /* + * Now display the last powerfail time and the fatal hardware + * reset information. We do this under a couple of conditions. + * First if the user asks for it. The second is if the user + * told us to do logging, and we found a system failure. + */ + + if (flag) { + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + disp_envc_status(); + + display_hw_revisions(root, tree->bd_list); + } + return; + +} + +/* + * display_pci + * Display all the PCI IO cards on this board. + */ +void +display_pci(Board_node *board) +{ + struct io_card *card_list = NULL; + struct io_card card; + void *value; + Prom_node *pci; + Prom_node *card_node; + static int banner = FALSE; + + char *slot_name_arr[NUM_PCI_SLOTS]; + int i; + + if (board == NULL) + return; + + memset(&card, 0, sizeof (struct io_card)); + /* Initialize all the common information */ + card.display = TRUE; + card.board = board->board_num; + + /* + * Search for each pci instance, then find/display all nodes under + * each instance node found. + */ + for (pci = dev_find_node_by_compat(board->nodes, SCHIZO_COMPAT_PROP); + pci != NULL; + pci = dev_next_node_by_compat(pci, SCHIZO_COMPAT_PROP)) { + (void) snprintf(card.bus_type, MAXSTRLEN, + dgettext(TEXT_DOMAIN, "PCI")); + /* + * Get slot-name properties from parent node and + * store them in an array. + */ + value = (char *)get_prop_val( + find_prop(pci, "slot-names")); + + if (value != NULL) { + /* array starts after first int */ + slot_name_arr[0] = (char *)value + sizeof (int); + for (i = 1; i < NUM_PCI_SLOTS; i++) { + slot_name_arr[i] = (char *)slot_name_arr[i - 1] + + strlen(slot_name_arr[i - 1]) +1; + } + } + /* + * Search for Children of this node ie. Cards. + * Note: any of these cards can be a pci-bridge + * that itself has children. If we find a + * pci-bridge we need to handle it specially. + */ + card_node = pci->child; + /* Generate the list of pci cards on pci instance: pci */ + fill_pci_card_list(pci, card_node, &card, &card_list, + slot_name_arr); + } /* end-for */ + + if (!banner && card_list != NULL) { + log_printf(dgettext(TEXT_DOMAIN, + " Bus Max\n" + " IO Port Bus Freq Bus Dev,\n" + "Type ID Side Slot MHz Freq Func State " + "Name Model" +#ifdef DEBUG + " Notes" +#endif + "\n" + "---- ---- ---- ---- ---- ---- ---- ----- " + "-------------------------------- " +#ifdef DEBUG + "---------------------- " +#endif + "----------------------\n")); + banner = TRUE; + } + + display_io_cards(card_list); + free_io_cards(card_list); +} + +/* + * Print out all the io cards in the list. Also print the column + * headers if told to do so. + */ +void +display_io_cards(struct io_card *list) +{ + struct io_card *p; + + for (p = list; p != NULL; p = p -> next) { + log_printf(dgettext(TEXT_DOMAIN, + "%-4s %-3d %c %-1s %-3d"), + p->bus_type, p->schizo_portid, p->pci_bus, + p->slot_str, p->freq); + + switch (p->pci_bus) { + case 'A': + log_printf(dgettext(TEXT_DOMAIN, " 66 ")); + break; + case 'B': + log_printf(dgettext(TEXT_DOMAIN, " 33 ")); + break; + default: + assert(0); + break; + } + + log_printf(dgettext(TEXT_DOMAIN, + "%-1d,%-1d %-5s %-32.32s"), + p->dev_no, p->func_no, p->status, p->name); + if (strlen(p->name) > 32) + log_printf(dgettext(TEXT_DOMAIN, "+ ")); + else + log_printf(dgettext(TEXT_DOMAIN, " ")); + log_printf(dgettext(TEXT_DOMAIN, "%-22.22s"), p->model); + if (strlen(p->model) > 22) + log_printf(dgettext(TEXT_DOMAIN, "+")); +#ifdef DEBUG + log_printf("%s", p->notes); +#endif + log_printf("\n"); + } +} + +/*ARGSUSED*/ +void +display_ffb(Board_node *board, int table) +{ + /* NOP, since there are no FFB's on this platform. */ +} + + +/* + * local functions + */ + + +static void +disp_envc_status() +{ + int err; + char *system = "SYSTEM"; + picl_nodehdl_t system_node, root; + + log_printf(dgettext(TEXT_DOMAIN, + "\n" + "========================= Environmental Status " + "=========================\n\n")); + + err = picl_initialize(); + if (err != PICL_SUCCESS) + goto err_out; + err = picl_get_root(&root); + if (err != PICL_SUCCESS) + goto err_out; + err = find_child_device(root, system, &system_node); + if (err != PICL_SUCCESS) + goto err_out; + + err = print_temps(system_node); + err |= print_keyswitch(system_node); + err |= print_FSP_LEDS(system_node); + err |= print_disk(system_node); + err |= print_fans(system_node); + err |= print_ps(system_node); + + if (err != PICL_SUCCESS) + goto err_out; + + return; + +err_out: + log_printf(dgettext(TEXT_DOMAIN, + "\nEnvironmental reporting error: %s\n"), + picl_strerror(err)); +} + +static int +print_ps(picl_nodehdl_t system_node) +{ + int i, j, err = 0; + int32_t number; + picl_nodehdl_t *ps; + picl_nodehdl_t *ps_fail_sensor; + char name[PICL_PROPNAMELEN_MAX]; + char fault_state[PICL_PROPNAMELEN_MAX]; + + log_printf(dgettext(TEXT_DOMAIN, "\n\n" + "Power Supplies:\n" + "---------------\n" + "\n" + "Supply Status Fault Fan Fail Temp Fail\n" + "------ ------------ -------- --------- ---------\n")); + + err = fill_device_array_from_id(system_node, "PSVC_PS", &number, &ps); + if (err != PICL_SUCCESS) { + return (err); + } + + for (i = 0; i < MAX_PS; i++) { + err = picl_get_propval_by_name(ps[i], PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) + continue; + + log_printf(dgettext(TEXT_DOMAIN, "%6-s"), name); + err = picl_get_propval_by_name(ps[i], "FaultInformation", + fault_state, PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + free(ps); + return (err); + } + log_printf(dgettext(TEXT_DOMAIN, " [%-12s]"), fault_state); + if (strcmp(fault_state, "NO AC POWER") == 0) { + log_printf("\n"); + continue; + } + + err = fill_device_array_from_id(ps[i], "PSVC_DEV_FAULT_SENSOR", + &number, &ps_fail_sensor); + + if (err != PICL_SUCCESS) { + free(ps); + return (err); + } + log_printf(" "); + for (j = 0; j < MAX_PS_SENSORS; j++) { + err = picl_get_propval_by_name(ps_fail_sensor[j], + "State", fault_state, PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + if (err == PICL_FAILURE) { + break; + } + free(ps); + free(ps_fail_sensor); + return (err); + } + log_printf(dgettext(TEXT_DOMAIN, "%-10s"), fault_state); + } + log_printf("\n"); + free(ps_fail_sensor); + } + + log_printf(dgettext(TEXT_DOMAIN, + "\n=================================\n\n")); + + free(ps); + return (PICL_SUCCESS); +} + +static int +print_fans(picl_nodehdl_t system_node) +{ + int i, err; + int32_t number; + picl_nodehdl_t *fans; + picl_nodehdl_t phdl; + char prop[PICL_PROPNAMELEN_MAX]; + char parent[PICL_PROPNAMELEN_MAX]; + int32_t rpm; + + err = fill_device_array_from_id(system_node, "PSVC_FAN", &number, + &fans); + if (err != PICL_SUCCESS) { + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "\n=================================\n\n" + "Fan Status:\n" + "-----------\n\n" + "Fan Tray Fan RPM Status\n" + "----------- ---- ----- ----------\n")); + + for (i = 0; i < MAX_FANS; i++) { + err = picl_get_propval_by_name(fans[i], PICL_PROP_NAME, prop, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) + continue; + + err = fill_device_from_id(fans[i], "PSVC_PARENT", &phdl); + if (err != PICL_SUCCESS) + continue; + err = picl_get_propval_by_name(phdl, PICL_PROP_NAME, parent, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) + continue; + + log_printf(dgettext(TEXT_DOMAIN, "%-16s"), parent); + + + log_printf(dgettext(TEXT_DOMAIN, "%-16s"), prop); + + err = picl_get_propval_by_name(fans[i], "Fan-speed", + &rpm, sizeof (rpm)); + if (err != PICL_SUCCESS) { + free(fans); + return (err); + } + log_printf(dgettext(TEXT_DOMAIN, "%5d "), rpm); + + err = picl_get_propval_by_name(fans[i], "FaultInformation", + prop, PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + free(fans); + return (err); + } + log_printf(dgettext(TEXT_DOMAIN, " [%s]\n"), prop); + } + log_printf(dgettext(TEXT_DOMAIN, + "\n=================================\n\n")); + free(fans); + return (PICL_SUCCESS); +} + +static int +print_disk(picl_nodehdl_t system_node) +{ + int i, err; + int32_t number; + picl_nodehdl_t *disks; + char state[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_DISK", &number, + &disks); + if (err != PICL_SUCCESS) { + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "Disk Status:\n" + "------------\n")); + for (i = 0; i < MAX_DISKS; i++) { + err = picl_get_propval_by_name(disks[i], "FaultInformation", + state, PICL_PROPNAMELEN_MAX); + + switch (err) { + case PICL_SUCCESS: + log_printf(dgettext(TEXT_DOMAIN, + "DISK %d: [%3s]\n"), i, state); + break; + case PICL_INVALIDHANDLE: + log_printf(dgettext(TEXT_DOMAIN, + "DISK %d: [ NOT PRESENT ]\n"), i); + break; + default: + free(disks); + return (err); + } + } + free(disks); + return (PICL_SUCCESS); +} + +static int +print_FSP_LEDS(picl_nodehdl_t system_node) +{ + int err; + int32_t number; + picl_nodehdl_t *fsp_led; + char fault_state[PICL_PROPNAMELEN_MAX]; + char locate_state[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_FSP_LED", &number, + &fsp_led); + if (err != PICL_SUCCESS) { + return (err); + } + + assert(number == 2); + err = picl_get_propval_by_name(fsp_led[0], "State", &fault_state, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + free(fsp_led); + return (err); + } + err = picl_get_propval_by_name(fsp_led[1], "State", &locate_state, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + free(fsp_led); + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "System LED Status:\n\n" + " LOCATOR FAULT POWER\n" + " ------- ------- -------\n" + " [%3s] [%3s] [ ON]"), + locate_state, fault_state); + + log_printf(dgettext(TEXT_DOMAIN, + "\n\n=================================\n\n")); + free(fsp_led); + return (err); +} + +static int +print_keyswitch(picl_nodehdl_t system_node) +{ + int err; + picl_nodehdl_t *keyswitch; + int32_t number; + char ks_pos[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_KEYSWITCH", &number, + &keyswitch); + if (err != PICL_SUCCESS) { + return (err); + } + err = picl_get_propval_by_name(keyswitch[0], "State", ks_pos, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + free(keyswitch); + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "Front Status Panel:\n" + "-------------------\n" + "Keyswitch position: %s\n\n"), ks_pos); + free(keyswitch); + return (err); +} + +static int +print_temps(picl_nodehdl_t system_node) +{ + int i; + int err; + picl_nodehdl_t *system_ts_nodes; + int32_t temp; + int32_t number; + char label[PICL_PROPNAMELEN_MAX]; + char state[PICL_PROPNAMELEN_MAX]; + char *p; + + err = fill_device_array_from_id(system_node, "PSVC_TS", &number, + &system_ts_nodes); + if (err != PICL_SUCCESS) { + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "System Temperatures (Celsius):\n" + "-------------------------------\n" + "Device\t\tTemperature\tStatus\n" + "---------------------------------------\n")); + + for (i = 0; i < number; i++) { + err = picl_get_propval_by_name(system_ts_nodes[i], + "State", state, sizeof (state)); + if (err != PICL_SUCCESS) { + if (err == PICL_INVALIDHANDLE) { + strcpy(state, "n/a"); + } else { + free(system_ts_nodes); + return (err); + } + } + err = picl_get_propval_by_name(system_ts_nodes[i], + PICL_PROP_NAME, label, PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + if (err == PICL_INVALIDHANDLE) + /* This FRU isn't present. Skip it. */ + continue; + free(system_ts_nodes); + return (err); + } + + /* + * The names in the tree are like "CPU0_DIE_TEMPERATURE_SENSOR". + * All we want to print is up to the first underscore. + */ + p = strchr(label, '_'); + if (p != NULL) + *p = '\0'; + + err = picl_get_propval_by_name(system_ts_nodes[i], + "Temperature", &temp, sizeof (temp)); + if (err != PICL_SUCCESS) { + free(system_ts_nodes); + return (err); + } + log_printf("%s\t\t%3d\t\t%s\n", label, temp, state); + } + + log_printf(dgettext(TEXT_DOMAIN, + "\n=================================\n\n")); + + free(system_ts_nodes); + return (PICL_SUCCESS); +} + +static void +display_hw_revisions(Prom_node *root, Board_node *bdlist) +{ + Prom_node *pnode; + char *value; + + log_printf(dgettext(TEXT_DOMAIN, "\n" + "========================= HW Revisions " + "=======================================\n\n")); + + log_printf(dgettext(TEXT_DOMAIN, + "System PROM revisions:\n" + "----------------------\n")); + + pnode = dev_find_node(root, "openprom"); + if (pnode != NULL) { + value = (char *)get_prop_val(find_prop(pnode, "version")); + log_printf(value); + } + + log_printf(dgettext(TEXT_DOMAIN, "\n\n" + "IO ASIC revisions:\n" + "------------------\n" + " Port\n" + "Model ID Status Version\n" + "-------- ---- ------ -------\n")); + + display_schizo_revisions(bdlist); +} + + +static void +display_schizo_revisions(Board_node *bdlist) +{ + Prom_node *pnode; + int *int_val; + int portid; + int prev_portid = -1; + char *status_a = NULL; + char *status_b = NULL; + int revision; +#ifdef DEBUG + uint32_t a_notes, b_notes; +#endif + int pci_bus; + Board_node *bnode; + bnode = bdlist; + + while (bnode != NULL) { + /* + * search this board node for all Schizos + */ + + for (pnode = dev_find_node_by_compat(bnode->nodes, + SCHIZO_COMPAT_PROP); pnode != NULL; + pnode = dev_next_node_by_compat(pnode, + SCHIZO_COMPAT_PROP)) { + + /* + * get the reg property to determine + * whether we are looking at side A or B + */ + + int_val = (int *)get_prop_val + (find_prop(pnode, "reg")); + if (int_val != NULL) { + int_val ++; /* second integer in array */ + pci_bus = ((*int_val) & 0x7f0000); + } + + /* get portid */ + int_val = (int *)get_prop_val + (find_prop(pnode, "portid")); + if (int_val == NULL) + continue; + + portid = *int_val; + + /* + * If this is a new portid and it is PCI bus B, + * we skip onto the PCI bus A. + */ + if ((portid != prev_portid) && (pci_bus == 0x700000)) { + prev_portid = portid; + /* status */ + status_b = (char *)get_prop_val + (find_prop(pnode, "status")); +#ifdef DEBUG + b_notes = pci_bus; +#endif + continue; /* skip to the next schizo */ + } + + /* + * This must be side A of the same Schizo. + * Gather all its props and display them. + */ +#ifdef DEBUG + a_notes = pci_bus; +#endif + + prev_portid = portid; + + int_val = (int *)get_prop_val + (find_prop(pnode, "version#")); + if (int_val != NULL) + revision = *int_val; + else + revision = -1; + + status_a = (char *)get_prop_val(find_prop + (pnode, "status")); + + log_printf(dgettext(TEXT_DOMAIN, "Schizo ")); + + log_printf(dgettext(TEXT_DOMAIN, "%-3d "), portid, 0); + + + log_printf((status_a == NULL && status_b == NULL) ? + dgettext(TEXT_DOMAIN, " ok ") : + dgettext(TEXT_DOMAIN, " fail ")); + + log_printf(dgettext(TEXT_DOMAIN, " %4d "), + revision); +#ifdef DEBUG + log_printf(" 0x%x 0x%x", a_notes, b_notes); +#endif + log_printf("\n"); + } + bnode = bnode->next; + } +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/cherrystone/common/workfile.c b/usr/src/lib/libprtdiag_psr/sparc/cherrystone/common/workfile.c new file mode 100644 index 0000000000..333d8420da --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/cherrystone/common/workfile.c @@ -0,0 +1,832 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2001, 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Cherrystone platform-specific functions that aren't platform specific + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libprtdiag.h> +#include <sys/mc.h> + + +static Prom_node *dev_next_node_by_compat(Prom_node *root, char *model); +static Prom_node *dev_find_node_by_compat(Prom_node *root, char *model); + +void print_us3_memory_line(int portid, + int bank_id, + uint64_t bank_size, + char *bank_status, + uint64_t dimm_size, + uint32_t intlv, + int seg_id); + +void add_node(Sys_tree *root, Prom_node *pnode); +int do_prominfo(int syserrlog, + char *pgname, + int log_flag, + int prt_flag); + +void *get_prop_val(Prop *prop); +Prop *find_prop(Prom_node *pnode, char *name); +char *get_node_name(Prom_node *pnode); +char *get_node_type(Prom_node *pnode); + +void fill_pci_card_list(Prom_node *pci_instance, + Prom_node *pci_card_node, + struct io_card *pci_card, + struct io_card **pci_card_list, + char **pci_slot_name_arr); + +static Prom_node *next_pci_card(Prom_node *curr_card, int *is_bridge, + int is_pcidev, Prom_node *curr_bridge, + Prom_node * parent_bridge, Prom_node *pci); + +#define HZ_TO_MHZ(x) (((x) + 500000) / 1000000) + +/* + * Start from the current node and return the next node besides + * the current one which has the requested model property. + */ +static Prom_node * +dev_next_node_by_compat(Prom_node *root, char *compat) +{ + Prom_node *node; + + if (root == NULL) + return (NULL); + + /* look at your children first */ + if ((node = dev_find_node_by_compat(root->child, compat)) != NULL) + return (node); + + /* now look at your siblings */ + if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL) + return (node); + + return (NULL); /* not found */ +} + +/* + * Do a depth-first walk of a device tree and + * return the first node with the matching model. + */ +static Prom_node * +dev_find_node_by_compat(Prom_node *root, char *compat) +{ + Prom_node *node; + char *compatible; + char *name; + + if (root == NULL) + return (NULL); + + if (compat == NULL) + return (NULL); + + name = get_node_name(root); + if (name == NULL) + name = ""; + + compatible = (char *)get_prop_val(find_prop(root, "compatible")); + + if (compatible == NULL) + return (NULL); + + if ((strcmp(name, "pci") == 0) && (compatible != NULL) && + (strcmp(compatible, compat) == 0)) { + return (root); /* found a match */ + } + + /* look at your children first */ + if ((node = dev_find_node_by_compat(root->child, compat)) != NULL) + return (node); + + /* now look at your siblings */ + if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL) + return (node); + + return (NULL); /* not found */ +} + +int32_t +find_child_device(picl_nodehdl_t parent, char *child_name, + picl_nodehdl_t *child) +{ + int32_t err; + char name[PICL_PROPNAMELEN_MAX]; + + err = picl_get_propval_by_name(parent, PICL_PROP_CHILD, &(*child), + sizeof (picl_nodehdl_t)); + switch (err) { + case PICL_SUCCESS: + break; + case PICL_PROPNOTFOUND: + err = PICL_INVALIDHANDLE; + return (err); + default: +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "Failed picl_get_propval_by_name with %s\n"), + picl_strerror(err)); +#endif + return (err); + } + + err = picl_get_propval_by_name(*child, PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + +#ifdef WORKFILE_DEBUG + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed the get name for root\n")); + log_printf(dgettext(TEXT_DOMAIN, "%s\n"), picl_strerror(err)); + } +#endif + + if (strcmp(name, child_name) == 0) + return (err); + + while (err != PICL_PROPNOTFOUND) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, "child name is %s\n"), name); +#endif + err = picl_get_propval_by_name(*child, PICL_PROP_PEER, + &(*child), sizeof (picl_nodehdl_t)); + switch (err) { + case PICL_SUCCESS: + err = picl_get_propval_by_name(*child, PICL_PROP_NAME, + name, PICL_PROPNAMELEN_MAX); + if (strcmp(name, child_name) == 0) + return (err); + break; + case PICL_PROPNOTFOUND: + break; + default: +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "Failed picl_get_propval_by_name with %s\n"), + picl_strerror(err)); +#endif + return (err); + } + } + err = PICL_INVALIDHANDLE; + return (err); +} + +int32_t +fill_device_from_id(picl_nodehdl_t device_id, char *assoc_id, + picl_nodehdl_t *device) +{ + int32_t err; + picl_prophdl_t tbl_hdl; + picl_prophdl_t reference_property; + + err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl, + sizeof (picl_prophdl_t)); + if (err != PICL_SUCCESS) { +#ifdef WORKFILE_DEBUG + if (err != PICL_INVALIDHANDLE) { + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_from_id failure in " + "picl_get_propval_by_name err is %s\n"), + picl_strerror(err)); + } +#endif + return (err); + } + + err = picl_get_next_by_row(tbl_hdl, &reference_property); + if (err != PICL_SUCCESS) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_from_id failure in picl_get_next_by_row" + " err is %s\n"), picl_strerror(err)); +#endif + return (err); + } + + /* get node associated with reference property */ + err = picl_get_propval(reference_property, &(*device), + sizeof (picl_nodehdl_t)); + +#ifdef WORKFILE_DEBUG + if (err != 0) { + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_from_id failure in picl_get_propval" + " err is %s\n"), picl_strerror(err)); + } +#endif + + return (err); +} + +int32_t +fill_device_array_from_id(picl_nodehdl_t device_id, char *assoc_id, + int32_t *number_of_devices, picl_nodehdl_t *device_array[]) +{ + int32_t err; + int i; + picl_prophdl_t tbl_hdl; + picl_prophdl_t entry; + int devs = 0; + + err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl, + sizeof (picl_prophdl_t)); + if ((err != PICL_SUCCESS) && (err != PICL_INVALIDHANDLE)) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure in " + "picl_get_propval_by_name err is %s\n"), picl_strerror(err)); +#endif + return (err); + } + + entry = tbl_hdl; + while (picl_get_next_by_row(entry, &entry) == 0) + ++devs; + + *device_array = calloc((devs), sizeof (picl_nodehdl_t)); + if (*device_array == NULL) { + +#ifdef WORFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure getting memory" + " for array\n")); +#endif + return (PICL_FAILURE); + } + + entry = tbl_hdl; + for (i = 0; i < devs; i++) { + err = picl_get_next_by_row(entry, &entry); + if (err != 0) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure in " + "picl_get_next_by_row err is %s\n"), + picl_strerror(err)); +#endif + return (err); + } + + /* get node associated with reference property */ + err = picl_get_propval(entry, &((*device_array)[i]), + sizeof (picl_nodehdl_t)); + if (err != 0) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure in " + "picl_get_propval err is %s\n"), picl_strerror(err)); +#endif + + return (err); + } + } + *number_of_devices = devs; + return (err); +} + +/* + * add_node + * + * This function adds a board node to the board structure where that + * that node's physical component lives. + */ +void +add_node(Sys_tree *root, Prom_node *pnode) +{ + int board = -1; + int portid = -1; + + void *value = NULL; + Board_node *bnode = NULL; + Prom_node *p = NULL; + + /* Get the board number of this board from the portid prop */ + value = get_prop_val(find_prop(pnode, "portid")); + if (value != NULL) { + portid = *(int *)value; + } + + board = CHERRYSTONE_GETSLOT(portid); + + if ((bnode = find_board(root, board)) == NULL) { + bnode = insert_board(root, board); + } + + /* now attach this prom node to the board list */ + /* Insert this node at the end of the list */ + pnode->sibling = NULL; + if (bnode->nodes == NULL) + bnode->nodes = pnode; + else { + p = bnode->nodes; + while (p->sibling != NULL) + p = p->sibling; + p->sibling = pnode; + } +} + +/* + * This function provides formatting of the memory config + * information that get_us3_mem_regs() and display_us3_banks() code has + * gathered. It overrides the generic print_us3_memory_line() code + * which prints an error message. + */ +void +print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, + char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id) +{ + log_printf(dgettext(TEXT_DOMAIN, + "\n %-1c %2d %2d %4lldMB %11-s %4lldMB " + " %2d-way %d"), + CHERRYSTONE_GETSLOT_LABEL(portid), portid, + (bank_id % 4), bank_size, bank_status, dimm_size, + intlv, seg_id, 0); +} + +/* + * We call do_devinfo() in order to use the libdevinfo device tree + * instead of OBP's device tree. + */ +int +do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag) +{ + return (do_devinfo(syserrlog, pgname, log_flag, prt_flag)); +} + +/* + * return the property value for the Prop + * passed in. (When using libdevinfo) + */ +void * +get_prop_val(Prop *prop) +{ + if (prop == NULL) + return (NULL); + + return ((void *)(prop->value.val_ptr)); +} + +/* + * Search a Prom node and retrieve the property with the correct + * name. (When using libdevinfo) + */ +Prop * +find_prop(Prom_node *pnode, char *name) +{ + Prop *prop; + + if (pnode == NULL) + return (NULL); + + if (pnode->props == NULL) + return (NULL); + + prop = pnode->props; + if (prop == NULL) + return (NULL); + + if (prop->name.val_ptr == NULL) + return (NULL); + + while ((prop != NULL) && (strcmp((char *)(prop->name.val_ptr), name))) { + prop = prop->next; + } + return (prop); +} + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the name property. + * (When using libdevinfo) + */ +char * +get_node_name(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("name", (char *)prop->name.val_ptr) == 0) + return (prop->value.val_ptr); + prop = prop->next; + } + return (NULL); +} + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the device_type property. + * (When using libdevinfo) + */ +char * +get_node_type(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("device_type", (char *)prop->name.val_ptr) == 0) + return (prop->value.val_ptr); + prop = prop->next; + } + return (NULL); +} + + +/* + * Fills in the i/o card list to be displayed later in display_pci(); + */ +void +fill_pci_card_list(Prom_node * pci_instance, Prom_node * pci_card_node, + struct io_card *pci_card, + struct io_card **pci_card_list, char **slot_name_arr) +{ + Prom_node *pci_bridge_node; + Prom_node *pci_parent_bridge; + int *int_val; + int pci_bridge = FALSE; + int pci_bridge_dev_no = -1; + int portid; + int pci_bus; + char buf[MAXSTRLEN]; + char *slot_name = NULL; /* info in "slot-names" prop */ + char *child_name; + char *name; + char *type; + void *value; + + while (pci_card_node != NULL) { + int is_pci = FALSE; + type = NULL; + name = NULL; + /* If it doesn't have a name, skip it */ + name = (char *)get_prop_val( + find_prop(pci_card_node, "name")); + if (name == NULL) { + pci_card_node = pci_card_node->sibling; + continue; + } + + /* + * Get the portid of the schizo that this card + * lives under. + */ + portid = -1; + value = get_prop_val(find_prop(pci_instance, "portid")); + if (value != NULL) { + portid = *(int *)value; + } + pci_card->schizo_portid = portid; + if (pci_card->schizo_portid != 8) { + /* + * Schizo0 (portid 8) has no slots on Cherrystone. + * So if that's who we're looking at, we're done. + */ + return; + } + + /* + * Find out whether this is PCI bus A or B + * using the 'reg' property. + */ + int_val = (int *)get_prop_val(find_prop(pci_instance, "reg")); + + if (int_val != NULL) { + int_val++; /* skip over first integer */ + pci_bus = ((*int_val) & 0x7f0000); + if (pci_bus == 0x600000) + pci_card->pci_bus = 'A'; + else if (pci_bus == 0x700000) + pci_card->pci_bus = 'B'; + else { + assert(0); /* should never happen */ + pci_card->pci_bus = '-'; + } + } else { + assert(0); /* should never happen */ + pci_card->pci_bus = '-'; + } + + /* + * get dev# and func# for this card from the + * 'reg' property. + */ + int_val = (int *)get_prop_val( + find_prop(pci_card_node, "reg")); + if (int_val != NULL) { + pci_card->dev_no = (((*int_val) & 0xF800) >> 11); + pci_card->func_no = (((*int_val) & 0x700) >> 8); + } else { + pci_card->dev_no = -1; + pci_card->func_no = -1; + } + + switch (pci_card->pci_bus) { + case 'A': + if ((pci_card->dev_no < 1 || pci_card->dev_no > 2) && + (!pci_bridge)) { + pci_card_node = pci_card_node->sibling; + continue; + } + break; + case 'B': + if ((pci_card->dev_no < 2 || pci_card->dev_no > 5) && + (!pci_bridge)) { + pci_card_node = pci_card_node->sibling; + continue; + } + break; + default: + pci_card_node = pci_card_node->sibling; + continue; + } + + type = (char *)get_prop_val( + find_prop(pci_card_node, "device_type")); + /* + * If this is a pci-bridge, then store its dev# + * as its children nodes need this to get their slot#. + * We set the pci_bridge flag so that we know we are + * looking at a pci-bridge node. This flag gets reset + * every time we enter this while loop. + */ + + /* + * Check for a PCI-PCI Bridge for PCI and cPCI + * IO Boards using the name and type properties. + */ + if ((type != NULL) && (strncmp(name, "pci", 3) == 0) && + (strcmp(type, "pci") == 0)) { + pci_bridge_node = pci_card_node; + is_pci = TRUE; + if (!pci_bridge) { + pci_bridge_dev_no = pci_card->dev_no; + pci_parent_bridge = pci_bridge_node; + pci_bridge = TRUE; + } + } + + /* + * Get slot-names property from slot_names_arr. + * If we are the child of a pci_bridge we use the + * dev# of the pci_bridge as an index to get + * the slot number. We know that we are a child of + * a pci-bridge if our parent is the same as the last + * pci_bridge node found above. + */ + if (pci_card->dev_no != -1) { + /* + * We compare this cards parent node with the + * pci_bridge_node to see if it's a child. + */ + if (pci_card_node->parent != pci_instance && + pci_bridge) { + /* use dev_no of pci_bridge */ + if (pci_card->pci_bus == 'B') { + slot_name = + slot_name_arr[pci_bridge_dev_no -2]; + } else { + slot_name = + slot_name_arr[pci_bridge_dev_no -1]; + } + } else { + if (pci_card->pci_bus == 'B') { + slot_name = + slot_name_arr[pci_card->dev_no-2]; + } else { + slot_name = + slot_name_arr[pci_card->dev_no-1]; + } + } + + if (slot_name != NULL && + strlen(slot_name) != 0) { + /* Slot num is last char in string */ + (void) snprintf(pci_card->slot_str, MAXSTRLEN, + "%c", slot_name[strlen(slot_name) - 1]); + } else { + (void) snprintf(pci_card->slot_str, MAXSTRLEN, + "-"); + } + + } else { + (void) snprintf(pci_card->slot_str, MAXSTRLEN, + "%c", '-'); + } + + /* + * Check for failed status. + */ + if (node_failed(pci_card_node)) + strcpy(pci_card->status, "fail"); + else + strcpy(pci_card->status, "ok"); + + /* Get the model of this pci_card */ + value = get_prop_val(find_prop(pci_card_node, "model")); + if (value == NULL) + pci_card->model[0] = '\0'; + else { + (void) snprintf(pci_card->model, MAXSTRLEN, "%s", + (char *)value); + } + /* + * The card may have a "clock-frequency" but we + * are not interested in that. Instead we get the + * "clock-frequency" of the PCI Bus that the card + * resides on. PCI-A can operate at 33Mhz or 66Mhz + * depending on what card is plugged into the Bus. + * PCI-B always operates at 33Mhz. + */ + int_val = get_prop_val(find_prop(pci_instance, + "clock-frequency")); + if (int_val != NULL) { + pci_card->freq = HZ_TO_MHZ(*int_val); + } else { + pci_card->freq = -1; + } + + /* + * Figure out how we want to display the name + */ + value = get_prop_val(find_prop(pci_card_node, + "compatible")); + if (value != NULL) { + /* use 'name'-'compatible' */ + (void) snprintf(buf, MAXSTRLEN, "%s-%s", name, + (char *)value); + } else { + /* just use 'name' */ + (void) snprintf(buf, MAXSTRLEN, "%s", name); + } + name = buf; + + /* + * If this node has children, add the device_type + * of the child to the name value of this pci_card-> + */ + child_name = (char *)get_node_name(pci_card_node->child); + if ((pci_card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(pci_card_node->child, + "device_type")); + if (value != NULL) { + /* add device_type of child to name */ + (void) snprintf(pci_card->name, MAXSTRLEN, + "%s/%s (%s)", name, child_name, + (char *)value); + } else { + /* just add childs name */ + (void) snprintf(pci_card->name, MAXSTRLEN, + "%s/%s", name, child_name); + } + } else { + (void) snprintf(pci_card->name, MAXSTRLEN, "%s", + (char *)name); + } + + /* + * If this is a pci-bridge, then add the word + * 'pci-bridge' to its model. If we can't find + * a model, then we just describe what the device + * is based on some properties. + */ + if (pci_bridge) { + if (strlen(pci_card->model) == 0) { + if (pci_card_node->parent == pci_bridge_node) + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s", "device on pci-bridge"); + else if (pci_card_node->parent + == pci_parent_bridge) + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s", "pci-bridge/pci-bridge"); + else + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s", "PCI-BRIDGE"); + } + else + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s/pci-bridge", pci_card->model); + } + /* insert this pci_card in the list to be displayed later */ + + *pci_card_list = insert_io_card(*pci_card_list, pci_card); + + /* + * If we are dealing with a pci-bridge, we need to move + * down to the children of this bridge if there are any. + * + * If we are not, we are either dealing with a regular + * card (in which case we move onto the sibling of this + * card) or we are dealing with a child of a pci-bridge + * (in which case we move onto the child's siblings or + * if there are no more siblings for this child, we + * move onto the parents siblings). + */ + pci_card_node = next_pci_card(pci_card_node, &pci_bridge, + is_pci, pci_bridge_node, + pci_parent_bridge, pci_instance); + } /* end-while */ +} + +/* + * Helper function for fill_pci_card_list(). Indicates which + * card node to go to next. + * Parameters: + * ----------- + * Prom_node * curr_card: pointer to the current card node + * + * int * is_bridge: indicates whether or not the card (is | is on) + * a pci bridge + * + * int is_pcidev: indicates whether or not the current card + * is a pci bridge + * + * Prom_node * curr_bridge: pointer to the current pci bridge. Eg: + * curr_card->parent. + * + * Prom_node * parent_bridge: pointer to the first pci bridge encountered. + * we could have nested pci bridges, this would + * be the first one. + * + * Prom_node * pci: pointer to the pci instance that we are attached to. + * This would be parent_bridge->parent, or + * curr_node->parent, if curr_node is not on a pci bridge. + */ +static Prom_node * +next_pci_card(Prom_node *curr_card, int *is_bridge, int is_pcidev, + Prom_node *curr_bridge, Prom_node *parent_bridge, + Prom_node *pci) +{ + Prom_node * curr_node = curr_card; + if (*is_bridge) { + /* + * is_pcidev is used to prevent us from following the + * children of something like a scsi device. + */ + if (curr_node->child != NULL && is_pcidev) { + curr_node = curr_node->child; + } else { + curr_node = curr_node->sibling; + if (curr_node == NULL) { + curr_node = curr_bridge->sibling; + while (curr_node == NULL && + curr_bridge != parent_bridge && + curr_bridge != NULL) { + curr_node = + curr_bridge->parent->sibling; + curr_bridge = curr_bridge->parent; + if (curr_node != NULL && + curr_node->parent == pci) + break; + } + if (curr_bridge == NULL || + curr_node == NULL || + curr_node->parent == pci || + curr_bridge == parent_bridge || + curr_node == parent_bridge) { + *is_bridge = FALSE; + } + } + } + + } else { + curr_node = curr_node->sibling; + } + return (curr_node); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/daktari/Makefile b/usr/src/lib/libprtdiag_psr/sparc/daktari/Makefile new file mode 100644 index 0000000000..af6d11db23 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/daktari/Makefile @@ -0,0 +1,101 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/daktari/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= daktari.o + +include ../Makefile.com + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../libprtdiag/inc +IFLAGS += -I $(SRC)/cmd/picl/plugins/sun4u/psvc/psvcobj + +LDLIBS += -lpicl +LINTFLAGS += $(IFLAGS) + +# +# links in /usr/platform +# +LINKED_PLATFORMS = SUNW,Sun-Fire-V890 + +LINKED_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%) +LINKED_LIB_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib) +LINKED_PRTDIAG_DIRS = \ + $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib/libprtdiag_psr.so.1) + + +PLATFORM=SUNW,Sun-Fire-880 + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) $(LINKED_PRTDIAG_DIRS) + +# +# install rules +# + +$(PLATLIBS): + $(INS.dir) + +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/daktari; pwd ; $(MAKE) $(USR_PSM_LIB_DIR) + +$(LINKED_DIRS): $(USR_PLAT_DIR) + -$(INS.dir.root.sys) + +$(LINKED_LIB_DIRS): $(LINKED_DIRS) + -$(INS.dir.root.sys) + +$(LINKED_PRTDIAG_DIRS): $(USR_PLAT_DIR) + -$(INS.slink6) + +# +# used for message files +# +POFILE= libprtdiag_psr_daktari.po +POFILES= daktari.po + + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/daktari.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/sparc/daktari/common/daktari.c b/usr/src/lib/libprtdiag_psr/sparc/daktari/common/daktari.c new file mode 100644 index 0000000000..4139eec282 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/daktari/common/daktari.c @@ -0,0 +1,1346 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Daktari Platform specific functions. + * + * called when : + * machine_type == MTYPE_DAKTARI + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <kstat.h> +#include <string.h> +#include <assert.h> +#include <libintl.h> +#include <note.h> + +#include <sys/openpromio.h> +#include <sys/sysmacros.h> +#include <sys/daktari.h> + +#include <pdevinfo.h> +#include <display.h> +#include <pdevinfo_sun4u.h> +#include <display_sun4u.h> +#include <libprtdiag.h> + +#include <picl.h> +#include "workfile.c" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +#define DAK_MAX_SLOTS_PER_IO_BD 9 +#define DAK_MAX_DISKS 12 +#define DAK_MAX_FSP_LEDS 2 +#define DAK_MAX_PS 3 +#define DAK_MAX_PS_VOLTAGE_SENSORS 4 +#define DAK_MAX_PS_FAULT_SENSORS 3 +#define DAK_MAX_FANS 10 +#ifndef SCHIZO_COMPAT_PROP +#define SCHIZO_COMPAT_PROP "pci108e,8001" +#endif + +#define MULTIPLE_BITS_SET(x) ((x)&((x)-1)) + +extern int print_flag; + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (workgroup server systems only) + */ +void display_cpu_devices(Sys_tree *tree); +void display_cpus(Board_node *board); +void display_pci(Board_node *board); +void display_io_cards(struct io_card *list); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void display_ffb(Board_node *board, int table); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); + +/* local functions */ +static int disp_envc_status(void); +static int dak_env_print_temps(picl_nodehdl_t); +static int dak_env_print_keyswitch(picl_nodehdl_t); +static int dak_env_print_FSP_LEDS(picl_nodehdl_t); +static int dak_env_print_disk(picl_nodehdl_t); +static int dak_env_print_fans(picl_nodehdl_t); +static int dak_env_print_ps(picl_nodehdl_t); + +static void dak_display_hw_revisions(Prom_node *root, + Board_node *bnode); +static void display_schizo_revisions(Board_node *bdlist); + + +/* + * Defining the error_check function in order to return the + * appropriate error code. + */ +/*ARGSUSED0*/ +int +error_check(Sys_tree *tree, struct system_kstat_data *kstats) +{ + int exit_code = 0; /* init to all OK */ + /* + * silently check for any types of machine errors + */ + print_flag = 0; + if (disp_fail_parts(tree)) { + /* set exit_code to show failures */ + exit_code = 1; + } + print_flag = 1; + + return (exit_code); +} + +/* + * disp_fail_parts + * + * Display the failed parts in the system. This function looks for + * the status property in all PROM nodes. On systems where + * the PROM does not support passing diagnostic information + * through the device tree, this routine will be silent. + */ +int +disp_fail_parts(Sys_tree *tree) +{ + int exit_code = 0; + int system_failed = 0; + Board_node *bnode = tree->bd_list; + Prom_node *pnode; + + /* go through all of the boards looking for failed units. */ + while (bnode != NULL) { + /* find failed chips */ + pnode = find_failed_node(bnode->nodes); + if ((pnode != NULL) && !system_failed) { + system_failed = 1; + exit_code = 1; + if (print_flag == 0) { + return (exit_code); + } + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, "Failed Field " + "Replaceable Units (FRU) in System:\n")); + log_printf("==========================" + "====================\n"); + } + while (pnode != NULL) { + void *value; + char *name; /* node name string */ + char *type; /* node type string */ + char *board_type = NULL; + + value = get_prop_val(find_prop(pnode, "status")); + name = get_node_name(pnode); + + /* sanity check of data retrieved from PROM */ + if ((value == NULL) || (name == NULL)) { + pnode = next_failed_node(pnode); + continue; + } + + /* Find the board type of this board */ + if (bnode->board_type == CPU_BOARD) { + board_type = "CPU"; + } else { + board_type = "IO"; + } + + log_printf(dgettext(TEXT_DOMAIN, "%s unavailable " + "on %s Board #%d\n"), name, board_type, + bnode->board_num); + + log_printf(dgettext(TEXT_DOMAIN, + "\tPROM fault string: %s\n"), value); + + log_printf(dgettext(TEXT_DOMAIN, + "\tFailed Field Replaceable Unit is ")); + + /* + * Determine whether FRU is CPU module, system + * board, or SBus card. + */ + if ((name != NULL) && (strstr(name, "sbus"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "SBus Card %d\n"), + get_sbus_slot(pnode)); + + } else if (((name = get_node_name(pnode->parent)) != + NULL) && (strstr(name, "pci"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "PCI Card %d"), + get_pci_device(pnode)); + + } else if (((type = get_node_type(pnode)) != NULL) && + (strstr(type, "cpu"))) { + + log_printf(dgettext(TEXT_DOMAIN, "UltraSPARC " + "module Board %d Module %d\n"), 0, + get_id(pnode)); + + } else { + log_printf(dgettext(TEXT_DOMAIN, + "%s board %d\n"), board_type, + bnode->board_num); + } + pnode = next_failed_node(pnode); + } + bnode = bnode->next; + } + + if (!system_failed) { + log_printf(dgettext(TEXT_DOMAIN, + "No failures found in System\n")); + log_printf("===========================\n\n"); + } + + if (system_failed) + return (1); + else + return (0); +} + +/*ARGSUSED*/ +void +display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) +{ + /* Display failed units */ + (void) disp_fail_parts(tree); +} + +/*ARGSUSED*/ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + Board_node *bnode = tree->bd_list; + + log_printf(dgettext(TEXT_DOMAIN, + "========================= Memory Configuration" + " ===============================\n" + "\n Logical Logical" + " Logical " + "\n MC Bank Bank Bank" + " DIMM Interleave Interleaved" + "\n Brd ID num size " + "Status Size " + "Factor with" + "\n---- --- ---- ------ " + "----------- ------ " + "---------- -----------")); + + while (bnode != NULL) { + if (get_us3_mem_regs(bnode)) { + log_printf(dgettext(TEXT_DOMAIN, + "\nFailed to get memory information.\n")); + return; + } + bnode = bnode->next; + } + + /* Display what we have found */ + display_us3_banks(); +} + +void +display_cpu_devices(Sys_tree *tree) +{ + Board_node *bnode; + + /* + * Display the table header for CPUs . Then display the CPU + * frequency, cache size, and processor revision of all cpus. + */ + log_printf(dgettext(TEXT_DOMAIN, + "\n" + "=========================" + " CPUs " + "===============================================" + "\n" + "\n" + " Run E$ CPU CPU \n" + "Brd CPU MHz MB Impl. Mask \n" + "--- ----- ---- ---- ------- ---- \n")); + + /* Now display all of the cpus on each board */ + bnode = tree->bd_list; + if (bnode == NULL) { + log_printf(dgettext(TEXT_DOMAIN, + "CPU Board list was NULL\n")); + } + while (bnode != NULL) { + display_cpus(bnode); + bnode = bnode->next; + } + + log_printf("\n"); +} + +/* + * Display the CPUs present on this board. + */ +void +display_cpus(Board_node *board) +{ + Prom_node *cpu; + int freq; /* CPU clock frequency */ + int ecache_size; /* External cache size */ + int *l3_shares; + int *mid; + int *impl; + int *mask; + int *coreid; + char fru_prev = 'X'; /* Valid frus are 'A','B','C','D' */ + int mid_prev; + int ecache_size_prev = 0; + char fru_name; + + /* + * display the CPUs' operating frequency, cache size, impl. field + * and mask revision. + */ + for (cpu = dev_find_type(board->nodes, "cpu"); cpu != NULL; + cpu = dev_next_type(cpu, "cpu")) { + + mid = (int *)get_prop_val(find_prop(cpu, "portid")); + if (mid == NULL) + mid = (int *)get_prop_val(find_prop(cpu, "cpuid")); + freq = DAK_CLK_FREQ_TO_MHZ(get_cpu_freq(cpu)); + ecache_size = get_ecache_size(cpu); + impl = (int *)get_prop_val(find_prop(cpu, "implementation#")); + mask = (int *)get_prop_val(find_prop(cpu, "mask#")); + l3_shares = + (int *)get_prop_val(find_prop(cpu, "l3-cache-sharing")); + + /* Do not display a failed CPU node */ + if ((impl == NULL) || (freq == 0) || (node_failed(cpu))) + continue; + + /* Board number */ + fru_name = (char)('A' + DAK_GETSLOT(*mid)); + + if (CPU_IMPL_IS_CMP(*impl)) { + coreid = (int *)get_prop_val(find_prop(cpu, "reg")); + if (coreid == NULL) { + continue; + } + if ((fru_prev == 'X') || + ((fru_prev != 'X') && + (fru_name != fru_prev))) { + fru_prev = fru_name; + mid_prev = *mid; + ecache_size_prev = ecache_size; + continue; + } else { + /* + * Some CMP chips have a split E$, + * so the size for both cores is added + * together to get the total size for + * the chip. + * + * Still, other CMP chips have E$ (L3) + * which is logically shared, so the + * total size is equal to the core size. + */ + if ((l3_shares == NULL) || + ((l3_shares != NULL) && + MULTIPLE_BITS_SET(*l3_shares))) { + ecache_size += ecache_size_prev; + } + ecache_size_prev = 0; + fru_prev = 'X'; + } + } + + log_printf("%2c", fru_name); + + /* CPU Module ID */ + if (CPU_IMPL_IS_CMP(*impl)) { + log_printf("%3d,%3d", mid_prev, *mid, 0); + } else + log_printf(" %d ", *mid); + + /* Running frequency */ + log_printf(" %4d ", freq); + + /* Ecache size */ + if (ecache_size == 0) + log_printf(dgettext(TEXT_DOMAIN, "%3s "), + "N/A"); + else + log_printf("%4.1f ", + (float)ecache_size / (float)(1<<20)); + + /* Implementation */ + if (impl == NULL) { + log_printf(dgettext(TEXT_DOMAIN, "%s "), + "N/A"); + } else { + if (IS_CHEETAH(*impl)) + log_printf("%7s", "US-III ", 0); + else if (IS_CHEETAH_PLUS(*impl)) + log_printf("%7s", "US-III+", 0); + else if (IS_JAGUAR(*impl)) + log_printf("%7s", "US-IV ", 0); + else if (IS_PANTHER(*impl)) + log_printf("%7s", "US-IV+ ", 0); + else + log_printf("%-7x", *impl, 0); + } + + /* CPU Mask */ + if (mask == NULL) { + log_printf(dgettext(TEXT_DOMAIN, " %3s "), + "N/A"); + } else { + log_printf(dgettext(TEXT_DOMAIN, " %2d.%d"), + (*mask >> 4) & 0xf, *mask & 0xf); + } + + log_printf("\n"); + } +} + +/* + * display_pci + * Display all the PCI IO cards on this board. + */ +void +display_pci(Board_node *board) +{ + struct io_card *card_list = NULL; + struct io_card card; + void *value; + Prom_node *pci; + Prom_node *card_node; + char *slot_name_arr[DAK_MAX_SLOTS_PER_IO_BD] = {NULL}; + int i; +#ifdef DEBUG + int slot_name_bits; +#endif + + if (board == NULL) + return; + + memset(&card, 0, sizeof (struct io_card)); + /* Initialize all the common information */ + card.display = TRUE; + card.board = board->board_num; + + /* + * Search for each pci instance, then find/display all nodes under + * each instance node found. + */ + for (pci = dev_find_node_by_compat(board->nodes, SCHIZO_COMPAT_PROP); + pci != NULL; + pci = dev_next_node_by_compat(pci, SCHIZO_COMPAT_PROP)) { + (void) snprintf(card.bus_type, MAXSTRLEN, + dgettext(TEXT_DOMAIN, "PCI")); + /* + * Get slot-name properties from parent node and + * store them in an array. + */ + value = (char *)get_prop_val( + find_prop(pci, "slot-names")); + + if (value != NULL) { +#ifdef DEBUG + /* save the 4 byte bitmask */ + slot_name_bits = *(int *)value; +#endif + + /* array starts after first int */ + slot_name_arr[0] = (char *)value + sizeof (int); + for (i = 1; i < DAK_MAX_SLOTS_PER_IO_BD; i++) { + slot_name_arr[i] = (char *)slot_name_arr[i - 1] + + strlen(slot_name_arr[i - 1]) +1; + } + } + /* + * Search for Children of this node ie. Cards. + * Note: any of these cards can be a pci-bridge + * that itself has children. If we find a + * pci-bridge we need to handle it specially. + */ + card_node = pci->child; + /* Generate the list of pci cards on pci instance: pci */ + fill_pci_card_list(pci, card_node, &card, &card_list, + slot_name_arr); + } /* end-for */ + + display_io_cards(card_list); + free_io_cards(card_list); + log_printf("\n"); +} + +/* + * Print out all the io cards in the list. Also print the column + * headers if told to do so. + */ +void +display_io_cards(struct io_card *list) +{ + static int banner = 0; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + if (banner == FALSE) { + log_printf(dgettext(TEXT_DOMAIN, + " Bus Max\n" + " IO Port Bus Freq Bus Dev," + "\n" + "Brd Type ID Side Slot MHz Freq " + "Func State Name " + "Model\n" + /* ---------Brd IO Port Bus Slot Bus Max Dev Stat */ + "---- ---- ---- ---- ---- ---- ---- ----" + " ----- " + "-------------------------------- " + "----------------------\n")); + banner = TRUE; + } + + for (p = list; p != NULL; p = p -> next) { + log_printf(dgettext(TEXT_DOMAIN, "I/O ")); + log_printf("%-4s ", p->bus_type); + log_printf("%-3d ", p->schizo_portid); + log_printf("%c ", p->pci_bus); + log_printf("%-1s ", p->slot_str); + log_printf("%-3d ", p->freq); + switch (p->pci_bus) { + case 'A': + log_printf(dgettext(TEXT_DOMAIN, " 66 ")); + break; + case 'B': + log_printf(dgettext(TEXT_DOMAIN, " 33 ")); + break; + default: + log_printf(dgettext(TEXT_DOMAIN, " - ")); + break; + } + + log_printf("%-1d,%-1d ", p->dev_no, p->func_no); + log_printf("%-5s ", p->status); + log_printf("%-32.32s", p->name); + if (strlen(p->name) > 32) + log_printf(dgettext(TEXT_DOMAIN, "+ ")); + else + log_printf(dgettext(TEXT_DOMAIN, " ")); + log_printf("%-22.22s", p->model); + if (strlen(p->model) > 22) + log_printf(dgettext(TEXT_DOMAIN, "+")); + +#ifdef DEBUG + log_printf(dgettext(TEXT_DOMAIN, "%s "), p->notes); +#endif + log_printf("\n"); + } +} + +/* + * display_ffb + * + * There are no FFB's on a Daktari, however in the generic library, + * the display_ffb() function is implemented so we have to define an + * empty function here. + */ +/* ARGSUSED */ +void +display_ffb(Board_node *board, int table) +{} + + +/* + * ---------------------------------------------------------------------------- + */ + +/* ARGSUSED */ +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + /* NOTE(ARGUNUSED(kstats)) */ + /* + * Now display the last powerfail time and the fatal hardware + * reset information. We do this under a couple of conditions. + * First if the user asks for it. The second is if the user + * told us to do logging, and we found a system failure. + */ + if (flag) { + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + (void) disp_envc_status(); + + /* platform_disp_prom_version(tree); */ + dak_display_hw_revisions(root, tree->bd_list); + } +} + +/* + * local functions + */ + +/* + * disp_envc_status + * + * This routine displays the environmental status passed up from + * device drivers via the envlibobj.so library. + * This is a Daktari specific environmental information display routine. + */ +int +disp_envc_status() +{ + int err; + char *system = "SYSTEM"; + picl_nodehdl_t system_node, root; + + err = picl_initialize(); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "picl_initialize failed\n" + "%s\nCannot display environmental status\n"), + picl_strerror(err)); + return (err); + } + err = picl_get_root(&root); + err = find_child_device(root, system, &system_node); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_node_by_path for the SYSTEM node " + "failed\n" + "%s\nCannot display environmental status\n"), + picl_strerror(err)); + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "\n" + "========================= " + "Environmental Status " + "=========================" + "\n" + "\n")); + + dak_env_print_temps(system_node); + dak_env_print_keyswitch(system_node); + dak_env_print_FSP_LEDS(system_node); + dak_env_print_disk(system_node); + dak_env_print_fans(system_node); + dak_env_print_ps(system_node); + + (void) picl_shutdown(); + return (0); +} + +int +dak_env_print_ps(picl_nodehdl_t system_node) +{ + int i, r, fail, err = 0; + int32_t number; + char name[PICL_PROPNAMELEN_MAX]; + picl_nodehdl_t *ps; + picl_nodehdl_t *ps_fail[DAK_MAX_PS_FAULT_SENSORS]; + picl_nodehdl_t *ps_I_sensor[DAK_MAX_PS_VOLTAGE_SENSORS]; + int32_t volts[DAK_MAX_PS_VOLTAGE_SENSORS]; + char fault_state + [DAK_MAX_PS_FAULT_SENSORS][PICL_PROPNAMELEN_MAX]; + char ps_state[PICL_PROPNAMELEN_MAX]; + /* Printing out the Power Supply Heading information */ + log_printf(dgettext(TEXT_DOMAIN, + "Power Supplies:\n" + "---------------\n" + " " + "Current Drain:\n" + "Supply Status Fan Fail Temp Fail CS Fail " + "3.3V 5V 12V 48V\n" + "------ ------------ -------- --------- " + "------- ---- -- --- ---\n")); + + err = fill_device_array_from_id(system_node, "PSVC_PS", &number, + &ps); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed in fill_device_array_from_id for PS\n" + "%s\n"), picl_strerror(err)); + return (err); + } + /* Printing out the Power Supply Status information */ + for (i = 0; i < DAK_MAX_PS; i++) { + /* + * Re initialize the fail variable so that if + * one power supply fails, they don't all do also. + */ + fail = 0; + + err = picl_get_propval_by_name(ps[i], PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + continue; + } + err = picl_get_propval_by_name(ps[i], "State", ps_state, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "Error getting ps[%d]'s state: %s"), + i, picl_strerror(err)); + } + + err = fill_device_array_from_id(ps[i], "PSVC_DEV_FAULT_SENSOR", + &number, &ps_fail[i]); + + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed to get present PS fault sensors\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + err = fill_device_array_from_id(ps[i], "PSVC_PS_I_SENSOR", + &number, &ps_I_sensor[i]); + + if ((err != PICL_SUCCESS) && (err != PICL_INVALIDHANDLE)) { + log_printf(dgettext(TEXT_DOMAIN, + "failed to get present PS I sensors\n" + "%s\n"), picl_strerror(err)); + } + + log_printf("%s", name); + + /* + * If the AC cord is unplugged, then the power supply + * sensors will have unreliable values. In this case, + * skip to the next power supply. + */ + if (strcmp(ps_state, "HOTPLUGGED") == 0) { + log_printf(dgettext(TEXT_DOMAIN, + " UNPLUGGED\n")); + continue; + } + + for (r = 0; r < DAK_MAX_PS_FAULT_SENSORS; r++) { + err = picl_get_propval_by_name(ps_fail[i][r], "State", + fault_state[r], PICL_PROPNAMELEN_MAX); + if (err == PICL_SUCCESS) { + fail = + strcmp(fault_state[r], "OFF") + + fail; + } else { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_propval_by_name for ps " + "fault state failed\n" + "%s\n"), picl_strerror(err)); + return (err); + } + } + for (r = 0; r < DAK_MAX_PS_VOLTAGE_SENSORS; r++) { + err = picl_get_propval_by_name(ps_I_sensor[i][r], + "AtoDSensorValue", &volts[r], + sizeof (int32_t)); + } + + if (fail != 0) { + log_printf(dgettext(TEXT_DOMAIN, + " FAIL ")); + for (r = 0; r < DAK_MAX_PS_FAULT_SENSORS; r++) { + log_printf(dgettext(TEXT_DOMAIN, " %-4s"), + fault_state[r]); + } + } else { + log_printf(dgettext(TEXT_DOMAIN, " GOOD ")); + for (r = 0; r < DAK_MAX_PS_FAULT_SENSORS; r++) { + log_printf(dgettext(TEXT_DOMAIN, " ")); + } + } + for (r = 0; r < DAK_MAX_PS_VOLTAGE_SENSORS; r++) { + log_printf(dgettext(TEXT_DOMAIN, " %2d"), volts[r]); + } + log_printf("\n"); + } + log_printf("\n"); + return (err); +} + +int +dak_env_print_fans(picl_nodehdl_t system_node) +{ + int i, err = 0; + int32_t number, fan_speed; + picl_nodehdl_t *fans; + char name[PICL_PROPNAMELEN_MAX]; + char enabled[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_FAN", &number, + &fans); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed in fill_device_array_from_id " + "for FAN\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, + "=================================\n")); + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, "Fan Bank :\n")); + log_printf(dgettext(TEXT_DOMAIN, "----------\n")); + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, "Bank Speed " + " Status Fan State\n")); + log_printf(dgettext(TEXT_DOMAIN, " ( RPMS )" + " \n")); + log_printf(dgettext(TEXT_DOMAIN, "---- --------" + " --------- ---------\n")); + + + for (i = 0; i < DAK_MAX_FANS; i++) { + char fan_state[PICL_PROPNAMELEN_MAX]; + fan_speed = 0; + err = picl_get_propval_by_name(fans[i], PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + if (err == PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, "%16-s"), name); + } else { + continue; + } + + err = picl_get_propval_by_name(fans[i], "Fan-speed", + &fan_speed, sizeof (int32_t)); + if ((err != PICL_SUCCESS) && (err != PICL_INVALIDHANDLE)) { + log_printf(dgettext(TEXT_DOMAIN, + "failed in picl_get_propval_by_name for " + "fan speed\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + if ((strcmp(name, "CPU0_PRIM_FAN") != 0) && + (strcmp(name, "CPU1_PRIM_FAN") != 0)) { + err = picl_get_propval_by_name(fans[i], "Fan-switch", + enabled, PICL_PROPNAMELEN_MAX); + if ((err != PICL_SUCCESS) && + (err != PICL_INVALIDHANDLE)) { + log_printf(dgettext(TEXT_DOMAIN, + "failed in picl_get_propval_by_name for" + " fan enabled/disabled\n" + "%s\n"), picl_strerror(err)); + return (err); + } + /* + * Display the fan's speed and whether or not + * it's enabled. + */ + if (strcmp(enabled, "ON") == 0) { + log_printf(dgettext(TEXT_DOMAIN, + "\t %4d [ENABLED]"), + fan_speed); + } else { + log_printf(dgettext(TEXT_DOMAIN, + "\t 0 [DISABLED]")); + } + + } else { + /* Display the fan's speed */ + log_printf(dgettext(TEXT_DOMAIN, "\t %4d"), + fan_speed); + log_printf(dgettext(TEXT_DOMAIN, + " [ENABLED]")); + } + + err = picl_get_propval_by_name(fans[i], "State", fan_state, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_propval_by_name failed: %s"), + picl_strerror(err)); + return (err); + } + log_printf(dgettext(TEXT_DOMAIN, "\t %s\n"), fan_state); + } + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, + "=================================\n")); + log_printf("\n"); + + return (err); +} + +int +dak_env_print_disk(picl_nodehdl_t system_node) +{ + int i, err; + int32_t number; + picl_nodehdl_t *disks; + picl_nodehdl_t disk_slots[DAK_MAX_DISKS]; + picl_nodehdl_t disk_fault_leds[DAK_MAX_DISKS]; + picl_nodehdl_t disk_remove_leds[DAK_MAX_DISKS]; + char led_state[PICL_PROPNAMELEN_MAX]; + char name[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_DISK", &number, + &disks); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed in fill_device_array_from_id for " + "DISK\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "Disk Status:\n" + " Presence Fault LED Remove LED\n")); + + for (i = 0; i < DAK_MAX_DISKS; i++) { + err = picl_get_propval_by_name(disks[i], PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + switch (err) { + case PICL_SUCCESS: + log_printf(dgettext(TEXT_DOMAIN, "DISK %2d: [%7s]"), + i, "PRESENT"); + break; + case PICL_INVALIDHANDLE: + log_printf(dgettext(TEXT_DOMAIN, "DISK %2d: [%7s]"), + i, "EMPTY"); + log_printf("\n"); + continue; + default: + log_printf(dgettext(TEXT_DOMAIN, + "Failed picl_get_propval_by_name for " + "disk %d with %s\n"), i, picl_strerror(err)); + return (err); + } + + err = fill_device_from_id(disks[i], "PSVC_PARENT", + &(disk_slots[i])); + switch (err) { + case PICL_SUCCESS: + break; + case PICL_INVALIDHANDLE: + continue; + default: + log_printf(dgettext(TEXT_DOMAIN, + "failed in fill_device_from_id for disk " + "slot\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + err = fill_device_from_id(disk_slots[i], "PSVC_SLOT_FAULT_LED", + &disk_fault_leds[i]); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed in fill_device_from_id for disk slot " + "fault led\n" + "%s\n"), picl_strerror(err)); + return (err); + } + err = picl_get_propval_by_name(disk_fault_leds[i], + "State", led_state, PICL_PROPNAMELEN_MAX); + if (err == PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, " [%3s]"), + led_state); + } else { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_propval_by_name for fault led_state" + " failed\n" + "%s\n"), picl_strerror(err)); + return (err); + } + err = fill_device_from_id(disk_slots[i], "PSVC_SLOT_REMOVE_LED", + &disk_remove_leds[i]); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed in fill_device_from_id for disk slot " + "remove led\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + err = picl_get_propval_by_name(disk_remove_leds[i], + "State", led_state, PICL_PROPNAMELEN_MAX); + if (err == PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + " [%3s]"), led_state); + } else { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_propval_by_name for remove" + " led_state failed\n" + "%s\n"), picl_strerror(err)); + return (err); + } + log_printf("\n"); + } + return (err); +} + +int +dak_env_print_FSP_LEDS(picl_nodehdl_t system_node) +{ + int i, err = 0; + int32_t number; + picl_nodehdl_t *fsp_leds; + char led_state[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_FSP_LED", &number, + &fsp_leds); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed in fill_device_array_from_id for " + "FSP_LED\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "System LED Status:\n" + " GEN FAULT REMOVE\n")); + for (i = 0; i < DAK_MAX_FSP_LEDS; i++) { + err = picl_get_propval_by_name(fsp_leds[i], "State", + led_state, PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_propval_by_name for led_state" + " failed\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + " [%3s]"), led_state); + } + log_printf("\n\n"); + log_printf(dgettext(TEXT_DOMAIN, + " DISK FAULT ")); + log_printf(dgettext(TEXT_DOMAIN, "POWER FAULT\n")); + for (i = 2; i < 4; i++) { + err = picl_get_propval_by_name(fsp_leds[i], "State", + led_state, PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_propval_by_name for led_state" + " failed\n" + "%s\n"), picl_strerror(err)); + return (err); + } + log_printf(dgettext(TEXT_DOMAIN, " [%3s]"), + led_state); + } + log_printf("\n\n"); + log_printf(dgettext(TEXT_DOMAIN, + " LEFT THERMAL FAULT " + "RIGHT THERMAL FAULT\n")); + for (i = 4; i < 6; i++) { + err = picl_get_propval_by_name(fsp_leds[i], "State", + led_state, PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_propval_by_name for led_state " + "failed\n" + "%s\n"), picl_strerror(err)); + return (err); + } + log_printf(dgettext(TEXT_DOMAIN, " [%3s]"), + led_state); + } + log_printf("\n\n"); + log_printf(dgettext(TEXT_DOMAIN, + " LEFT DOOR " + "RIGHT DOOR\n")); + for (i = 6; i < 8; i++) { + err = picl_get_propval_by_name(fsp_leds[i], "State", + led_state, PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_propval_by_name for led_state" + " failed\n" + "%s\n"), picl_strerror(err)); + return (err); + } + log_printf(dgettext(TEXT_DOMAIN, " [%3s]"), + led_state); + } + log_printf("\n\n"); + log_printf(dgettext(TEXT_DOMAIN, + "=================================\n")); + log_printf("\n"); + + return (err); +} + +int +dak_env_print_keyswitch(picl_nodehdl_t system_node) +{ + int err = 0; + picl_nodehdl_t *keyswitch; + int32_t number; + char ks_pos[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_KEYSWITCH", &number, + &keyswitch); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed in fill_device_array_from_id for " + " PSVC_KEYSWITCH\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + err = picl_get_propval_by_name(keyswitch[0], "State", ks_pos, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "picl_get_propval_by_name for keyswitch state " + "failed\n" + "%s\n"), picl_strerror(err)); + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "Front Status Panel:\n" + "-------------------\n" + "Keyswitch position: " + "%s\n"), ks_pos); + log_printf("\n"); + + return (err); +} + +int +dak_env_print_temps(picl_nodehdl_t system_node) +{ + int i; + int err; + picl_nodehdl_t *system_ts_nodes; + int32_t temp; + int32_t number; + char label[PICL_PROPNAMELEN_MAX]; + char state[PICL_PROPNAMELEN_MAX]; + char *p; + + err = fill_device_array_from_id(system_node, "PSVC_TS", &number, + &system_ts_nodes); + if (err != PICL_SUCCESS) { + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "System Temperatures (Celsius):\n" + "-------------------------------\n" + "Device\t\tTemperature\tStatus\n" + "---------------------------------------\n")); + + for (i = 0; i < number; i++) { + err = picl_get_propval_by_name(system_ts_nodes[i], + "State", state, sizeof (state)); + if (err != PICL_SUCCESS) { + if (err == PICL_INVALIDHANDLE) { + strcpy(state, "n/a"); + } else { + log_printf("%s\n", picl_strerror(err)); + return (err); + } + } + err = picl_get_propval_by_name(system_ts_nodes[i], + PICL_PROP_NAME, label, PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + if (err == PICL_INVALIDHANDLE) + /* This FRU isn't present. Skip it. */ + continue; + log_printf("%s\n", picl_strerror(err)); + return (err); + } + + /* + * The names in the tree are like "CPU0_DIE_TEMPERATURE_SENSOR". + * All we want to print is up to the first underscore. + */ + p = strchr(label, '_'); + if (p != NULL) + *p = '\0'; + + err = picl_get_propval_by_name(system_ts_nodes[i], + "Temperature", &temp, sizeof (temp)); + if (err != PICL_SUCCESS) { + log_printf("%s\n", picl_strerror(err)); + return (err); + } + log_printf("%s\t\t%3d\t\t%s\n", label, temp, state); + } + + log_printf(dgettext(TEXT_DOMAIN, + "\n=================================\n\n")); + + return (PICL_SUCCESS); +} + +static void +dak_display_hw_revisions(Prom_node *root, Board_node *bdlist) +{ + Prom_node *pnode; + char *value; + + log_printf(dgettext(TEXT_DOMAIN, "\n" + "========================= HW Revisions " + "=======================================\n\n")); + + log_printf(dgettext(TEXT_DOMAIN, + "System PROM revisions:\n" + "----------------------\n")); + + pnode = dev_find_node(root, "openprom"); + if (pnode != NULL) { + value = (char *)get_prop_val(find_prop(pnode, "version")); + log_printf(value); + } + + log_printf(dgettext(TEXT_DOMAIN, "\n\n" + "IO ASIC revisions:\n" + "------------------\n" + " Port\n" + "Model ID Status Version\n" + "-------- ---- ------ -------\n")); + + display_schizo_revisions(bdlist); +} + +static void +display_schizo_revisions(Board_node *bdlist) +{ + Prom_node *pnode; + int *int_val; + int portid; + int prev_portid = -1; + char *status_a = NULL; + char *status_b = NULL; + int revision; +#ifdef DEBUG + uint32_t a_notes, b_notes; +#endif + int pci_bus; + Board_node *bnode; + bnode = bdlist; + + while (bnode != NULL) { + /* + * search this board node for all Schizos + */ + for (pnode = dev_find_node_by_compat(bnode->nodes, + SCHIZO_COMPAT_PROP); pnode != NULL; + pnode = dev_next_node_by_compat(pnode, + SCHIZO_COMPAT_PROP)) { + + /* + * get the reg property to determine + * whether we are looking at side A or B + */ + int_val = (int *)get_prop_val + (find_prop(pnode, "reg")); + if (int_val != NULL) { + int_val ++; /* second integer in array */ + pci_bus = ((*int_val) & 0x7f0000); + } + + /* get portid */ + int_val = (int *)get_prop_val + (find_prop(pnode, "portid")); + if (int_val == NULL) + continue; + + portid = *int_val; + + /* + * If this is a new portid and it is PCI bus B, + * we skip onto the PCI bus A. + */ + if ((portid != prev_portid) && (pci_bus == 0x700000)) { + prev_portid = portid; + /* status */ + status_b = (char *)get_prop_val + (find_prop(pnode, "status")); +#ifdef DEBUG + b_notes = pci_bus; +#endif + continue; /* skip to the next schizo */ + } + + /* + * This must be side A of the same Schizo. + * Gather all its props and display them. + */ +#ifdef DEBUG + a_notes = pci_bus; +#endif + + prev_portid = portid; + + int_val = (int *)get_prop_val + (find_prop(pnode, "version#")); + if (int_val != NULL) + revision = *int_val; + else + revision = -1; + + status_a = (char *)get_prop_val(find_prop + (pnode, "status")); + + log_printf(dgettext(TEXT_DOMAIN, "Schizo ")); + + log_printf(dgettext(TEXT_DOMAIN, "%-3d "), portid, 0); + + + log_printf((status_a == NULL && status_b == NULL) ? + dgettext(TEXT_DOMAIN, " ok ") : + dgettext(TEXT_DOMAIN, " fail ")); + + log_printf(dgettext(TEXT_DOMAIN, " %4d "), + revision); +#ifdef DEBUG + log_printf(" 0x%x 0x%x", a_notes, b_notes); +#endif + log_printf("\n"); + } + bnode = bnode->next; + } +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/daktari/common/workfile.c b/usr/src/lib/libprtdiag_psr/sparc/daktari/common/workfile.c new file mode 100644 index 0000000000..33ffdfb1da --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/daktari/common/workfile.c @@ -0,0 +1,939 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2000, 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Daktari Platform specific functions. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libdevinfo.h> +#include <alloca.h> +#include <inttypes.h> +#include <libprtdiag.h> +#include <sys/mc.h> + +#define EXIT_MSG(msg, err) \ + { printf("\n%s failed with %d\n", msg, err); exit(err); } + +/* we only need the 5 LSB of the portid to calculate the board number */ +#define DAK_SAFARI_ID_MASK 0x1F /* 5 bits */ +#define DAK_NODE_MASK 0x1F /* 5 bits */ +#define DAK_PORTID_NODE_SHIFT 5 +#define DAK_MIN_CPU_SAFARI_ID 0 /* 0x00 */ +#define DAK_MAX_CPU_SAFARI_ID 23 /* 0x17 */ +#define DAK_MIN_IO_SAFARI_ID 24 /* 0x18 */ +#define DAK_MAX_IO_SAFARI_ID 31 /* 0x1F */ +#define NUM_MBANKS_PER_MC 4 + +#define DAK_CLK_FREQ_TO_MHZ(x) (((x) + 500000) / 1000000) + +/* + * DAK_PORTID_IS_CPU_TYPE + * + * If the portid associated with a CPU board is passed in, TRUE is returned, + * otherwise FALSE. + */ +#define DAK_PORTID_IS_CPU_TYPE(portid) \ + (((((portid) & DAK_SAFARI_ID_MASK) >= DAK_MIN_CPU_SAFARI_ID) && \ + (((portid) & DAK_SAFARI_ID_MASK) <= DAK_MAX_CPU_SAFARI_ID)) ? \ + TRUE: FALSE) +/* + * DAK_CPU_BD_PORTID_TO_BD_NUM + * + * If the portid associated with a CPU board is passed in, the board number + * associated with this portid is returned, otherwise -1. + */ +#define DAK_CPU_BD_PORTID_TO_BD_NUM(portid) \ + ((DAK_PORTID_IS_CPU_TYPE(portid)) ? \ + (((portid) & DAK_SAFARI_ID_MASK) / 4) : (-1)) + +/* + * DAK_PORTID_IS_IO_TYPE + * + * If the portid associated with an IO board is passed in, TRUE is returned, + * otherwise FALSE. + */ +#define DAK_PORTID_IS_IO_TYPE(portid) \ + (((((portid) & DAK_SAFARI_ID_MASK) >= DAK_MIN_IO_SAFARI_ID) && \ + (((portid) & DAK_SAFARI_ID_MASK) <= DAK_MAX_IO_SAFARI_ID)) ? \ + TRUE: FALSE) + +/* + * DAK_IO_BD_PORTID_TO_BD_NUM + * + * If the portid associated with an IO board is passed in, the board number + * associated with this portid is returned, otherwise -1. + */ +#define DAK_IO_BD_PORTID_TO_BD_NUM(portid) \ + (DAK_PORTID_IS_IO_TYPE(portid) ? \ + (((((portid) & DAK_SAFARI_ID_MASK) - 24) / 2) + 6) : (-1)) + +/* + * DAK_PORTID_TO_BOARD_NUM + * + * If a valid portid is passed in, this macro returns the board number + * associated with it, otherwise it returns -1. + */ + +#define DAK_PORTID_TO_BOARD_NUM(portid) \ + ((DAK_PORTID_IS_CPU_TYPE(portid)) ? \ + (DAK_CPU_BD_PORTID_TO_BD_NUM(portid)) : \ + ((DAK_PORTID_IS_IO_TYPE(portid)) ? \ + DAK_IO_BD_PORTID_TO_BD_NUM(portid) : (-1))) + + +/* Local Functions */ +char *get_node_name(Prom_node *pnode); +char *get_node_type(Prom_node *pnode); +void add_node(Sys_tree *root, Prom_node *pnode); +Prop *find_prop(Prom_node *pnode, char *name); +int do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag); +void *get_prop_val(Prop *prop); +char *get_node_type(Prom_node *pnode); +int get_us3_mem_regs(Board_node *bnode); + +void fill_pci_card_list(Prom_node *pci_instance, + Prom_node *pci_card_node, + struct io_card *pci_card, + struct io_card **pci_card_list, + char **pci_slot_name_arr); + +static Prom_node *next_pci_card(Prom_node *curr_card, int *is_bridge, + int is_pcidev, Prom_node *curr_bridge, + Prom_node * parent_bridge, Prom_node *pci); + +static Prom_node *dev_next_node_by_compat(Prom_node *root, char *compat); +static Prom_node *dev_find_node_by_compat(Prom_node *root, char *compat); +static Board_node *daktari_insert_board(Sys_tree *root, int board); +static Board_node *daktari_find_board(Sys_tree *root, int board); + +static int32_t find_child_device(picl_nodehdl_t, char *, picl_nodehdl_t *); +static int32_t fill_device_from_id(picl_nodehdl_t, char *, picl_nodehdl_t *); +static int32_t fill_device_array_from_id(picl_nodehdl_t, char *, int32_t *, + picl_nodehdl_t **); + +/* Overlaying routines */ + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the name property. + */ +char * +get_node_name(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) + return (NULL); + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("name", (char *)prop->name.val_ptr) == 0) + return (prop->value.val_ptr); + prop = prop->next; + } + return (NULL); +} + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the name property. + */ +char * +get_node_type(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("device_type", (char *)prop->name.val_ptr) == 0) + return (prop->value.val_ptr); + prop = prop->next; + } + return (NULL); +} + +/* + * add_node + * + * This function adds a board node to the board structure where that + * that node's physical component lives. + */ +void +add_node(Sys_tree *root, Prom_node *pnode) +{ + int board = -1; + int portid = -1; + + void *value = NULL; + Board_node *bnode = NULL; + Prom_node *p = NULL; + + /* Get the board number of this board from the portid prop */ + value = get_prop_val(find_prop(pnode, "portid")); + if (value != NULL) { + portid = *(int *)value; + } + board = DAK_PORTID_TO_BOARD_NUM(portid); + /* board = DAK_GETSLOT(portid); */ + /* find the board node with the same board number */ + if ((bnode = daktari_find_board(root, board)) == NULL) { + bnode = daktari_insert_board(root, board); + } + + /* now attach this prom node to the board list */ + /* Insert this node at the end of the list */ + pnode->sibling = NULL; + if (bnode->nodes == NULL) + bnode->nodes = pnode; + else { + p = bnode->nodes; + while (p->sibling != NULL) + p = p->sibling; + p->sibling = pnode; + } +} + +/* + * Search a Prom node and retrieve the property with the correct + * name. + */ +Prop * +find_prop(Prom_node *pnode, char *name) +{ + Prop *prop; + + if (pnode == NULL) + return (NULL); + + if (pnode->props == NULL) + return (NULL); + + prop = pnode->props; + + if (prop == NULL) + return (NULL); + + if (prop->name.val_ptr == NULL) + return (NULL); + + while ((prop != NULL) && (strcmp((char *)(prop->name.val_ptr), name))) { + prop = prop->next; + } + return (prop); +} + +int +do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag) +{ + return (do_devinfo(syserrlog, pgname, log_flag, prt_flag)); +} + + +/* + * return the property value for the Prop + * passed in. + */ +void * +get_prop_val(Prop *prop) +{ + if (prop == NULL) + return (NULL); + + return ((void *)(prop->value.val_ptr)); +} + +/* Local Routines */ + +/* + * Start from the current node and return the next node besides + * the current one which has the requested model property. + */ +static Prom_node * +dev_next_node_by_compat(Prom_node *root, char *compat) +{ + Prom_node *node; + + if (root == NULL) + return (NULL); + + /* look at your children first */ + if ((node = dev_find_node_by_compat(root->child, compat)) != NULL) + return (node); + + /* now look at your siblings */ + if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL) + return (node); + + return (NULL); /* not found */ +} + +/* + * Do a depth-first walk of a device tree and + * return the first node with the matching model. + */ +static Prom_node * +dev_find_node_by_compat(Prom_node *root, char *compat) +{ + Prom_node *node; + char *compatible; + char *name; + + if (root == NULL) + return (NULL); + + if (compat == NULL) + return (NULL); + + name = get_node_name(root); + if (name == NULL) + name = ""; + + compatible = (char *)get_prop_val(find_prop(root, "compatible")); + + if (compatible == NULL) + return (NULL); + + if ((strcmp(name, "pci") == 0) && (compatible != NULL) && + (strcmp(compatible, compat) == 0)) { + return (root); /* found a match */ + } + + /* look at your children first */ + if ((node = dev_find_node_by_compat(root->child, compat)) != NULL) + return (node); + + /* now look at your siblings */ + if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL) + return (node); + + return (NULL); /* not found */ +} + + +/* + * Add a board to the system list in order (sorted by board#). + * Initialize all pointer fields to NULL. + */ +static Board_node * +daktari_insert_board(Sys_tree *root, int board) +{ + Board_node *bnode; + Board_node *temp = root->bd_list; + + if ((bnode = (Board_node *) malloc(sizeof (Board_node))) == NULL) { + perror("malloc"); + exit(1); + } + + bnode->nodes = NULL; + bnode->next = NULL; + bnode->board_num = board; + bnode->board_type = UNKNOWN_BOARD; + + if (temp == NULL) + root->bd_list = bnode; + + else if (temp->board_num > board) { + bnode->next = temp; + root->bd_list = bnode; + + } else { + while ((temp->next != NULL) && (board > temp->next->board_num)) + temp = temp->next; + + bnode->next = temp->next; + temp->next = bnode; + } + root->board_cnt++; + + return (bnode); +} + +/* + * Find the requested board struct in the system device tree. + * + * This function overrides the functionality of the generic find_board() + * function in libprtdiag, but since we need to pass another parameter, + * we cannot simply overlay the symbol table. + */ +static Board_node * +daktari_find_board(Sys_tree *root, int board) +{ + Board_node *bnode = root->bd_list; + + while ((bnode != NULL) && (board != bnode->board_num)) { + bnode = bnode->next; + } + return (bnode); +} + + +int32_t +find_child_device(picl_nodehdl_t parent, char *child_name, + picl_nodehdl_t *child) +{ + int32_t err; + char name[PICL_PROPNAMELEN_MAX]; + + err = picl_get_propval_by_name(parent, PICL_PROP_CHILD, &(*child), + sizeof (picl_nodehdl_t)); + switch (err) { + case PICL_SUCCESS: + break; + case PICL_PROPNOTFOUND: + err = PICL_INVALIDHANDLE; + return (err); + default: +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "Failed picl_get_propval_by_name with %s\n"), + picl_strerror(err)); +#endif + return (err); + } + + err = picl_get_propval_by_name(*child, PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + +#ifdef WORKFILE_DEBUG + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed the get name for root\n"), 0); + log_printf(dgettext(TEXT_DOMAIN, "%s\n"), + picl_strerror(err), 0); + } +#endif + + if (strcmp(name, child_name) == 0) + return (err); + + while (err != PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(*child, PICL_PROP_PEER, + &(*child), sizeof (picl_nodehdl_t)); + switch (err) { + case PICL_SUCCESS: + err = picl_get_propval_by_name(*child, PICL_PROP_NAME, + name, PICL_PROPNAMELEN_MAX); + if (strcmp(name, child_name) == 0) + return (err); + break; + case PICL_PROPNOTFOUND: + break; + default: +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "Failed picl_get_propval_by_name with %s\n"), + picl_strerror(err), 0); +#endif + return (err); + } + } + err = PICL_INVALIDHANDLE; + return (err); +} + +int32_t +fill_device_from_id(picl_nodehdl_t device_id, char *assoc_id, + picl_nodehdl_t *device) +{ + int32_t err; + picl_prophdl_t tbl_hdl; + picl_prophdl_t reference_property; + + err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl, + sizeof (picl_prophdl_t)); + if (err != PICL_SUCCESS) { +#ifdef WORKFILE_DEBUG + if (err != PICL_INVALIDHANDLE) { + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_from_id failure in " + "picl_get_propval_by_name err is %s\n"), + picl_strerror(err), 0); + } +#endif + return (err); + } + + err = picl_get_next_by_row(tbl_hdl, &reference_property); + if (err != PICL_SUCCESS) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_from_id failure in picl_get_next_by_row" + " err is %s\n"), picl_strerror(err), 0); +#endif + return (err); + } + + /* get node associated with reference property */ + err = picl_get_propval(reference_property, &(*device), + sizeof (picl_nodehdl_t)); + +#ifdef WORKFILE_DEBUG + if (err != 0) { + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_from_id failure in picl_get_propval" + " err is %s\n"), picl_strerror(err), 0); + } +#endif + return (err); +} + +int32_t +fill_device_array_from_id(picl_nodehdl_t device_id, char *assoc_id, + int32_t *number_of_devices, picl_nodehdl_t *device_array[]) +{ + int32_t err; + int i; + picl_prophdl_t tbl_hdl; + picl_prophdl_t entry; + int devs = 0; + err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl, + sizeof (picl_prophdl_t)); + if ((err != PICL_SUCCESS) && (err != PICL_INVALIDHANDLE)) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure in " + "picl_get_propval_by_name err is %s\n"), + picl_strerror(err), 0); +#endif + return (err); + } + + entry = tbl_hdl; + while (picl_get_next_by_row(entry, &entry) == 0) + ++devs; + + *device_array = calloc((devs), sizeof (picl_nodehdl_t)); + if (*device_array == NULL) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure getting memory" + " for array\n"), 0); +#endif + return (PICL_FAILURE); + } + + entry = tbl_hdl; + for (i = 0; i < (devs); i++) { + err = picl_get_next_by_row(entry, &entry); + if (err != 0) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure in " + "picl_get_next_by_row err is %s\n"), + picl_strerror(err), 0); +#endif + return (err); + } + + /* get node associated with reference property */ + err = picl_get_propval(entry, &((*device_array)[i]), + sizeof (picl_nodehdl_t)); + if (err != 0) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure in " + "picl_get_propval err is %s\n"), + picl_strerror(err), 0); +#endif + return (err); + } + } + *number_of_devices = devs; + return (err); +} + +/* + * This function provides formatting of the memory config + * information that get_us3_mem_regs() and display_us3_banks() code has + * gathered. It overrides the generic print_us3_memory_line() code + * which prints an error message. + */ +void +print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, + char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id) +{ + int mcid; + mcid = portid; + + log_printf(dgettext(TEXT_DOMAIN, + "\n %-1c %2d %2d %4lldMB %11-s %4lldMB " + " %2d-way %d"), + 'A' + DAK_GETSLOT(portid), mcid, (bank_id % 4), + bank_size, bank_status, dimm_size, intlv, seg_id, 0); +} + + +/* + * Fills in the i/o card list to be displayed later in display_pci(); + */ +void +fill_pci_card_list(Prom_node * pci_instance, Prom_node * pci_card_node, + struct io_card *pci_card, + struct io_card **pci_card_list, char **slot_name_arr) +{ + Prom_node *pci_bridge_node; + Prom_node *pci_parent_bridge; + int *int_val; + int pci_bridge = FALSE; + int pci_bridge_dev_no = -1; + int portid; + int pci_bus; + char buf[MAXSTRLEN]; + char *slot_name = NULL; /* info in "slot-names" prop */ + char *child_name; + char *name; + char *type; + void *value; + + while (pci_card_node != NULL) { + int is_pci = FALSE; + type = NULL; + name = NULL; + /* If it doesn't have a name, skip it */ + name = (char *)get_prop_val( + find_prop(pci_card_node, "name")); + if (name == NULL) { + pci_card_node = pci_card_node->sibling; + continue; + } + + /* + * Get the portid of the schizo that this card + * lives under. + */ + portid = -1; + value = get_prop_val(find_prop(pci_instance, "portid")); + if (value != NULL) { + portid = *(int *)value; + } + pci_card->schizo_portid = portid; + /* + * Find out whether this is PCI bus A or B + * using the 'reg' property. + */ + int_val = (int *)get_prop_val(find_prop(pci_instance, "reg")); + + if (int_val != NULL) { + int_val++; /* skip over first integer */ + pci_bus = ((*int_val) & 0x7f0000); + if (pci_bus == 0x600000) + pci_card->pci_bus = 'A'; + else if (pci_bus == 0x700000) + pci_card->pci_bus = 'B'; + else + pci_card->pci_bus = '-'; + } else { + pci_card->pci_bus = '-'; + } + + if ((pci_card->schizo_portid == 8) && + (pci_card->pci_bus == 'A')) { + pci_card_node = pci_card_node->sibling; + continue; + } + + /* + * get dev# and func# for this card from the + * 'reg' property. + */ + int_val = (int *)get_prop_val( + find_prop(pci_card_node, "reg")); + if (int_val != NULL) { + pci_card->dev_no = (((*int_val) & 0xF800) >> 11); + pci_card->func_no = (((*int_val) & 0x700) >> 8); + } else { + pci_card->dev_no = -1; + pci_card->func_no = -1; + } + + type = (char *)get_prop_val( + find_prop(pci_card_node, "device_type")); + /* + * If this is a pci-bridge, then store its dev# + * as its children nodes need this to get their slot#. + * We set the pci_bridge flag so that we know we are + * looking at a pci-bridge node. This flag gets reset + * every time we enter this while loop. + */ + + /* + * Check for a PCI-PCI Bridge for PCI and cPCI + * IO Boards using the name and type properties. + */ + if ((type != NULL) && (strncmp(name, "pci", 3) == 0) && + (strcmp(type, "pci") == 0)) { + pci_bridge_node = pci_card_node; + is_pci = TRUE; + if (!pci_bridge) { + pci_bridge_dev_no = pci_card->dev_no; + pci_parent_bridge = pci_bridge_node; + pci_bridge = TRUE; + } + } + if ((pci_card->pci_bus == 'B') && (pci_card->dev_no == 1) && + (!pci_bridge)) { + pci_card_node = pci_card_node->sibling; + continue; + } + + /* + * Get slot-names property from slot_names_arr. + * If we are the child of a pci_bridge we use the + * dev# of the pci_bridge as an index to get + * the slot number. We know that we are a child of + * a pci-bridge if our parent is the same as the last + * pci_bridge node found above. + */ + if (pci_card->dev_no != -1) { + /* + * We compare this cards parent node with the + * pci_bridge_node to see if it's a child. + */ + if (pci_card_node->parent != pci_instance && + pci_bridge) { + /* use dev_no of pci_bridge */ + if (pci_card->pci_bus == 'B') { + slot_name = + slot_name_arr[pci_bridge_dev_no -2]; + } else { + slot_name = + slot_name_arr[pci_bridge_dev_no -1]; + } + } else { + if (pci_card->pci_bus == 'B') { + slot_name = + slot_name_arr[pci_card->dev_no-2]; + } else { + slot_name = + slot_name_arr[pci_card->dev_no-1]; + } + } + + if (slot_name != NULL && + strlen(slot_name) != 0) { + /* Slot num is last char in string */ + (void) snprintf(pci_card->slot_str, MAXSTRLEN, + "%c", slot_name[strlen(slot_name) - 1]); + } else { + (void) snprintf(pci_card->slot_str, MAXSTRLEN, + "-"); + } + + } else { + (void) snprintf(pci_card->slot_str, MAXSTRLEN, + "%c", '-'); + } + + /* + * Check for failed status. + */ + if (node_failed(pci_card_node)) + strcpy(pci_card->status, "fail"); + else + strcpy(pci_card->status, "ok"); + + /* Get the model of this pci_card */ + value = get_prop_val(find_prop(pci_card_node, "model")); + if (value == NULL) + pci_card->model[0] = '\0'; + else { + (void) snprintf(pci_card->model, MAXSTRLEN, "%s", + (char *)value); + } + /* + * The card may have a "clock-frequency" but we + * are not interested in that. Instead we get the + * "clock-frequency" of the PCI Bus that the card + * resides on. PCI-A can operate at 33Mhz or 66Mhz + * depending on what card is plugged into the Bus. + * PCI-B always operates at 33Mhz. + */ + int_val = get_prop_val(find_prop(pci_instance, + "clock-frequency")); + if (int_val != NULL) { + pci_card->freq = DAK_CLK_FREQ_TO_MHZ(*int_val); + } else { + pci_card->freq = -1; + } + + /* + * Figure out how we want to display the name + */ + value = get_prop_val(find_prop(pci_card_node, + "compatible")); + if (value != NULL) { + /* use 'name'-'compatible' */ + (void) snprintf(buf, MAXSTRLEN, "%s-%s", name, + (char *)value); + } else { + /* just use 'name' */ + (void) snprintf(buf, MAXSTRLEN, "%s", name); + } + name = buf; + + /* + * If this node has children, add the device_type + * of the child to the name value of this pci_card-> + */ + child_name = (char *)get_node_name(pci_card_node->child); + if ((pci_card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(pci_card_node->child, + "device_type")); + if (value != NULL) { + /* add device_type of child to name */ + (void) snprintf(pci_card->name, MAXSTRLEN, + "%s/%s (%s)", name, child_name, + (char *)value); + } else { + /* just add childs name */ + (void) snprintf(pci_card->name, MAXSTRLEN, + "%s/%s", name, child_name); + } + } else { + (void) snprintf(pci_card->name, MAXSTRLEN, "%s", + (char *)name); + } + + /* + * If this is a pci-bridge, then add the word + * 'pci-bridge' to its model. If we can't find + * a model, then we just describe what the device + * is based on some properties. + */ + if (pci_bridge) { + if (strlen(pci_card->model) == 0) { + if (pci_card_node->parent == pci_bridge_node) + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s", "device on pci-bridge"); + else if (pci_card_node->parent + == pci_parent_bridge) + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s", "pci-bridge/pci-bridge"); + else + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s", "PCI-BRIDGE"); + } + else + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s/pci-bridge", pci_card->model); + } + /* insert this pci_card in the list to be displayed later */ + + *pci_card_list = insert_io_card(*pci_card_list, pci_card); + + /* + * If we are dealing with a pci-bridge, we need to move + * down to the children of this bridge if there are any. + * + * If we are not, we are either dealing with a regular + * card (in which case we move onto the sibling of this + * card) or we are dealing with a child of a pci-bridge + * (in which case we move onto the child's siblings or + * if there are no more siblings for this child, we + * move onto the parents siblings). + */ + pci_card_node = next_pci_card(pci_card_node, &pci_bridge, + is_pci, pci_bridge_node, + pci_parent_bridge, pci_instance); + } /* end-while */ +} + + +/* + * Helper function for fill_pci_card_list(). Indicates which + * card node to go to next. + * Parameters: + * ----------- + * Prom_node * curr_card: pointer to the current card node + * + * int * is_bridge: indicates whether or not the card (is | is on) + * a pci bridge + * + * int is_pcidev: indicates whether or not the current card + * is a pci bridge + * + * Prom_node * curr_bridge: pointer to the current pci bridge. Eg: + * curr_card->parent. + * + * Prom_node * parent_bridge: pointer to the first pci bridge encountered. + * we could have nested pci bridges, this would + * be the first one. + * + * Prom_node * pci: pointer to the pci instance that we are attached to. + * This would be parent_bridge->parent, or + * curr_node->parent, if curr_node is not on a pci bridge. + */ +static Prom_node * +next_pci_card(Prom_node *curr_card, int *is_bridge, int is_pcidev, + Prom_node *curr_bridge, Prom_node *parent_bridge, + Prom_node *pci) +{ + Prom_node * curr_node = curr_card; + if (*is_bridge) { + /* + * is_pcidev is used to prevent us from following the + * children of something like a scsi device. + */ + if (curr_node->child != NULL && is_pcidev) { + curr_node = curr_node->child; + } else { + curr_node = curr_node->sibling; + if (curr_node == NULL) { + curr_node = curr_bridge->sibling; + + while (curr_node == NULL && + curr_bridge != parent_bridge && + curr_bridge != NULL) { + curr_node = + curr_bridge->parent->sibling; + curr_bridge = curr_bridge->parent; + if (curr_node != NULL && + curr_node->parent == pci) { + break; + } + } + + if (curr_bridge == NULL || + curr_node == NULL || + curr_node->parent == pci || + curr_bridge == parent_bridge || + curr_node == parent_bridge) { + *is_bridge = FALSE; + } + } + } + + } else { + curr_node = curr_node->sibling; + } + return (curr_node); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/desktop/Makefile b/usr/src/lib/libprtdiag_psr/sparc/desktop/Makefile new file mode 100644 index 0000000000..713cc7d570 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/desktop/Makefile @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2000 by Sun Microsystems, Inc. +# All rights reserved. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/desktop/Makefile + +PRTDIAG_DIRS= nonpicl picl + +all := TARGET= all +lint := TARGET= lint +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +_msg := TARGET= _msg + +.KEEP_STATE: + +all lint clean clobber install _msg : $(PRTDIAG_DIRS) + +$(PRTDIAG_DIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + diff --git a/usr/src/lib/libprtdiag_psr/sparc/desktop/common/desktop.c b/usr/src/lib/libprtdiag_psr/sparc/desktop/common/desktop.c new file mode 100644 index 0000000000..29d72ee246 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/desktop/common/desktop.c @@ -0,0 +1,622 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Desktop Platform specific functions. + * + * Called when: + * machine_type == MTYPE_DARWIN && + * machine_type == MTYPE_DEFAULT + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/systeminfo.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + + +#define PCI_BUS(x) ((x >> 16) & 0xff) + +/* + * State variable to signify the type of machine we're currently + * running on. Since prtdiag has come to be the dumping ground + * for lots of platform-specific routines, and machine architecture + * alone is not enough to determine our course of action, we need + * to enumerate the different machine types that we should worry + * about. + */ +enum machine_type { + MTYPE_DEFAULT = 0, /* Desktop-class machine */ + MTYPE_DARWIN = 1 +}; + +enum machine_type machine_type = MTYPE_DEFAULT; + +extern int print_flag; + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (desktop systems only) + */ +int error_check(Sys_tree *tree, struct system_kstat_data *kstats); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); +int disp_fail_parts(Sys_tree *tree); +void display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void display_pci(Board_node *bnode); +void read_platform_kstats(Sys_tree *tree, + struct system_kstat_data *sys_kstat, + struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep); +void display_sbus(Board_node *); + + +/* local functions */ +static void dt_disp_asic_revs(Sys_tree *); +static void display_sabre_pci(Board_node *); +static void display_dev_node(Prom_node *np, int depth); +static void get_machine_type(void); + +int +error_check(Sys_tree *tree, struct system_kstat_data *kstats) +{ + int exit_code = 0; /* init to all OK */ + +#ifdef lint + kstats = kstats; +#endif + + /* + * silently check for any types of machine errors + */ + print_flag = 0; + if (disp_fail_parts(tree)) { + /* set exit_code to show failures */ + exit_code = 1; + } + print_flag = 1; + + return (exit_code); +} + + +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ +#ifdef lint + tree = tree; + grps = grps; +#endif +} + +/* + * disp_fail_parts + * + * Display the failed parts in the system. This function looks for + * the status property in all PROM nodes. On systems where + * the PROM does not supports passing diagnostic information + * thruogh the device tree, this routine will be silent. + */ +int +disp_fail_parts(Sys_tree *tree) +{ + int exit_code; + int system_failed = 0; + Board_node *bnode = tree->bd_list; + Prom_node *pnode; + + exit_code = 0; + + /* go through all of the boards looking for failed units. */ + while (bnode != NULL) { + /* find failed chips */ + pnode = find_failed_node(bnode->nodes); + if ((pnode != NULL) && !system_failed) { + system_failed = 1; + exit_code = 1; + if (print_flag == 0) { + return (exit_code); + } + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, "Failed Field " + "Replaceable Units (FRU) in System:\n"), 0); + log_printf("==========================" + "====================\n", 0); + } + + while (pnode != NULL) { + void *value; + char *name; /* node name string */ + char *type; /* node type string */ + char *board_type = NULL; + + value = get_prop_val(find_prop(pnode, "status")); + name = get_node_name(pnode); + + /* sanity check of data retreived from PROM */ + if ((value == NULL) || (name == NULL)) { + pnode = next_failed_node(pnode); + continue; + } + + /* Find the board type of this board */ + if (bnode->board_type == CPU_BOARD) { + board_type = "CPU"; + } else { + board_type = "IO"; + } + + log_printf(dgettext(TEXT_DOMAIN, "%s unavailable " + "on %s Board #%d\n"), name, board_type, + bnode->board_num, 0); + + log_printf(dgettext(TEXT_DOMAIN, + "\tPROM fault string: %s\n"), value, 0); + + log_printf(dgettext(TEXT_DOMAIN, + "\tFailed Field Replaceable Unit is "), 0); + + /* + * Determine whether FRU is CPU module, system + * board, or SBus card. + */ + if ((name != NULL) && (strstr(name, "sbus"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "SBus Card %d\n"), + get_sbus_slot(pnode), 0); + + } else if (((name = get_node_name(pnode->parent)) != + NULL) && (strstr(name, "pci"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "PCI Card %d"), + get_pci_device(pnode), 0); + + } else if (((type = get_node_type(pnode)) != NULL) && + (strstr(type, "cpu"))) { + + log_printf(dgettext(TEXT_DOMAIN, "UltraSPARC " + "module Board %d Module %d\n"), 0, + get_id(pnode)); + + } else { + log_printf(dgettext(TEXT_DOMAIN, + "%s board %d\n"), board_type, + bnode->board_num, 0); + } + pnode = next_failed_node(pnode); + } + bnode = bnode->next; + } + + if (!system_failed) { + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, + "No failures found in System\n"), 0); + log_printf("===========================\n", 0); + } + + if (system_failed) + return (1); + else + return (0); +} + + +void +display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) +{ + +#ifdef lint + kstats = kstats; +#endif + /* Display failed units */ + (void) disp_fail_parts(tree); +} + +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + +#ifdef lint + kstats = kstats; +#endif + /* + * Now display the last powerfail time and the fatal hardware + * reset information. We do this under a couple of conditions. + * First if the user asks for it. The second is iof the user + * told us to do logging, and we found a system failure. + */ + if (flag) { + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + dt_disp_asic_revs(tree); + + platform_disp_prom_version(tree); + } + return; + +} + +void +display_pci(Board_node *bnode) +{ + Prom_node *pci; + + /* + * We have different routines for walking/displaying PCI + * devices depending on whether the PCI device is a + * Psycho or a Sabre. + */ + pci = dev_find_node_by_type(bnode->nodes, "model", "SUNW,psycho"); + if (pci != NULL) { + display_psycho_pci(bnode); + return; + } + + pci = dev_find_node_by_type(bnode->nodes, "model", "SUNW,sabre"); + if (pci != NULL) { + display_sabre_pci(bnode); + return; + } +} + +void +read_platform_kstats(Sys_tree *tree, struct system_kstat_data *sys_kstat, + struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep) + +{ +#ifdef lint + tree = tree; + sys_kstat = sys_kstat; + bdp = bdp; + ep = ep; +#endif +} + + +/* + * local functions + */ + +void +dt_disp_asic_revs(Sys_tree *tree) +{ + Board_node *bnode; + Prom_node *pnode; + char *name; + int *version; + + /* Print the header */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(" HW Revisions ", 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + + bnode = tree->bd_list; + + log_printf("ASIC Revisions:\n", 0); + log_printf("---------------\n", 0); + + /* Find sysio and print rev */ + for (pnode = dev_find_node(bnode->nodes, "sbus"); pnode != NULL; + pnode = dev_next_node(pnode, "sbus")) { + version = (int *)get_prop_val(find_prop(pnode, "version#")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) { + log_printf("SBus: %s Rev %d\n", + name, *version, 0); + } + } + + /* Find Psycho and print rev */ + for (pnode = dev_find_node(bnode->nodes, "pci"); pnode != NULL; + pnode = dev_next_node(pnode, "pci")) { + version = (int *)get_prop_val(find_prop(pnode, "version#")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) + log_printf("PCI: %s Rev %d\n", + name, *version, 0); + } + + /* Find Cheerio and print rev */ + for (pnode = dev_find_node(bnode->nodes, "ebus"); pnode != NULL; + pnode = dev_next_node(pnode, "ebus")) { + version = (int *)get_prop_val(find_prop(pnode, "revision-id")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) + log_printf("Cheerio: %s Rev %d\n", name, *version, 0); + } + + + /* Find the FEPS and print rev */ + for (pnode = dev_find_node(bnode->nodes, "SUNW,hme"); pnode != NULL; + pnode = dev_next_node(pnode, "SUNW,hme")) { + version = (int *)get_prop_val(find_prop(pnode, "hm-rev")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) { + log_printf("FEPS: %s Rev ", name); + if (*version == 0xa0) { + log_printf("2.0\n", 0); + } else if (*version == 0x20) { + log_printf("2.1\n", 0); + } else { + log_printf("%x\n", *version, 0); + } + } + } + log_printf("\n", 0); + + display_ffb(bnode, 0); +} + +/* + * print the header and call display_dev_node() to walk the device + * tree (darwin platform only). + */ +static void +display_sabre_pci(Board_node *board) +{ + if (board == NULL) + return; + + log_printf(" Bus# Freq\n", 0); + log_printf("Brd Type MHz Slot " + "Name Model", 0); + log_printf("\n", 0); + log_printf("--- ---- ---- ---- " + "-------------------------------- ----------------------", 0); + log_printf("\n", 0); + display_dev_node(board->nodes, 0); + log_printf("\n", 0); +} + + +/* + * Recursively traverse the device tree and use tree depth as filter. + * called by: display_sabre_pci() + */ +static void +display_dev_node(Prom_node *np, int depth) +{ + char *name, *model, *compat, *regval; + unsigned int reghi; + + if (!np) + return; + if (depth > 2) + return; + + name = get_prop_val(find_prop(np, "name")); + model = get_prop_val(find_prop(np, "model")); + compat = get_prop_val(find_prop(np, "compatible")); + regval = get_prop_val(find_prop(np, "reg")); + + if (!regval) + return; + else + reghi = *(int *)regval; + + if (!model) + model = ""; + if (!name) + name = ""; + + if (depth == 2) { + char buf[256]; + if (compat) + (void) sprintf(buf, "%s-%s", name, compat); + else + (void) sprintf(buf, "%s", name); + + log_printf(" 0 PCI-%d 33 ", PCI_BUS(reghi), 0); + log_printf("%3d ", PCI_DEVICE(reghi), 0); + log_printf("%-32.32s", buf, 0); + log_printf(strlen(buf) > 32 ? "+ " : " ", 0); + log_printf("%-22.22s", model, 0); + log_printf(strlen(model) > 22 ? "+" : "", 0); + log_printf("\n", 0); + +#ifdef DEBUG + if (!compat) + compat = ""; + printf("bus=%d slot=%d name=%s model=%s compat=%s\n", + PCI_BUS(reghi), PCI_DEVICE(reghi), name, model, compat); +#endif + } + + if ((!strstr(name, "ebus")) && (!strstr(name, "ide"))) + display_dev_node(np->child, depth+1); + display_dev_node(np->sibling, depth); +} + +/* + * display_sbus + * Display all the SBus IO cards on this board. + */ +void +display_sbus(Board_node *board) +{ + struct io_card card; + struct io_card *card_list = NULL; + int freq; + int card_num; + void *value; + Prom_node *sbus; + Prom_node *card_node; + + if (board == NULL) + return; + + for (sbus = dev_find_node(board->nodes, SBUS_NAME); sbus != NULL; + sbus = dev_next_node(sbus, SBUS_NAME)) { + + /* Skip failed nodes for now */ + if (node_failed(sbus)) + continue; + + /* Calculate SBus frequency in MHz */ + value = get_prop_val(find_prop(sbus, "clock-frequency")); + if (value != NULL) + freq = ((*(int *)value) + 500000) / 1000000; + else + freq = -1; + + for (card_node = sbus->child; card_node != NULL; + card_node = card_node->sibling) { + char *model; + char *name; + char *child_name; + + card_num = get_sbus_slot(card_node); + if (card_num == -1) + continue; + + /* Fill in card information */ + card.display = 1; + card.freq = freq; + card.board = board->board_num; + (void) sprintf(card.bus_type, "SBus"); + card.slot = card_num; + card.status[0] = '\0'; + + /* Try and get card status */ + value = get_prop_val(find_prop(card_node, "status")); + if (value != NULL) + (void) strncpy(card.status, (char *)value, + MAXSTRLEN); + + /* XXX - For now, don't display failed cards */ + if (strstr(card.status, "fail") != NULL) + continue; + + /* + * sets the machine_type var if not already set + */ + get_machine_type(); + + /* + * For desktops, the only high slot number that + * needs to be displayed is the # 14 slot. + */ + if (machine_type == MTYPE_DEFAULT && + card_num >= MX_SBUS_SLOTS && card_num != 14) { + continue; + } + + /* Now gather all of the node names for that card */ + model = (char *)get_prop_val(find_prop(card_node, + "model")); + name = get_node_name(card_node); + + if (name == NULL) + continue; + + card.name[0] = '\0'; + card.model[0] = '\0'; + + /* Figure out how we want to display the name */ + child_name = get_node_name(card_node->child); + if ((card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(card_node->child, + "device_type")); + if (value != NULL) + (void) sprintf(card.name, "%s/%s (%s)", + name, child_name, + (char *)value); + else + (void) sprintf(card.name, "%s/%s", name, + child_name); + } else { + (void) strncpy(card.name, name, MAXSTRLEN); + } + + if (model != NULL) + (void) strncpy(card.model, model, MAXSTRLEN); + + card_list = insert_io_card(card_list, &card); + } + } + + /* We're all done gathering card info, now print it out */ + display_io_cards(card_list); + free_io_cards(card_list); +} + +static void +get_machine_type(void) +{ + char name[MAXSTRLEN]; + + machine_type = MTYPE_DEFAULT; + + /* Figure out what kind of machine we're on */ + if (sysinfo(SI_PLATFORM, name, MAXSTRLEN) != -1) { + if (strcmp(name, "SUNW,Ultra-5_10") == 0) + machine_type = MTYPE_DARWIN; + } +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/desktop/common/picldiag.c b/usr/src/lib/libprtdiag_psr/sparc/desktop/common/picldiag.c new file mode 100644 index 0000000000..51677867f0 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/desktop/common/picldiag.c @@ -0,0 +1,3687 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <alloca.h> +#include <errno.h> +#include <libintl.h> +#include <sys/utsname.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/openpromio.h> +#include <sys/ddi.h> +#include <syslog.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <locale.h> +#include <picl.h> +#include "pdevinfo.h" +#include "display.h" +#include "display_sun4u.h" +#include "picldefs.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +#define EM_INIT_FAIL dgettext(TEXT_DOMAIN,\ + "picl_initialize failed: %s\n") +#define EM_GET_ROOT_FAIL dgettext(TEXT_DOMAIN,\ + "Getting root node failed: %s\n") +#define EM_PRTDIAG_FAIL dgettext(TEXT_DOMAIN, "Prtdiag failed!\n") + +#define SIGN_ON_MSG dgettext(TEXT_DOMAIN,\ + "System Configuration: Sun Microsystems ") +#define SYSCLK_FREQ_MSG dgettext(TEXT_DOMAIN,\ + "System clock frequency: %d MHZ\n") +#define SERIAL_NUM_MSG dgettext(TEXT_DOMAIN,\ + "Chassis Serial Number:\n") +#define MEM_SIZE_MSG dgettext(TEXT_DOMAIN, "Memory size: ") +#define FFB_DOUBLE_BUF dgettext(TEXT_DOMAIN, "FFB, Double Buffered") +#define FFB_SINGLE_BUF dgettext(TEXT_DOMAIN, "FFB, Single Buffered") + +#define DEFAULT_BOARD_NUM 0 +#define DEFAULT_PORTID 0 +#define CLK_FREQ_66MHZ 66 +#define USB -1 +#define HUB -2 + +/* bus id */ +#define SBUS_TYPE 0 +#define PCI_TYPE 1 +#define UPA_TYPE 2 +#define PCIEX_TYPE 3 + +#define UPA_NAME "upa" +#define PCIEX_NAME "pciex" + +/* + * PICL classes + */ +#define PICL_CLASS_OPTIONS "options" + +/* + * Property names + */ + +#define OBP_PROP_REG "reg" +#define OBP_PROP_CLOCK_FREQ "clock-frequency" +#define OBP_PROP_BOARD_NUM "board#" +#define OBP_PROP_REVISION_ID "revision-id" +#define OBP_PROP_VERSION_NUM "version#" +#define OBP_PROP_MODREV_NUM "module-revision#" +#define OBP_PROP_BOARD_TYPE "board_type" +#define OBP_PROP_ECACHE_SIZE "ecache-size" +#define OBP_PROP_IMPLEMENTATION "implementation#" +#define OBP_PROP_MASK "mask#" +#define OBP_PROP_COMPATIBLE "compatible" +#define OBP_PROP_BANNER_NAME "banner-name" +#define OBP_PROP_MODEL "model" +#define OBP_PROP_66MHZ_CAPABLE "66mhz-capable" +#define OBP_PROP_FBC_REG_ID "fbc_reg_id" +#define OBP_PROP_VERSION "version" + +#define PROP_POWERFAIL_TIME "powerfail-time" +#define PICL_PROP_LOW_WARNING_THRESHOLD "LowWarningThreshold" + +#define DEFAULT_LINE_WIDTH 78 +#define HEADING_SYMBOL "=" + +#define MAX_IWAYS 32 + +typedef struct bank_list { + picl_nodehdl_t nodeh; + uint32_t iway_count; + uint32_t iway[MAX_IWAYS]; + struct bank_list *next; +} bank_list_t; + +typedef struct { + uint64_t base; + uint64_t size; + int ifactor; + int bank_count; +} seg_info_t; + +static struct io_card *io_card_list = NULL; /* The head of the IO card list */ +static bank_list_t *mem_banks = NULL; +static int mem_xfersize; +static int no_xfer_size = 0; + +static char *io_device_table[] = { + "block", + "disk", + "cdrom", + "floppy", + "tape", + "network", + "display", + "serial", + "parallel", + "scsi", + "scsi-2", + "scsi-3", + "ide", + "fcal", + "keyboard", + "mouse", + "dma" +}; + +#define NIODEVICE sizeof (io_device_table) / sizeof (io_device_table[0]) + +static char *bus_table[] = { + "ebus", + "isa", + "pmu", + "pci", + "pciex" +}; + +#define NBUS sizeof (bus_table) / sizeof (bus_table[0]) + +/* prtdiag exit codes */ +#define PD_SUCCESS 0 +#define PD_SYSTEM_FAILURE 1 +#define PD_INTERNAL_FAILURE 2 + +/* + * Use of global assumes do_prominfo only called from main in prtdiag and + * access does not need to be multi-thread safe. + */ +static int exit_code = PD_SUCCESS; + +/* + * This function is called from every location where a status value is output. + * It checks the status arg and sets exit_code if an error is detected. + * The status is typically returned from a PICL query. A case-insensitive + * string comparison is done to check for any status that starts with "fail" + * or "fault". + */ +static void +set_exit_code(char *status) +{ + if (status == NULL) + return; + + if (strncasecmp(status, "fail", 4) == 0 || + strncasecmp(status, "fault", 5) == 0) + exit_code = PD_SYSTEM_FAILURE; +} + +/* + * check if it is an IO deice + */ +static int +is_io_device(char *device_class) +{ + int i; + + for (i = 0; i < NIODEVICE; i++) { + if (strcmp(device_class, io_device_table[i]) == 0) + return (1); + } + + return (0); +} + +/* + * check if it is a bus + */ +static int +is_bus(char *device_class) +{ + int i; + + for (i = 0; i < NBUS; i++) { + if (strcmp(device_class, bus_table[i]) == 0) + return (1); + } + + return (0); +} + +/* + * search children to get the node by the nodename + */ +static int +picldiag_get_node_by_name(picl_nodehdl_t rooth, char *name, + picl_nodehdl_t *nodeh) +{ + picl_nodehdl_t childh; + int err; + char *nodename; + + nodename = alloca(strlen(name) + 1); + if (nodename == NULL) + return (PICL_FAILURE); + + err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_NAME, + nodename, (strlen(name) + 1)); + if (err != PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + continue; + } + + if (strcmp(nodename, name) == 0) { + *nodeh = childh; + return (PICL_SUCCESS); + } + + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + } + + return (err); +} + +/* + * get the value by the property name of the string prop + * Caller must free the outbuf + */ +static int +picldiag_get_string_propval(picl_nodehdl_t modh, char *prop_name, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + char *prop_value; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + /* + * If it is not a string prop, return NULL + */ + if (pinfo.type != PICL_PTYPE_CHARSTRING) + return (PICL_FAILURE); + + prop_value = malloc(pinfo.size); + if (prop_value == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(proph, prop_value, pinfo.size); + if (err != PICL_SUCCESS) { + free(prop_value); + return (err); + } + + *outbuf = prop_value; + return (PICL_SUCCESS); +} + + +/* + * return the value as a signed integer + */ + +static int64_t +picldiag_get_int_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + int8_t int8v; + int16_t int16v; + int32_t int32v; + int64_t int64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int, uint or byte array prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT) && + (pinfo.type != PICL_PTYPE_BYTEARRAY)) { + *ret = PICL_FAILURE; + return (0); + } + + switch (pinfo.size) { + case sizeof (int8_t): + err = picl_get_propval(proph, &int8v, sizeof (int8v)); + *ret = err; + return (int8v); + case sizeof (int16_t): + err = picl_get_propval(proph, &int16v, sizeof (int16v)); + *ret = err; + return (int16v); + case sizeof (int32_t): + err = picl_get_propval(proph, &int32v, sizeof (int32v)); + *ret = err; + return (int32v); + case sizeof (int64_t): + err = picl_get_propval(proph, &int64v, sizeof (int64v)); + *ret = err; + return (int64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} + +/* + * return the value of the uint prop + */ +static uint64_t +picldiag_get_uint_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + uint8_t uint8v; + uint16_t uint16v; + uint32_t uint32v; + uint64_t uint64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int or uint prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) { + *ret = PICL_FAILURE; + return (0); + } + + /* uint prop */ + + switch (pinfo.size) { + case sizeof (uint8_t): + err = picl_get_propval(proph, &uint8v, sizeof (uint8v)); + *ret = err; + return (uint8v); + case sizeof (uint16_t): + err = picl_get_propval(proph, &uint16v, sizeof (uint16v)); + *ret = err; + return (uint16v); + case sizeof (uint32_t): + err = picl_get_propval(proph, &uint32v, sizeof (uint32v)); + *ret = err; + return (uint32v); + case sizeof (uint64_t): + err = picl_get_propval(proph, &uint64v, sizeof (uint64v)); + *ret = err; + return (uint64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} + +/* + * return the value of the float prop + */ +static float +picldiag_get_float_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + float floatv; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return ((float)0); + } + + /* + * If it is not a float prop, return failure + */ + if (pinfo.type != PICL_PTYPE_FLOAT) { + *ret = PICL_FAILURE; + return ((float)0); + } + + *ret = picl_get_propval(proph, &floatv, sizeof (floatv)); + return (floatv); +} + +/* + * get the clock frequency + */ +static int +picldiag_get_clock_freq(picl_nodehdl_t modh, uint32_t *freq) +{ +#define ROUND_TO_MHZ(x) (((x) + 500000)/ 1000000) + int err; + uint64_t clk_freq; + + clk_freq = picldiag_get_uint_propval(modh, OBP_PROP_CLOCK_FREQ, &err); + if (err != PICL_SUCCESS) + return (err); + + *freq = ROUND_TO_MHZ(clk_freq); + + return (PICL_SUCCESS); +} + +/* + * get the clock frequency from parent + */ +static int +picldiag_get_clock_from_parent(picl_nodehdl_t nodeh, uint32_t *clk) +{ + picl_nodehdl_t parenth; + int err; + + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &parenth, sizeof (parenth)); + + while (err == PICL_SUCCESS) { + err = picldiag_get_clock_freq(parenth, clk); + if (err != PICL_PROPNOTFOUND) + return (err); + + err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT, + &parenth, sizeof (parenth)); + } + + return (err); +} + +/* + * get _fru_parent prop + * If not found, then travese superiors (parent nodes) until + * a _fru_parent property is found. + * If not found, no fru parent + */ +static int +picldiag_get_fru_parent(picl_nodehdl_t nodeh, picl_nodehdl_t *fruparenth) +{ + picl_nodehdl_t fruh; + int err; + + /* find fru parent */ + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_FRU_PARENT, + &fruh, sizeof (fruh)); + + if (err != PICL_SUCCESS) + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_LOC_PARENT, + &fruh, sizeof (fruh)); + + while (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &nodeh, sizeof (nodeh)); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_FRU_PARENT, + &fruh, sizeof (fruh)); + if (err != PICL_SUCCESS) + err = picl_get_propval_by_name(nodeh, + PICL_REFPROP_LOC_PARENT, &fruh, sizeof (fruh)); + } + + if (err == PICL_SUCCESS) + *fruparenth = fruh; + + return (err); +} + +/* + * get label + * + * To get the label, use the following algorithm: + * Lookup "Label" property in the fru node itself. If no + * Label found, then traverse superiors (parent nodes) until + * a Label property is found. + * if not found, then no label + */ +static int +picldiag_get_label(picl_nodehdl_t nodeh, char **label) +{ + int err; + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, label); + + while (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &nodeh, sizeof (nodeh)); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, + label); + } + + return (err); +} + +/* + * get combined label + * + * like picldiag_get_label, except concatenates the labels of parent locations + * eg SB0/P3 for processor P3 on system board SB0 + * + * if caller specifies non-zero label length, label will be cut to specified + * length. + * negative length is left justified, non-negative length is right justified + */ +static int +picldiag_get_combined_label(picl_nodehdl_t nodeh, char **label, int lablen) +{ + int err; + char *ptr; + char *ptr1 = NULL; + char *ptr2; + int len; + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, &ptr1); + if (err != PICL_PROPNOTFOUND && err != PICL_SUCCESS) + return (err); + + for (;;) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &nodeh, sizeof (nodeh)); + if (err == PICL_PROPNOTFOUND) + break; + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, &ptr); + if (err == PICL_SUCCESS) { + if (ptr1 == NULL) { + ptr1 = ptr; + } else { + ptr2 = malloc(strlen(ptr1) + strlen(ptr) + 2); + if (ptr2 == NULL) + return (PICL_FAILURE); + (void) strcpy(ptr2, ptr); + (void) strcat(ptr2, "/"); + (void) strcat(ptr2, ptr1); + (void) free(ptr); + (void) free(ptr1); + ptr1 = ptr2; + } + } else if (err != PICL_PROPNOTFOUND) { + return (err); + } + } + + if (ptr1 == NULL) + return (PICL_PROPNOTFOUND); + + len = strlen(ptr1); + /* if no string truncation is desired or required */ + if ((lablen == 0) || (len <= abs(lablen))) { + *label = ptr1; + return (PICL_SUCCESS); + } + + /* string truncation is required; alloc space for (lablen + \0) */ + ptr = malloc(abs(lablen) + 1); + if (ptr == 0) + return (PICL_FAILURE); + if (lablen > 0) { + /* right justification; label = "+<string>\0" */ + strcpy(ptr, "+"); + strncat(ptr, ptr1 + len - lablen + 1, lablen + 1); + } else { + /* left justification; label = "<string>+\0" */ + strncpy(ptr, ptr1, abs(lablen) - 1); + strcat(ptr, "+"); + } + + *label = ptr; + return (PICL_SUCCESS); +} + +/* + * return the first compatible value + */ +static int +picldiag_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + char *pval; + + err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE, + &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + *outbuf = pval; + return (PICL_SUCCESS); + } + + if (pinfo.type != PICL_PTYPE_TABLE) + return (PICL_FAILURE); + + /* get first string from table */ + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + + *outbuf = pval; + return (PICL_SUCCESS); +} + +/* + * print the header in the center + */ +static void +logprintf_header(char *header, size_t line_width) +{ + size_t start_pos; + size_t i; + + log_printf("\n"); + start_pos = (line_width - strlen(header) - 2) / 2; + + for (i = 0; i < start_pos; i++) + log_printf("%s", HEADING_SYMBOL); + + log_printf(" %s ", header); + + for (i = 0; i < start_pos; i++) + log_printf("%s", HEADING_SYMBOL); + + log_printf("\n"); +} + +/* + * print the size + */ +static void +logprintf_size(uint64_t size) +{ +#define SIZE_FIELD 11 + + uint64_t kbyte = 1024; + uint64_t mbyte = 1024 * 1024; + uint64_t gbyte = 1024 * 1024 * 1024; + uint64_t residue; + char buf[SIZE_FIELD]; + + if (size >= gbyte) { + residue = size % gbyte; + if (residue == 0) + snprintf(buf, sizeof (buf), "%dGB", + (int)(size / gbyte)); + else + snprintf(buf, sizeof (buf), "%.2fGB", + (float)size / gbyte); + } else if (size >= mbyte) { + residue = size % mbyte; + if (residue == 0) + snprintf(buf, sizeof (buf), "%dMB", + (int)(size / mbyte)); + else + snprintf(buf, sizeof (buf), "%.2fMB", + (float)size / mbyte); + } else { + residue = size % kbyte; + if (residue == 0) + snprintf(buf, sizeof (buf), "%dKB", + (int)(size / kbyte)); + else + snprintf(buf, sizeof (buf), "%.2fKB", + (float)size / kbyte); + } + + log_printf("%-10s ", buf); +} + +/* + * display platform banner + */ +static int +display_platform_banner(picl_nodehdl_t plafh) +{ + char *platform; + char *banner_name; + int err; + + /* + * get PICL_PROP_MACHINE and PICL_PROP_BANNER_NAME + */ + log_printf(SIGN_ON_MSG); + err = picldiag_get_string_propval(plafh, PICL_PROP_MACHINE, + &platform); + if (err != PICL_SUCCESS) + return (err); + log_printf(" %s", platform); + free(platform); + + err = picldiag_get_string_propval(plafh, OBP_PROP_BANNER_NAME, + &banner_name); + if (err != PICL_SUCCESS) + return (err); + log_printf(" %s", banner_name); + free(banner_name); + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +serialnum_callback(picl_nodehdl_t serialh, void *arg) +{ + int *countp = arg; + int err; + picl_nodehdl_t fruph; + char *buf; + + err = picl_get_propval_by_name(serialh, PICL_REFPROP_FRU_PARENT, + &fruph, sizeof (fruph)); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(serialh, + PICL_REFPROP_LOC_PARENT, &fruph, sizeof (fruph)); + } + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) + return (PICL_WALK_CONTINUE); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(serialh, + PICL_PROP_SERIAL_NUMBER, &buf); + if (err == PICL_SUCCESS) { + log_printf("\n"); + log_printf(SERIAL_NUM_MSG); + log_printf("----------------------\n"); + log_printf("%s\n", buf); + free(buf); + return (PICL_WALK_TERMINATE); + } + return (err); +} + +/* + * display the chassis serial number + */ +static int +display_serial_number(picl_nodehdl_t plafh) +{ + int print_header; + + picl_walk_tree_by_class(plafh, PICL_CLASS_CHASSIS_SERIAL_NUM, + &print_header, serialnum_callback); + return (PICL_SUCCESS); +} + +/* + * display the clock frequency + */ +static int +display_system_clock(picl_nodehdl_t plafh) +{ + uint32_t system_clk; + int err; + + err = picldiag_get_clock_freq(plafh, &system_clk); + if (err != PICL_SUCCESS) + return (err); + + log_printf(SYSCLK_FREQ_MSG, system_clk); + + return (PICL_SUCCESS); +} + +/* + * callback function to display the memory size + */ +/*ARGSUSED*/ +static int +memory_callback(picl_nodehdl_t memh, void *args) +{ + uint64_t mem_size; + int err; + + log_printf(MEM_SIZE_MSG); + mem_size = picldiag_get_uint_propval(memh, PICL_PROP_SIZE, &err); + if (err == PICL_SUCCESS) + logprintf_size(mem_size); + log_printf("\n"); + no_xfer_size = 0; + mem_xfersize = picldiag_get_uint_propval(memh, PICL_PROP_TRANSFER_SIZE, + &err); + if (err == PICL_PROPNOTFOUND) + no_xfer_size = 1; + return (PICL_WALK_TERMINATE); +} + +/* + * callback function to print cpu information + */ +/*ARGSUSED*/ +static int +cpu_callback(picl_nodehdl_t nodeh, void *args) +{ + int err; + int id; + uint64_t uintval; + uint32_t freq; + char *impl_name; + char *status; + picl_prophdl_t parenth; + char *label; + + /* + * If no ID is found, return + */ + id = picldiag_get_uint_propval(nodeh, PICL_PROP_ID, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + log_printf("%-3d ", id); + + /* + * If no freq is found, return + */ + err = picldiag_get_clock_freq(nodeh, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + log_printf("%4d MHz ", freq); + + /* Ecache size */ + uintval = picldiag_get_uint_propval(nodeh, OBP_PROP_ECACHE_SIZE, &err); + if (err == PICL_PROPNOTFOUND) + log_printf(" - "); + else if (err == PICL_SUCCESS) + logprintf_size(uintval); + else + return (err); + + /* Implementation */ + impl_name = NULL; + err = picldiag_get_string_propval(nodeh, PICL_PROP_NAME, &impl_name); + if (err != PICL_SUCCESS) + log_printf(" <unknown> "); + else + log_printf(" %-22s ", impl_name); + + /* CPU Mask */ + uintval = picldiag_get_uint_propval(nodeh, OBP_PROP_MASK, &err); + if (err == PICL_PROPNOTFOUND) + log_printf(" - "); + else if (err == PICL_SUCCESS) + log_printf("%2lld.%-2lld ", (uintval >> 4) & 0xf, + uintval & 0xf); + else + return (err); + + /* + * Status - if the node has a status property then display that + * otherwise display the State property + */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_STATUS, &status); + if (err == PICL_SUCCESS) { + log_printf("%-12s", status); + set_exit_code(status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != + PICL_PROPVALUNAVAILABLE && err != PICL_ENDOFLIST) { + return (err); + } else { + err = picldiag_get_string_propval(nodeh, + PICL_PROP_STATE, &status); + if (err == PICL_SUCCESS) { + log_printf("%-12s", status); + set_exit_code(status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != + PICL_PROPVALUNAVAILABLE && err != + PICL_ENDOFLIST) { + return (err); + } else { + log_printf("unknown "); + } + } + + /* + * Location: use label of fru parent + */ + err = picldiag_get_fru_parent(nodeh, &parenth); + if (err == PICL_PROPNOTFOUND) { + log_printf(" - "); + } else if (err == PICL_SUCCESS) { + err = picldiag_get_combined_label(parenth, &label, 12); + if (err == PICL_PROPNOTFOUND) + log_printf(" - "); + else if (err == PICL_SUCCESS) { + log_printf("%s", label); + free(label); + } else + return (err); + } else + return (err); + + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +/* + * display cpu information + */ +static int +display_cpu_info(picl_nodehdl_t plafh) +{ + int err; + + /* + * Display the table header for CPUs . Then display the CPU + * frequency, cache size, and processor revision on all the boards. + */ + logprintf_header(dgettext(TEXT_DOMAIN, "CPUs"), DEFAULT_LINE_WIDTH); + log_printf(" E$ CPU " + "CPU\n"); + log_printf("CPU Freq Size Implementation " + "Mask Status Location\n"); + log_printf("--- -------- ---------- --------------------- " + "----- ------ --------\n"); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_CPU, PICL_CLASS_CPU, + cpu_callback); + return (err); +} + +/* + * Inserts an io_card structure into the list. + */ +static void +add_io_card(uint32_t board, uint32_t bus_id, uint32_t slot, char *label, + uint32_t freq, char *name, char *model, char *status, char *devfs_path) +{ + struct io_card card; + + card.display = 1; + card.board = board; + switch (bus_id) { + case SBUS_TYPE: + strlcpy(card.bus_type, SBUS_NAME, MAXSTRLEN); + break; + case PCI_TYPE: + strlcpy(card.bus_type, PCI_NAME, MAXSTRLEN); + break; + case PCIEX_TYPE: + strlcpy(card.bus_type, PCIEX_NAME, MAXSTRLEN); + break; + case UPA_TYPE: + strlcpy(card.bus_type, UPA_NAME, MAXSTRLEN); + break; + default: /* won't reach here */ + strlcpy(card.bus_type, "", MAXSTRLEN); + break; + } + if (label == NULL) + card.slot = slot; + else { + card.slot = PCI_SLOT_IS_STRING; + (void) strlcpy(card.slot_str, label, MAXSTRLEN); + } + card.freq = freq; + card.status[0] = '\0'; + card.name[0] = '\0'; + card.model[0] = '\0'; + card.notes[0] = '\0'; + if (status != NULL) + strlcpy(card.status, status, MAXSTRLEN); + if (name != NULL) + strlcpy(card.name, name, MAXSTRLEN); + if (model != NULL) + strlcpy(card.model, model, MAXSTRLEN); + if (status != NULL) + strlcpy(card.status, status, MAXSTRLEN); + if (devfs_path != NULL) + strlcpy(card.notes, devfs_path, MAXSTRLEN); + + io_card_list = insert_io_card(io_card_list, &card); +} + +static void +append_to_bank_list(bank_list_t *newptr) +{ + bank_list_t *ptr; + + if (mem_banks == NULL) { + mem_banks = newptr; + return; + } + ptr = mem_banks; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = newptr; +} + +static void +free_bank_list(void) +{ + bank_list_t *ptr; + bank_list_t *tmp; + + for (ptr = mem_banks; ptr != NULL; ptr = tmp) { + tmp = ptr->next; + free(ptr); + } + mem_banks = NULL; +} + + +/* + * print label for memory module + */ +static int +logprintf_memory_module_label(picl_nodehdl_t moduleh) +{ + picl_nodehdl_t fruparenth; + int err; + char *label; + + err = picldiag_get_fru_parent(moduleh, &fruparenth); + if (err == PICL_PROPNOTFOUND) { + log_printf("-"); + return (PICL_SUCCESS); + } else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruparenth, &label, 30); + if (err == PICL_PROPNOTFOUND) + log_printf("-"); + else if (err == PICL_SUCCESS) { + log_printf("%-15s", label); + free(label); + } else + return (err); + + return (PICL_SUCCESS); +} + +/* + * print the bank id and add the bank handle in the bank list + * return the head of the bank list + */ +static int +membank_callback(picl_nodehdl_t bankh, void *args) +{ + int err; + int64_t id; + uint64_t match; + uint64_t mask; + int i; + bank_list_t *newptr; + seg_info_t *segp = args; + + /* + * print the bank id in the segment table contains column + */ + id = picldiag_get_uint_propval(bankh, PICL_PROP_ID, &err); + if (segp->bank_count > 0) + log_printf(","); + if (err == PICL_PROPNOTFOUND) + log_printf("-"); + else if (err == PICL_SUCCESS) + log_printf("%-lld", id); + else + return (err); + segp->bank_count++; + + /* + * Save the bank information for later (print_bank_table) + */ + newptr = malloc(sizeof (*newptr)); + if (newptr == NULL) + return (PICL_FAILURE); + + newptr->nodeh = bankh; + newptr->iway_count = 0; + newptr->next = NULL; + append_to_bank_list(newptr); + + /* + * Compute the way numbers for the bank + */ + if (no_xfer_size) + return (PICL_WALK_CONTINUE); + + match = picldiag_get_uint_propval(bankh, PICL_PROP_ADDRESSMATCH, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + mask = picldiag_get_uint_propval(bankh, PICL_PROP_ADDRESSMASK, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + i = 0; + while ((i < segp->ifactor) && (newptr->iway_count < MAX_IWAYS)) { + if (((segp->base + i * mem_xfersize) & mask) == match) + newptr->iway[newptr->iway_count++] = i; + ++i; + } + return (PICL_WALK_CONTINUE); +} + + +/* + * find the memory bank and add the bank handle in the bank list + * return the head of the bank list + */ +static int +logprintf_bankinfo(picl_nodehdl_t segh, seg_info_t *segp) +{ + int err; + + log_printf("BankIDs "); + /* + * find memory-bank + */ + segp->bank_count = 0; + err = picl_walk_tree_by_class(segh, PICL_CLASS_MEMORY_BANK, segp, + membank_callback); + log_printf("\n"); + return (err); +} + +/* + * print the label of memory module or the memory module bank ids + */ +static int +logprintf_seg_contains_col(picl_nodehdl_t nodeh, seg_info_t *segp) +{ + picl_nodehdl_t moduleh; + int err; + + /* + * find memory-module if referenced directly from the memory-segment + * (ie no memory banks) + */ + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_MEMORY_MODULE, + &moduleh, sizeof (moduleh)); + if ((err != PICL_SUCCESS) && (err != PICL_PROPNOTFOUND)) + return (err); + if (err == PICL_SUCCESS) { + err = logprintf_memory_module_label(moduleh); + log_printf("\n"); + return (err); + } + + /* + * memory-module not referenced directly from the memory segment + * so list memory banks instead + */ + err = logprintf_bankinfo(nodeh, segp); + return (err); +} + +/* + * find all memory modules under the given memory module group + * and print its label + */ +static int +logprintf_memory_module_group_info(picl_nodehdl_t memgrph, uint64_t mcid) +{ + int err; + int64_t id; + boolean_t got_status; + picl_nodehdl_t moduleh; + char piclclass[PICL_CLASSNAMELEN_MAX]; + picl_nodehdl_t fruparenth; + char *status; + + id = picldiag_get_uint_propval(memgrph, PICL_PROP_ID, &err); + if (err == PICL_PROPNOTFOUND) + id = -1; + else if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(memgrph, PICL_PROP_CHILD, &moduleh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + /* controller id */ + log_printf("%-8lld ", mcid); + + /* group id */ + if (id == -1) { + log_printf("- "); + } else { + log_printf("%-8lld ", id); + } + + err = picl_get_propval_by_name(moduleh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, PICL_CLASS_MEMORY_MODULE) == 0) { + err = logprintf_memory_module_label(moduleh); + if (err != PICL_SUCCESS) + return (err); + } + + got_status = B_FALSE; + err = picldiag_get_fru_parent(moduleh, &fruparenth); + if (err == PICL_SUCCESS) { + err = picldiag_get_string_propval(fruparenth, + PICL_PROP_OPERATIONAL_STATUS, &status); + if (err == PICL_SUCCESS) { + got_status = B_TRUE; + } else if (err != PICL_PROPNOTFOUND) + return (err); + } else if (err != PICL_PROPNOTFOUND) + return (err); + + if (!got_status) { + err = picldiag_get_string_propval(moduleh, + PICL_PROP_STATUS, &status); + if (err == PICL_SUCCESS) + got_status = B_TRUE; + else if (err != PICL_PROPNOTFOUND) + return (err); + } + if (got_status) { + log_printf("%s", status); + set_exit_code(status); + free(status); + } + err = picl_get_propval_by_name(moduleh, PICL_PROP_PEER, + &moduleh, sizeof (picl_nodehdl_t)); + + log_printf("\n"); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + return (err); +} + +/* + * search children to find memory module group under memory-controller + */ +static int +find_memory_module_group(picl_nodehdl_t mch, int *print_header) +{ + picl_nodehdl_t memgrph; + uint64_t mcid; + int err; + char piclclass[PICL_CLASSNAMELEN_MAX]; + + mcid = picldiag_get_uint_propval(mch, OBP_PROP_PORTID, &err); + if (err == PICL_PROPNOTFOUND) + mcid = DEFAULT_PORTID; + else if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(mch, PICL_PROP_CHILD, + &memgrph, sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(memgrph, + PICL_PROP_CLASSNAME, piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, PICL_CLASS_MEMORY_MODULE_GROUP) == 0) { + if (*print_header == 1) { + log_printf( + dgettext(TEXT_DOMAIN, + "\nMemory Module Groups:\n")); + log_printf("--------------------------"); + log_printf("------------------------\n"); + log_printf("ControllerID GroupID Labels"); + log_printf(" Status\n"); + log_printf("--------------------------"); + log_printf("------------------------\n"); + *print_header = 0; + } + err = logprintf_memory_module_group_info(memgrph, mcid); + if (err != PICL_SUCCESS) + return (err); + } + + err = picl_get_propval_by_name(memgrph, PICL_PROP_PEER, + &memgrph, sizeof (picl_nodehdl_t)); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + return (err); +} + +/* + * print memory module group table per memory-controller + */ +static int +print_memory_module_group_table(picl_nodehdl_t plafh) +{ + picl_nodehdl_t mch; + int err; + char piclclass[PICL_CLASSNAMELEN_MAX]; + int print_header; + + print_header = 1; + + /* + * find memory-controller + */ + err = picl_get_propval_by_name(plafh, PICL_PROP_CHILD, &mch, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(mch, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, PICL_CLASS_MEMORY_CONTROLLER) != 0) { + err = print_memory_module_group_table(mch); + if (err != PICL_SUCCESS) + return (err); + err = picl_get_propval_by_name(mch, PICL_PROP_PEER, + &mch, sizeof (picl_nodehdl_t)); + continue; + } + + err = find_memory_module_group(mch, &print_header); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(mch, PICL_PROP_PEER, + &mch, sizeof (picl_nodehdl_t)); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + + return (err); +} + +/* + * print bank table + */ +static int +print_bank_table(void) +{ + bank_list_t *ptr; + picl_nodehdl_t bankh; + picl_nodehdl_t memgrph; + picl_nodehdl_t mch; + int err; + int32_t i; + uint64_t size; + int id; + + log_printf(dgettext(TEXT_DOMAIN, "\nBank Table:\n")); + log_printf("---------------------------------------"); + log_printf("--------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, " Physical Location\n")); + log_printf(dgettext(TEXT_DOMAIN, "ID ControllerID GroupID ")); + log_printf(dgettext(TEXT_DOMAIN, "Size Interleave Way\n")); + log_printf("---------------------------------------"); + log_printf("--------------------\n"); + + for (ptr = mem_banks; ptr != NULL; ptr = ptr->next) { + bankh = ptr->nodeh; + id = picldiag_get_uint_propval(bankh, PICL_PROP_ID, &err); + if (err != PICL_SUCCESS) + log_printf("%-8s ", "-"); + else + log_printf("%-8d ", id); + + /* find memory-module-group */ + err = picl_get_propval_by_name(bankh, + PICL_REFPROP_MEMORY_MODULE_GROUP, &memgrph, + sizeof (memgrph)); + if (err == PICL_PROPNOTFOUND) { + log_printf("%-8s ", "-"); + log_printf("%-8s ", "-"); + } else if (err != PICL_SUCCESS) + return (err); + else { + /* + * get controller id + */ + err = picl_get_propval_by_name(memgrph, + PICL_PROP_PARENT, &mch, sizeof (picl_nodehdl_t)); + if (err != PICL_SUCCESS) + return (err); + + id = picldiag_get_uint_propval(mch, OBP_PROP_PORTID, + &err); + if (err == PICL_PROPNOTFOUND) + id = DEFAULT_PORTID; /* use default */ + else if (err != PICL_SUCCESS) + return (err); + + log_printf("%-8d ", id); + + /* get group id */ + id = picldiag_get_uint_propval(memgrph, PICL_PROP_ID, + &err); + if (err == PICL_PROPNOTFOUND) + log_printf("- "); + else if (err == PICL_SUCCESS) + log_printf("%-8d ", id); + else + return (err); + } + + size = picldiag_get_uint_propval(bankh, PICL_PROP_SIZE, &err); + if (err == PICL_PROPNOTFOUND) + log_printf("- "); + else if (err == PICL_SUCCESS) + logprintf_size(size); + else + return (err); + + log_printf(" "); + for (i = 0; i < ptr->iway_count; i++) { + if (i != 0) + log_printf(","); + log_printf("%d", ptr->iway[i]); + } + + log_printf("\n"); + } + return (PICL_SUCCESS); +} + +/* + * callback function to print segment, add the bank in the list and + * return the bank list + */ +/* ARGSUSED */ +static int +memseg_callback(picl_nodehdl_t segh, void *args) +{ + seg_info_t seginfo; + int err; + + /* get base address */ + seginfo.base = picldiag_get_uint_propval(segh, PICL_PROP_BASEADDRESS, + &err); + if (err == PICL_PROPNOTFOUND) { + log_printf("-\n"); + return (PICL_WALK_CONTINUE); + } else if (err == PICL_SUCCESS) + log_printf("0x%-16llx ", seginfo.base); + else + return (err); + + /* get size */ + seginfo.size = picldiag_get_uint_propval(segh, PICL_PROP_SIZE, &err); + if (err == PICL_PROPNOTFOUND) { + log_printf("-\n"); + return (PICL_WALK_CONTINUE); + } else if (err == PICL_SUCCESS) + logprintf_size(seginfo.size); + else + return (err); + + /* get interleave factor */ + seginfo.ifactor = picldiag_get_uint_propval(segh, + PICL_PROP_INTERLEAVE_FACTOR, &err); + + if (err == PICL_PROPNOTFOUND) { + log_printf(" -\n"); + return (PICL_WALK_CONTINUE); + } else if (err == PICL_SUCCESS) + log_printf(" %-2d ", seginfo.ifactor); + else + return (err); + + seginfo.bank_count = 0; + err = logprintf_seg_contains_col(segh, &seginfo); + if (err != PICL_SUCCESS) + return (err); + return (PICL_WALK_CONTINUE); +} + +/* + * search children to find memory-segment and set up the bank list + */ +static int +find_segments(picl_nodehdl_t plafh) +{ + int err; + + log_printf(dgettext(TEXT_DOMAIN, "Segment Table:\n")); + log_printf("------------------------------"); + log_printf("-----------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Base Address Size ")); + log_printf(dgettext(TEXT_DOMAIN, "Interleave Factor Contains\n")); + log_printf("------------------------------"); + log_printf("-----------------------------------------\n"); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT, + NULL, memseg_callback); + return (err); +} + +/* + * display memory configuration + */ +static int +display_memory_config(picl_nodehdl_t plafh) +{ + int err; + + logprintf_header(dgettext(TEXT_DOMAIN, "Memory Configuration"), + DEFAULT_LINE_WIDTH); + + mem_banks = NULL; + err = find_segments(plafh); + + if ((err == PICL_SUCCESS) && (mem_banks != NULL)) + print_bank_table(); + + free_bank_list(); + + return (print_memory_module_group_table(plafh)); +} + +/* + * print the hub device + */ +static int +logprintf_hub_devices(picl_nodehdl_t hubh) +{ + char *name; + int portnum; + char *labelp; + picl_nodehdl_t parenth; + int err; + + err = picldiag_get_string_propval(hubh, PICL_PROP_NAME, &name); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-12.12s ", name); + free(name); + + err = picl_get_propval_by_name(hubh, PICL_REFPROP_LOC_PARENT, &parenth, + sizeof (picl_nodehdl_t)); + + if (err == PICL_SUCCESS) { + /* Read the Label */ + err = picldiag_get_label(parenth, &labelp); + if (err == PICL_SUCCESS) { + log_printf("%s\n", labelp); + free(labelp); + return (PICL_SUCCESS); + } else if (err != PICL_PROPNOTFOUND) { + log_printf("\n"); + return (err); + } + } else if (err != PICL_PROPNOTFOUND) { + log_printf("\n"); + return (err); + } + + /* No Label, try the reg */ + err = picl_get_propval_by_name(hubh, OBP_PROP_REG, &portnum, + sizeof (portnum)); + if (err == PICL_PROPNOTFOUND) + log_printf(" -\n"); + else if (err != PICL_SUCCESS) { + log_printf("\n"); + return (err); + } else + log_printf("%3d\n", portnum); + + return (PICL_SUCCESS); +} + +/* + * callback functions to display hub devices + */ +/* ARGSUSED */ +static int +print_usb_devices(picl_nodehdl_t hubh, void *arg) +{ + picl_nodehdl_t chdh; + char *rootname; + int type = *(int *)arg; + int hubnum; + int err; + + err = picl_get_propval_by_name(hubh, PICL_PROP_CHILD, &chdh, + sizeof (picl_nodehdl_t)); + + /* print header */ + if (err == PICL_SUCCESS) { + err = picldiag_get_string_propval(hubh, PICL_PROP_NAME, + &rootname); + if (err != PICL_SUCCESS) + return (err); + + if (type == USB) { + log_printf("\n==============================="); + log_printf(dgettext(TEXT_DOMAIN, + " %s Devices "), rootname); + } else { + /* Get its hub number */ + err = picl_get_propval_by_name(hubh, + OBP_PROP_REG, &hubnum, sizeof (hubnum)); + if ((err != PICL_SUCCESS) && + (err != PICL_PROPNOTFOUND)) { + free(rootname); + return (err); + } + + log_printf("\n==============================="); + if (err == PICL_SUCCESS) + log_printf(dgettext(TEXT_DOMAIN, + " %s#%d Devices "), + rootname, hubnum); + else + log_printf(dgettext(TEXT_DOMAIN, + " %s Devices "), rootname); + } + + log_printf("===============================\n\n"); + log_printf("Name Port#\n"); + log_printf("------------ -----\n"); + free(rootname); + + do { + logprintf_hub_devices(chdh); + + err = picl_get_propval_by_name(chdh, PICL_PROP_PEER, + &chdh, sizeof (picl_nodehdl_t)); + } while (err == PICL_SUCCESS); + } + + + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback functions to display usb devices + */ +/* ARGSUSED */ +static int +usb_callback(picl_nodehdl_t usbh, void *args) +{ + int err; + int type; + + type = USB; + err = print_usb_devices(usbh, &type); + if (err != PICL_WALK_CONTINUE) + return (err); + type = HUB; + err = picl_walk_tree_by_class(usbh, NULL, &type, print_usb_devices); + if (err == PICL_SUCCESS) + err = PICL_WALK_CONTINUE; + return (err); +} + + +/* + * find usb devices and print its information + */ +static int +display_usb_devices(picl_nodehdl_t plafh) +{ + int err; + + /* + * get the usb node + */ + err = picl_walk_tree_by_class(plafh, PICL_CLASS_USB, NULL, + usb_callback); + return (err); +} + + + +/* + * If nodeh is the io device, add it into the io list and return + * If it is not an io device and it has the subtree, traverse the subtree + * and add all leaf io devices + */ +static int +add_io_leaves(picl_nodehdl_t nodeh, char *parentname, uint32_t board, + uint32_t bus_id, uint64_t slot, uint32_t freq, char *model, char *status) +{ + picl_nodehdl_t childh; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + int err; + char *nameval; + char piclclass[PICL_CLASSNAMELEN_MAX]; + char nodename[MAXSTRLEN]; + char name[MAXSTRLEN]; + char *devfs_path; + char *compatible; + picl_nodehdl_t fruparenth; + char *label; + char binding_name[MAXSTRLEN]; + + err = picl_get_propinfo_by_name(nodeh, PICL_PROP_NAME, &pinfo, + &proph); + if (err != PICL_SUCCESS) + return (err); + + nameval = alloca(pinfo.size); + if (nameval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(proph, nameval, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + (void) strlcpy(nodename, nameval, MAXSTRLEN); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + /* if binding_name is found, name will be <nodename>-<binding_name> */ + err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME, + binding_name, sizeof (binding_name)); + if (err == PICL_PROPNOTFOUND) { + /* + * if compatible prop is found, name will be + * <nodename>-<compatible> + */ + err = picldiag_get_first_compatible_value(nodeh, &compatible); + if (err == PICL_SUCCESS) { + strlcat(nodename, "-", MAXSTRLEN); + strlcat(nodename, compatible, MAXSTRLEN); + free(compatible); + } else if (err != PICL_PROPNOTFOUND) { + return (err); + } + } else if (err != PICL_SUCCESS) { + return (err); + } else if (strcmp(nodename, binding_name) != 0) { + if (strcmp(nodename, piclclass) == 0) { + /* + * nodename same as binding name - + * no need to display twice + */ + strlcpy(nodename, binding_name, MAXSTRLEN); + } else { + strlcat(nodename, "-", MAXSTRLEN); + strlcat(nodename, binding_name, MAXSTRLEN); + } + } + + /* + * If it is an immediate child under pci/pciex/sbus/upa and not + * a bus node, add it to the io list. + * If it is a child under sub-bus and it is in an io + * device, add it to the io list. + */ + if (((parentname == NULL) && (!is_bus(piclclass))) || + ((parentname != NULL) && (is_io_device(piclclass)))) { + if (parentname == NULL) + (void) snprintf(name, MAXSTRLEN, "%s", nodename); + else + (void) snprintf(name, MAXSTRLEN, "%s/%s", parentname, + nodename); + + /* + * append the class if its class is not a generic + * obp-device class + */ + if (strcmp(piclclass, PICL_CLASS_OBP_DEVICE)) + (void) snprintf(name, MAXSTRLEN, "%s (%s)", name, + piclclass); + + err = picldiag_get_fru_parent(nodeh, &fruparenth); + if (err == PICL_PROPNOTFOUND) { + label = NULL; + } else if (err != PICL_SUCCESS) { + return (err); + } else { + err = picldiag_get_combined_label(fruparenth, &label, + 15); + if (err == PICL_PROPNOTFOUND) + label = NULL; + else if (err != PICL_SUCCESS) + return (err); + } + /* devfs-path */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH, + &devfs_path); + if (err == PICL_PROPNOTFOUND) + devfs_path = NULL; + else if (err != PICL_SUCCESS) + return (err); + + add_io_card(board, bus_id, slot, label, freq, name, + model, status, devfs_path); + if (label != NULL) + free(label); + if (devfs_path != NULL) + free(devfs_path); + return (PICL_SUCCESS); + } + + /* + * If there is any child, Go through each child. + */ + + err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD, + &childh, sizeof (picl_nodehdl_t)); + + /* there is a child */ + while (err == PICL_SUCCESS) { + if (parentname == NULL) + (void) strlcpy(name, nodename, MAXSTRLEN); + else + (void) snprintf(name, MAXSTRLEN, "%s/%s", parentname, + nodename); + + err = add_io_leaves(childh, name, board, bus_id, slot, freq, + model, status); + if (err != PICL_SUCCESS) + return (err); + /* + * get next child + */ + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + } + + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + return (err); +} + +/* + * callback function to add all io devices under sbus in io list + */ +/*ARGSUSED*/ +static int +sbus_callback(picl_nodehdl_t sbush, void *args) +{ + picl_nodehdl_t nodeh; + int err; + uint32_t boardnum; + uint32_t bus_id; + uint32_t slot; + uint32_t freq; + char *model; + char *status; + + /* Fill in common infomation */ + bus_id = SBUS_TYPE; + + err = picldiag_get_clock_freq(sbush, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + /* + * If no board# is found, set boardnum to 0 + */ + boardnum = picldiag_get_uint_propval(sbush, OBP_PROP_BOARD_NUM, &err); + if (err == PICL_PROPNOTFOUND) + boardnum = DEFAULT_BOARD_NUM; + else if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(sbush, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + slot = picldiag_get_uint_propval(nodeh, + PICL_PROP_SLOT, &err); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, OBP_PROP_MODEL, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_STATUS, + &status); + if (err == PICL_PROPNOTFOUND) { + status = malloc(5); + if (status == NULL) + return (PICL_FAILURE); + strncpy(status, "okay", 5); + } else if (err != PICL_SUCCESS) + return (err); + + err = add_io_leaves(nodeh, NULL, boardnum, bus_id, slot, freq, + model, status); + if (model != NULL) + free(model); + if (status != NULL) + free(status); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * add all io devices under pci/pciex in io list + */ +/* ARGSUSED */ +static int +pci_pciex_callback(picl_nodehdl_t pcih, void *args) +{ + picl_nodehdl_t nodeh; + int err; + char piclclass[PICL_CLASSNAMELEN_MAX]; + uint32_t boardnum; + uint32_t bus_id; + uint32_t slot; + uint32_t freq; + char *model; + char *status; + + if (strcmp(args, PICL_CLASS_PCIEX) == 0) + bus_id = PCIEX_TYPE; + else + bus_id = PCI_TYPE; + + /* + * Check if it has the freq, if not, + * If not, use its parent's freq + * if its parent's freq is not found, return + */ + err = picldiag_get_clock_freq(pcih, &freq); + if (err == PICL_PROPNOTFOUND) { + err = picldiag_get_clock_from_parent(pcih, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + } else if (err != PICL_SUCCESS) + return (err); + + /* + * If no board# is found, set boardnum to 0 + */ + boardnum = picldiag_get_uint_propval(pcih, OBP_PROP_BOARD_NUM, &err); + if (err == PICL_PROPNOTFOUND) + boardnum = DEFAULT_BOARD_NUM; + else if (err != PICL_SUCCESS) + return (err); + + /* Walk through the children */ + + err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + /* + * Skip PCIEX, PCI bridge and USB devices because they will be + * processed later + */ + if ((strcmp(piclclass, PICL_CLASS_PCI) == 0) || + (strcmp(piclclass, PICL_CLASS_PCIEX) == 0) || + (strcmp(piclclass, PICL_CLASS_USB) == 0)) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } + + /* Get the device id for pci card */ + slot = picldiag_get_uint_propval(nodeh, + PICL_PROP_DEVICE_ID, &err); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } else if (err != PICL_SUCCESS) + return (err); + + /* Get the model of this card */ + err = picldiag_get_string_propval(nodeh, OBP_PROP_MODEL, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_STATUS, + &status); + if (err == PICL_PROPNOTFOUND) { + status = malloc(5); + if (status == NULL) + return (PICL_FAILURE); + strncpy(status, "okay", 5); + } else if (err != PICL_SUCCESS) + return (err); + + err = add_io_leaves(nodeh, NULL, boardnum, bus_id, slot, + freq, model, status); + + if (model != NULL) + free(model); + + if (status != NULL) + free(status); + + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + + } + + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + + return (err); +} + +/* + * add io devices in io list + * Its slot number is drived from upa-portid + */ +static int +add_io_devices(picl_nodehdl_t nodeh) +{ + int err; + uint64_t board_type; + char piclclass[PICL_CLASSNAMELEN_MAX]; + char name[MAXSTRLEN]; + char *devfs_path; + char *nameval; + uint32_t boardnum; + uint32_t bus_id; + uint32_t slot; + uint32_t freq; + char *model; + char *status; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_nodehdl_t fruparenth; + char *label; + + + bus_id = UPA_TYPE; + + /* + * If clock frequency can't be found from its parent, don't add + */ + err = picldiag_get_clock_from_parent(nodeh, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + else if (err != PICL_SUCCESS) + return (err); + + /* + * If no board# is found, set boardnum to 0 + */ + boardnum = picldiag_get_uint_propval(nodeh, OBP_PROP_BOARD_NUM, &err); + if (err == PICL_PROPNOTFOUND) + boardnum = DEFAULT_BOARD_NUM; + else if (err != PICL_SUCCESS) + return (err); + + /* + * get upa portid as slot number + * If upa portid is not found, don't add the card. + */ + slot = picldiag_get_uint_propval(nodeh, OBP_PROP_UPA_PORTID, + &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + else if (err != PICL_SUCCESS) + return (err); + + /* Get the model of this card */ + err = picldiag_get_string_propval(nodeh, OBP_PROP_MODEL, &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* + * check if it is a ffb device + * If it's a ffb device, append its board type to name + * otherwise, use its nodename + */ + err = picl_get_prop_by_name(nodeh, PICL_PROP_FFB_BOARD_REV, &proph); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propinfo_by_name(nodeh, PICL_PROP_NAME, + &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + nameval = alloca(pinfo.size); + if (nameval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(proph, nameval, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + (void) strlcpy(name, nameval, MAXSTRLEN); + } else if (err == PICL_SUCCESS) { + /* Find out if it's single or double buffered */ + board_type = picldiag_get_uint_propval(nodeh, + OBP_PROP_BOARD_TYPE, &err); + if (err == PICL_PROPNOTFOUND) + (void) strlcpy(name, FFB_NAME, sizeof (name)); + if (err == PICL_SUCCESS) { + if (board_type & FFB_B_BUFF) + (void) strlcpy(name, FFB_DOUBLE_BUF, + sizeof (name)); + else + (void) strlcpy(name, FFB_SINGLE_BUF, + sizeof (name)); + } else + return (err); + } else + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + (void) snprintf(name, sizeof (name), "%s (%s)", name, piclclass); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_STATUS, &status); + if (err == PICL_PROPNOTFOUND) { + status = malloc(5); + if (status == NULL) + return (PICL_FAILURE); + strncpy(status, "okay", 5); + } else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_fru_parent(nodeh, &fruparenth); + if (err == PICL_PROPNOTFOUND) { + label = NULL; + } else if (err != PICL_SUCCESS) { + return (err); + } else { + err = picldiag_get_combined_label(fruparenth, &label, 15); + if (err == PICL_PROPNOTFOUND) + label = NULL; + else if (err != PICL_SUCCESS) + return (err); + } + /* devfs-path */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH, + &devfs_path); + if (err == PICL_PROPNOTFOUND) + devfs_path = NULL; + else if (err != PICL_SUCCESS) + return (err); + + add_io_card(boardnum, bus_id, slot, label, freq, name, model, status, + devfs_path); + if (label != NULL) + free(label); + if (model != NULL) + free(model); + if (status != NULL) + free(status); + if (devfs_path != NULL) + free(devfs_path); + + return (PICL_SUCCESS); +} + +/* + * loop through all children and add io devices in io list + */ +static int +process_io_leaves(picl_nodehdl_t rooth) +{ + picl_nodehdl_t nodeh; + char classval[PICL_CLASSNAMELEN_MAX]; + int err; + + err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + classval, sizeof (classval)); + if (err != PICL_SUCCESS) + return (err); + + if (is_io_device(classval)) + err = add_io_devices(nodeh); + + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + } + + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + + return (err); +} + +/* + * callback function to add all io devices under upa in io list + */ +/*ARGSUSED*/ +static int +upa_callback(picl_nodehdl_t upah, void *args) +{ + int err; + + err = process_io_leaves(upah); + + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * display ffb hardware configuration + */ +/* ARGSUSED */ +static int +ffbconfig_callback(picl_nodehdl_t ffbh, void *arg) +{ + int err; + uint64_t board_rev; + uint64_t fbc_ver; + char *dac_ver; + char *fbram_ver; + + /* + * If it has PICL_PROP_FFB_BOARD_REV, it is a ffb device + * Otherwise, return. + */ + board_rev = picldiag_get_uint_propval(ffbh, PICL_PROP_FFB_BOARD_REV, + &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + log_printf("FFB Hardware Configuration:\n"); + log_printf("-----------------------------------\n"); + log_printf("Board rev: %lld\n", board_rev); + + fbc_ver = picldiag_get_uint_propval(ffbh, OBP_PROP_FBC_REG_ID, + &err); + if (err == PICL_SUCCESS) + log_printf("FBC version: 0x%llx\n", fbc_ver); + else if (err != PICL_PROPNOTFOUND) + return (err); + + err = picldiag_get_string_propval(ffbh, PICL_PROP_FFB_DAC_VER, + &dac_ver); + if (err == PICL_SUCCESS) { + log_printf("DAC: %s\n", dac_ver); + free(dac_ver); + } else if (err != PICL_PROPNOTFOUND) + return (err); + + err = picldiag_get_string_propval(ffbh, PICL_PROP_FFB_FBRAM_VER, + &fbram_ver); + if (err == PICL_SUCCESS) { + log_printf("3DRAM: %s\n", fbram_ver); + free(fbram_ver); + } else if (err != PICL_PROPNOTFOUND) + return (err); + + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +/* + * find all io devices and add them in the io list + */ +static int +gather_io_cards(picl_nodehdl_t plafh) +{ + int err; + + /* + * look for io devices under the immediate children of platform + */ + err = process_io_leaves(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_SBUS, + PICL_CLASS_SBUS, sbus_callback); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_PCI, + PICL_CLASS_PCI, pci_pciex_callback); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_PCIEX, + PICL_CLASS_PCIEX, pci_pciex_callback); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_UPA, + PICL_CLASS_UPA, upa_callback); + + return (err); +} + +static void +picldiag_display_io_cards(struct io_card *list) +{ + static int banner = 0; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + if (banner == 0) { + log_printf("Bus Freq Slot + Name +\n", 0); + log_printf("Type MHz Status Path" + " Model", 0); + log_printf("\n", 0); + log_printf("------ ---- ---------- " + "---------------------------- " + "--------------------", 0); + log_printf("\n", 0); + banner = 1; + } + + for (p = list; p != NULL; p = p -> next) { + log_printf("%-6s ", p->bus_type, 0); + log_printf("%-3d ", p->freq, 0); + /* + * We check to see if it's an int or + * a char string to display for slot. + */ + if (p->slot == PCI_SLOT_IS_STRING) + log_printf("%-10s ", p->slot_str, 0); + else + log_printf("%-10d ", p->slot, 0); + + log_printf("%-28.28s", p->name, 0); + if (strlen(p->name) > 28) + log_printf("+ ", 0); + else + log_printf(" ", 0); + log_printf("%-19.19s", p->model, 0); + if (strlen(p->model) > 19) + log_printf("+", 0); + log_printf("\n", 0); + log_printf(" %-10s ", p->status, 0); + set_exit_code(p->status); + if (strlen(p->notes) > 0) + log_printf("%s", p->notes, 0); + log_printf("\n\n", 0); + } +} + +/* + * display all io devices + */ +static int +display_io_device_info(picl_nodehdl_t plafh) +{ + int err; + + err = gather_io_cards(plafh); + if (err != PICL_SUCCESS) + return (err); + + logprintf_header(dgettext(TEXT_DOMAIN, "IO Devices"), + DEFAULT_LINE_WIDTH); + + picldiag_display_io_cards(io_card_list); + + free_io_cards(io_card_list); + + return (PICL_SUCCESS); +} + +/* + * print fan device information + */ +static int +logprintf_fan_info(picl_nodehdl_t fanh) +{ + int err; + char *label; + char *unit; + int64_t speed; + int64_t min_speed; + picl_nodehdl_t fruph; + + err = picldiag_get_fru_parent(fanh, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 20); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-20s ", label); + free(label); + + err = picldiag_get_label(fanh, &label); + if (err == PICL_SUCCESS) { + log_printf("%-14s ", label); + free(label); + } else if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf(" - "); + } else + return (err); + + speed = picldiag_get_uint_propval(fanh, PICL_PROP_FAN_SPEED, &err); + if (err == PICL_SUCCESS) { + min_speed = picldiag_get_uint_propval(fanh, + PICL_PROP_LOW_WARNING_THRESHOLD, &err); + if (err != PICL_SUCCESS) + min_speed = 0; + if (speed < min_speed) { + log_printf("failed (%lld", speed); + err = picldiag_get_string_propval(fanh, + PICL_PROP_FAN_SPEED_UNIT, &unit); + if (err == PICL_SUCCESS) { + log_printf("%s", unit); + free(unit); + } + log_printf(")"); + exit_code = PD_SYSTEM_FAILURE; + } else { + log_printf("okay"); + } + } else { + err = picldiag_get_string_propval(fanh, + PICL_PROP_FAN_SPEED_UNIT, &unit); + if (err == PICL_SUCCESS) { + log_printf("%-12s ", unit); + free(unit); + } + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +fan_callback(picl_nodehdl_t fanh, void *arg) +{ + int *countp = arg; + int err; + + if (*countp == 0) { + log_printf(dgettext(TEXT_DOMAIN, "Fan Status:\n")); + log_printf("-------------------------------------------\n"); + log_printf("Location Sensor Status\n"); + log_printf("-------------------------------------------\n"); + } + *countp += 1; + err = logprintf_fan_info(fanh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find fan device and print its speed + */ +static int +display_fan_speed(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_FAN, + &print_header, fan_callback); + return (err); +} + +/* + * print temperature sensor information + */ +static int +logprintf_temp_info(picl_nodehdl_t temph) +{ + int err; + char *label; + int64_t temperature; + int64_t threshold; + picl_nodehdl_t fruph; + char *status = "unknown"; + int got_temp = 0; + + err = picldiag_get_fru_parent(temph, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 14); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-14s ", label); + free(label); + + err = picldiag_get_label(temph, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-19s ", label); + free(label); + + temperature = picldiag_get_int_propval(temph, PICL_PROP_TEMPERATURE, + &err); + if (err == PICL_SUCCESS) { + got_temp = 1; + status = "okay"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_LOW_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature < threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_LOW_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature < threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_HIGH_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature > threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_HIGH_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature > threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + err = picldiag_get_string_propval(temph, PICL_PROP_CONDITION, &status); + if (err == PICL_SUCCESS) { + log_printf("%s", status); + set_exit_code(status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } else { + log_printf("%s ", status); + set_exit_code(status); + if (strcmp(status, "failed") == 0 || + strcmp(status, "warning") == 0) + log_printf("(%.2lldC)", temperature); + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +temp_callback(picl_nodehdl_t temph, void *arg) +{ + int err; + int *countp = arg; + + if (*countp == 0) { + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, "Temperature sensors:\n")); + log_printf("-----------------------------------------\n"); + log_printf("Location Sensor Status\n"); + log_printf("-----------------------------------------\n"); + } + *countp += 1; + err = logprintf_temp_info(temph); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find temp sensors and print the temp + */ +/* ARGSUSED */ +static int +display_temp(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_TEMPERATURE_SENSOR, + &print_header, temp_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_TEMPERATURE_INDICATOR, + &print_header, temp_callback); + return (err); +} + +/* + * print current sensor information + */ +static int +logprintf_current_info(picl_nodehdl_t currenth) +{ + int err; + char *label; + float current; + float threshold; + picl_nodehdl_t fruph; + char *status = "unknown"; + int got_current = 0; + + err = picldiag_get_fru_parent(currenth, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 20); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-20s ", label); + free(label); + + err = picldiag_get_label(currenth, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-10s ", label); + free(label); + + current = picldiag_get_float_propval(currenth, PICL_PROP_CURRENT, &err); + if (err == PICL_SUCCESS) { + status = "okay"; + got_current = 1; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, PICL_PROP_LOW_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_current && current < threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, PICL_PROP_LOW_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_current && current < threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, PICL_PROP_HIGH_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_current && current > threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, + PICL_PROP_HIGH_SHUTDOWN, &err); + if (err == PICL_SUCCESS) { + if (got_current && current > threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + err = picldiag_get_string_propval(currenth, + PICL_PROP_CONDITION, &status); + if (err == PICL_SUCCESS) { + log_printf(" %s", status); + set_exit_code(status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } else { + log_printf("%s ", status); + set_exit_code(status); + if (strcmp(status, "failed") == 0 || + strcmp(status, "warning") == 0) + log_printf("(%.2fA)", current); + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +current_callback(picl_nodehdl_t currh, void *arg) +{ + int err; + int *countp = arg; + + if (*countp == 0) { + log_printf("------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Current sensors:\n")); + log_printf("----------------------------------------\n"); + log_printf("Location Sensor Status\n"); + log_printf("----------------------------------------\n"); + } + *countp += 1; + err = logprintf_current_info(currh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find curr sensors and print the curr + */ +/* ARGSUSED */ +static int +display_current(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_CURRENT_SENSOR, + &print_header, current_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_CURRENT_INDICATOR, + &print_header, current_callback); + return (err); +} + +/* + * print voltage sensor information + */ +static int +logprintf_voltage_info(picl_nodehdl_t voltageh) +{ + int err; + char *label; + float voltage; + float threshold; + picl_nodehdl_t fruph; + char *status = "unknown"; + int got_voltage = 0; + + err = picldiag_get_fru_parent(voltageh, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 14); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-14s ", label); + free(label); + + err = picldiag_get_label(voltageh, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-12s ", label); + free(label); + + voltage = picldiag_get_float_propval(voltageh, PICL_PROP_VOLTAGE, &err); + if (err == PICL_SUCCESS) { + status = "okay"; + got_voltage = 1; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, PICL_PROP_LOW_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage < threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, PICL_PROP_LOW_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage < threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, PICL_PROP_HIGH_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage > threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, + PICL_PROP_HIGH_SHUTDOWN, &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage > threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + err = picldiag_get_string_propval(voltageh, + PICL_PROP_CONDITION, &status); + if (err == PICL_SUCCESS) { + log_printf("%s", status); + set_exit_code(status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } else { + log_printf("%s ", status); + set_exit_code(status); + if (strcmp(status, "warning") == 0 || + strcmp(status, "failed") == 0) + log_printf("(%.2fV)", voltage); + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +voltage_callback(picl_nodehdl_t voltageh, void *arg) +{ + int *countp = arg; + int err; + + if (*countp == 0) { + log_printf("------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Voltage sensors:\n")); + log_printf("-----------------------------------\n"); + log_printf("Location Sensor Status\n"); + log_printf("-----------------------------------\n"); + } + *countp += 1; + err = logprintf_voltage_info(voltageh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find voltage sensors and print voltage + */ +/* ARGSUSED */ +static int +display_voltage(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_VOLTAGE_SENSOR, + &print_header, voltage_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_VOLTAGE_INDICATOR, + &print_header, voltage_callback); + return (err); +} + +/* + * print led device information + */ +static int +logprintf_led_info(picl_nodehdl_t ledh) +{ + int err; + char *label; + char *state; + char *color; + picl_nodehdl_t fruph; + + err = picldiag_get_fru_parent(ledh, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 22); + if (err != PICL_SUCCESS) { + log_printf(" - ", label); + } else { + log_printf("%-22s ", label); + free(label); + } + + err = picldiag_get_label(ledh, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-20s ", label); + free(label); + + err = picldiag_get_string_propval(ledh, PICL_PROP_STATE, &state); + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf(" - "); + } else if (err != PICL_SUCCESS) { + return (err); + } else { + log_printf("%-10s ", state); + free(state); + } + + err = picldiag_get_string_propval(ledh, PICL_PROP_COLOR, &color); + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf("\n"); + } else if (err != PICL_SUCCESS) { + return (err); + } else { + log_printf("%-16s\n", color); + free(color); + } + + return (PICL_SUCCESS); +} + +static int +led_callback(picl_nodehdl_t ledh, void *arg) +{ + int *countp = arg; + int err; + + if (*countp == 0) { + + log_printf("--------------------------------------" + "------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Led State:\n")); + log_printf("----------------------------------------" + "----------------------\n"); + log_printf("Location Led State" + " Color\n"); + log_printf("----------------------------------------" + "----------------------\n"); + } + *countp += 1; + err = logprintf_led_info(ledh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find led devices and print status + */ +/* ARGSUSED */ +static int +display_led_status(picl_nodehdl_t plafh) +{ + int print_header; + + print_header = 0; + picl_walk_tree_by_class(plafh, PICL_CLASS_LED, + &print_header, led_callback); + return (PICL_SUCCESS); +} + +/* + * print keyswitch device information + */ +static int +logprintf_keyswitch_info(picl_nodehdl_t keyswitchh, picl_nodehdl_t fruph) +{ + int err; + char *label; + char *state; + + err = picldiag_get_combined_label(fruph, &label, 10); + if (err != PICL_SUCCESS) { + log_printf("%-14s", " -"); + } else { + log_printf("%-14s ", label); + free(label); + } + + err = picldiag_get_label(keyswitchh, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-11s ", label); + free(label); + + err = picldiag_get_string_propval(keyswitchh, PICL_PROP_STATE, &state); + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf(" -\n"); + } else if (err != PICL_SUCCESS) { + return (err); + } else { + log_printf("%s\n", state); + free(state); + } + + return (PICL_SUCCESS); +} + +static int +keyswitch_callback(picl_nodehdl_t keyswitchh, void *arg) +{ + int *countp = arg; + int err; + picl_nodehdl_t fruph; + + /* + * Tamale simulates a key-switch on ENxS. So the presence of a + * node of class keyswitch is not sufficient. If it has a fru parent + * or location parent, then believe it. + */ + err = picl_get_propval_by_name(keyswitchh, PICL_REFPROP_FRU_PARENT, + &fruph, sizeof (fruph)); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(keyswitchh, + PICL_REFPROP_LOC_PARENT, &fruph, sizeof (fruph)); + } + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) + return (PICL_WALK_CONTINUE); + if (err != PICL_SUCCESS) + return (err); + + if (*countp == 0) { + log_printf("-----------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Keyswitch:\n")); + log_printf("-----------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Location Keyswitch State\n")); + log_printf("-----------------------------------------\n"); + } + *countp += 1; + err = logprintf_keyswitch_info(keyswitchh, fruph); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * search children to find keyswitch device(s) and print status + */ +/* ARGSUSED */ +static int +display_keyswitch(picl_nodehdl_t plafh) +{ + int print_header = 0; + + picl_walk_tree_by_class(plafh, PICL_CLASS_KEYSWITCH, + &print_header, keyswitch_callback); + return (PICL_SUCCESS); +} + +/* + * display environment status + */ +static int +display_envctrl_status(picl_nodehdl_t plafh) +{ + logprintf_header(dgettext(TEXT_DOMAIN, "Environmental Status"), + DEFAULT_LINE_WIDTH); + + display_fan_speed(plafh); + display_temp(plafh); + display_current(plafh); + display_voltage(plafh); + display_keyswitch(plafh); + display_led_status(plafh); + + return (PICL_SUCCESS); +} + +/* + * print fru operational status + */ +static int +logprintf_fru_oper_status(picl_nodehdl_t fruh, int *countp) +{ + int err; + char *label; + char *status; + + err = picldiag_get_combined_label(fruh, &label, 23); + if (err != PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + + err = picldiag_get_string_propval(fruh, + PICL_PROP_OPERATIONAL_STATUS, &status); + if (err == PICL_SUCCESS) { + if (*countp == 0) { + logprintf_header(dgettext(TEXT_DOMAIN, + "FRU Operational Status"), + DEFAULT_LINE_WIDTH); + log_printf("---------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Fru Operational Status:\n")); + log_printf("---------------------------------\n"); + log_printf("Location Status\n"); + log_printf("---------------------------------\n"); + } + *countp += 1; + log_printf("%-23s ", label); + free(label); + log_printf("%s\n", status); + set_exit_code(status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + free(label); + return (err); + } else { + free(label); + } + return (PICL_WALK_CONTINUE); +} + +static int +fru_oper_status_callback(picl_nodehdl_t fruh, void *arg) +{ + int err; + + err = logprintf_fru_oper_status(fruh, (int *)arg); + return (err); +} + +/* + * display fru operational status + */ +static int +display_fru_oper_status(picl_nodehdl_t frutreeh) +{ + int print_header; + + print_header = 0; + picl_walk_tree_by_class(frutreeh, PICL_CLASS_FRU, + &print_header, fru_oper_status_callback); + return (PICL_SUCCESS); +} + +/* + * check if the node having the version prop + * If yes, print its nodename and version + */ +/* ARGSUSED */ +static int +asicrev_callback(picl_nodehdl_t nodeh, void *arg) +{ + uint32_t version; + char *name; + char *model; + char *status; + int err; + + /* + * Fire based platforms use "module-revision#" in place of "version#", + * so we need to look for this property if we don't find "version#" + */ + version = picldiag_get_uint_propval(nodeh, OBP_PROP_VERSION_NUM, &err); + if (err == PICL_PROPNOTFOUND) { + version = picldiag_get_uint_propval(nodeh, OBP_PROP_MODREV_NUM, + &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + } + if (err != PICL_SUCCESS) + return (err); + + /* devfs-path */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH, &name); + if (err == PICL_PROPNOTFOUND) + name = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* model */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_BINDING_NAME, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* status */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_STATUS, &status); + if (err == PICL_PROPNOTFOUND) + status = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* + * Display the data + */ + + /* name */ + if (name != NULL) { + log_printf("%-22s ", name); + free(name); + } else + log_printf("%-22s ", "unknown"); + /* model */ + if (model != NULL) { + log_printf("%-15s ", model); + free(model); + } else + log_printf("%-15s ", "unknown"); + /* status */ + if (status == NULL) + log_printf("%-15s ", "okay"); + else { + log_printf("%-15s ", status); + set_exit_code(status); + free(status); + } + /* revision */ + log_printf(" %-4d\n", version); + + return (PICL_WALK_CONTINUE); +} + +/* + * traverse the tree to display asic revision id for ebus + */ +/* ARGSUSED */ +static int +ebus_callback(picl_nodehdl_t ebush, void *arg) +{ + uint32_t id; + char *name; + int err; + char *model; + char *status; + + id = picldiag_get_uint_propval(ebush, OBP_PROP_REVISION_ID, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + /* devfs-path */ + err = picldiag_get_string_propval(ebush, PICL_PROP_DEVFS_PATH, &name); + if (err == PICL_PROPNOTFOUND) + name = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* model */ + err = picldiag_get_string_propval(ebush, PICL_PROP_BINDING_NAME, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* status */ + err = picldiag_get_string_propval(ebush, PICL_PROP_STATUS, &status); + if (err == PICL_PROPNOTFOUND) + status = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* + * Display the data + */ + + /* name */ + if (name != NULL) { + log_printf("%-22s ", name); + free(name); + } else + log_printf("%-22s ", "unknown"); + /* model */ + if (model != NULL) { + log_printf("%-15s ", model); + free(model); + } else + log_printf("%-15s ", "unknown"); + /* status */ + if (status == NULL) + log_printf("%-15s ", "okay"); + else { + log_printf("%-15s ", status); + set_exit_code(status); + free(status); + } + /* revision */ + log_printf(" %-4d\n", id); + + return (PICL_WALK_CONTINUE); +} + +/* + * display asic revision id + */ +static int +display_hw_revisions(picl_nodehdl_t plafh) +{ + int err; + + /* Print the header */ + logprintf_header(dgettext(TEXT_DOMAIN, "HW Revisions"), + DEFAULT_LINE_WIDTH); + + log_printf("ASIC Revisions:\n"); + log_printf("-----------------------------"); + log_printf("--------------------------------------\n"); + log_printf("Path Device"); + log_printf(" Status Revision\n"); + log_printf("-----------------------------"); + log_printf("--------------------------------------\n"); + + err = picl_walk_tree_by_class(plafh, NULL, NULL, asicrev_callback); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_EBUS, + NULL, ebus_callback); + if (err != PICL_SUCCESS) + return (err); + + log_printf("\n"); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_DISPLAY, + NULL, ffbconfig_callback); + return (err); +} + +/* + * find the options node and its powerfail_time prop + * If found, display the list of latest powerfail. + */ +/* ARGSUSED */ +static int +options_callback(picl_nodehdl_t nodeh, void *arg) +{ + time_t value; + char *failtime; + int err; + + err = picldiag_get_string_propval(nodeh, PROP_POWERFAIL_TIME, + &failtime); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_TERMINATE); + else if (err != PICL_SUCCESS) + return (err); + + value = (time_t)atoi(failtime); + free(failtime); + if (value == 0) + return (PICL_WALK_TERMINATE); + + log_printf(dgettext(TEXT_DOMAIN, "Most recent AC Power Failure:\n")); + log_printf("=============================\n"); + log_printf("%s", ctime(&value)); + log_printf("\n"); + return (PICL_WALK_TERMINATE); +} + +/* + * display the OBP and POST prom revisions + */ +/* ARGSUSED */ +static int +flashprom_callback(picl_nodehdl_t flashpromh, void *arg) +{ + picl_prophdl_t proph; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + picl_propinfo_t pinfo; + char *prom_version = NULL; + char *obp_version = NULL; + int err; + + err = picl_get_propinfo_by_name(flashpromh, OBP_PROP_VERSION, + &pinfo, &proph); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_TERMINATE); + else if (err != PICL_SUCCESS) + return (err); + + log_printf(dgettext(TEXT_DOMAIN, "System PROM revisions:\n")); + log_printf("----------------------\n"); + + /* + * If it's a table prop, the first element is OBP revision + * The second one is POST revision. + * If it's a charstring prop, the value will be only OBP revision + */ + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + prom_version = alloca(pinfo.size); + if (prom_version == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, prom_version, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + log_printf("%s\n", prom_version); + } + + if (pinfo.type != PICL_PTYPE_TABLE) /* not supported type */ + return (PICL_WALK_TERMINATE); + + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err == PICL_SUCCESS) { + /* get first row */ + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + prom_version = alloca(pinfo.size); + if (prom_version == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, prom_version, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + log_printf("%s\n", prom_version); + + /* get second row */ + err = picl_get_next_by_col(rowproph, &rowproph); + if (err == PICL_SUCCESS) { + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + obp_version = alloca(pinfo.size); + if (obp_version == NULL) + return (PICL_FAILURE); + err = picl_get_propval(rowproph, obp_version, + pinfo.size); + if (err != PICL_SUCCESS) + return (err); + log_printf("%s\n", obp_version); + } + } + + return (PICL_WALK_TERMINATE); +} + +static int +display_system_info(int serrlog, int log_flag, picl_nodehdl_t rooth) +{ + int err; + picl_nodehdl_t plafh; + picl_nodehdl_t frutreeh; + + err = picldiag_get_node_by_name(rooth, PICL_NODE_PLATFORM, &plafh); + if (err != PICL_SUCCESS) + return (err); + + if (!log_flag) { + err = display_platform_banner(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_system_clock(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY, + PICL_CLASS_MEMORY, memory_callback); + if (err != PICL_SUCCESS) + return (err); + + err = display_cpu_info(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_io_device_info(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_memory_config(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_usb_devices(plafh); + if (err != PICL_SUCCESS) + return (err); + } + + if (serrlog) { + err = picl_walk_tree_by_class(rooth, PICL_CLASS_OPTIONS, + NULL, options_callback); + if (err != PICL_SUCCESS) + return (err); + + err = display_envctrl_status(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_node_by_name(rooth, PICL_NODE_FRUTREE, + &frutreeh); + if (err != PICL_SUCCESS) + return (err); + + err = display_fru_oper_status(frutreeh); + if (err != PICL_SUCCESS) + return (err); + + err = display_hw_revisions(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_FLASHPROM, + NULL, flashprom_callback); + if (err != PICL_SUCCESS) + return (err); + + err = display_serial_number(plafh); + if ((err != PICL_SUCCESS) && (err != PICL_PROPNOTFOUND)) + return (err); + } + + return (PICL_SUCCESS); +} + +/* + * do_prominfo is called from main in prtdiag. It returns PD_SYSTEM_FAILURE if + * any system failure is detected, PD_INTERNAL_FAILURE for internal errors and + * PD_SUCCESS otherwise. main uses the return value as the exit code. + */ +/* ARGSUSED */ +int +do_prominfo(int serrlog, char *pgname, int log_flag, int prt_flag) +{ + int err; + char *errstr; + int done; + picl_nodehdl_t rooth; + + err = picl_initialize(); + if (err != PICL_SUCCESS) { + fprintf(stderr, EM_INIT_FAIL, picl_strerror(err)); + return (PD_INTERNAL_FAILURE); + } + + do { + done = 1; + err = picl_get_root(&rooth); + if (err != PICL_SUCCESS) { + fprintf(stderr, EM_GET_ROOT_FAIL, picl_strerror(err)); + return (PD_INTERNAL_FAILURE); + } + + err = display_system_info(serrlog, log_flag, rooth); + + if ((err == PICL_STALEHANDLE) || (err == PICL_INVALIDHANDLE)) + done = 0; + } while (!done); + + if (err != PICL_SUCCESS) { + errstr = picl_strerror(err); + fprintf(stderr, EM_PRTDIAG_FAIL); + fprintf(stderr, "%s\n", errstr? errstr : " "); + exit_code = PD_INTERNAL_FAILURE; + } + + (void) picl_shutdown(); + + return (exit_code); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/desktop/nonpicl/Makefile b/usr/src/lib/libprtdiag_psr/sparc/desktop/nonpicl/Makefile new file mode 100644 index 0000000000..1f15ae4d58 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/desktop/nonpicl/Makefile @@ -0,0 +1,107 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/desktop/nonpicl/Makefile + +UTSBASE = $(SRC)/uts + +PLATFORM_OBJECTS= desktop.o + +objs/%.o pics/%.o: ../common/%.c + $(COMPILE.c) $(IFLAGS) -o $@ $< + $(POST_PROCESS_O) + +include $(SRC)/lib/libprtdiag_psr/sparc/Makefile.com + +SRCS= $(OBJECTS:%.o=../common/%.c) + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../../libprtdiag/inc +LINTFLAGS += $(IFLAGS) + +# +# desktop platform library should install into +# SUNW,Ultra-2. All other desktop platforms can +# link to /usr/platform/SUNW,Ultra-2/lib/libprtdiag_psr.so +# +PLATFORM=SUNW,Ultra-2 + +# +# For all other sun4u platforms NOT delivered by ON (ie. delivered +# by SME or external hardware vendors) we add a default library in +# /usr/platform/sun4u/lib so that prtdiag will default to using +# the desktop library for any of these platforms. (this will be a +# link to ../../SUNW,Ultra-2/lib +# +DEF_PLATFORM=sun4u +USR_DEF_PSM_LIB_DIR=$(USR_PLAT_DIR)/$(DEF_PLATFORM)/lib +USR_DEF_PSM_LIBS=$(LIBS:%=$(USR_DEF_PSM_LIB_DIR)/%) + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) $(USR_DEF_PSM_LIBS) + +# +# install rules for SUNW,Ultra-2/lib/libprtdiag_psr.so.1 +# +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u; $(MAKE) $(USR_PSM_LIB_DIR) + +# +# install rules for sun4u/lib/libprtdiag_psr.so.1 +# +$(USR_DEF_PSM_LIB_DIR)/%: % $(USR_DEF_PSM_LIB_DIR) + $(RM) -r $@; \ + $(SYMLINK) ../../$(PLATFORM)/lib/libprtdiag_psr.so.1 $@ $(CHOWNLINK) $(CHGRPLINK) + +$(USR_DEF_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u; $(MAKE) $(USR_DEF_PSM_LIB_DIR) + + +# +# Rules for making message files +# +POFILE= libprtdiag_psr_desktop.po +POFILES= desktop.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../common/desktop.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po + diff --git a/usr/src/lib/libprtdiag_psr/sparc/desktop/picl/Makefile b/usr/src/lib/libprtdiag_psr/sparc/desktop/picl/Makefile new file mode 100644 index 0000000000..f0e48afa83 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/desktop/picl/Makefile @@ -0,0 +1,120 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/desktop/picl/Makefile + +UTSBASE = $(SRC)/uts + +PLATFORM_OBJECTS= picldiag.o + +objs/%.o pics/%.o: ../common/%.c + $(COMPILE.c) $(IFLAGS) -o $@ $< + $(POST_PROCESS_O) + +include $(SRC)/lib/libprtdiag_psr/sparc/Makefile.com + +SRCS= $(OBJECTS:%.o=../common/%.c) + +LDLIBS += -lpicl + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../../libprtdiag/inc +IFLAGS += -I$(SRC)/cmd/picl/plugins/inc +LINTFLAGS += $(IFLAGS) + +# +# links in /usr/platform +# +LINKED_PLATFORMS = SUNW,Sun-Blade-1000 SUNW,Netra-T4 +LINKED_PLATFORMS += SUNW,Sun-Blade-1500 +LINKED_PLATFORMS += SUNW,Sun-Blade-2500 +LINKED_PLATFORMS += SUNW,A70 +LINKED_PLATFORMS += SUNW,Sun-Fire-V445 +LINKED_PLATFORMS += SUNW,Sun-Fire-V215 +LINKED_PLATFORMS += SUNW,Serverblade1 +LINKED_PLATFORMS += SUNW,Sun-Fire-V240 +LINKED_PLATFORMS += SUNW,Sun-Fire-V250 +LINKED_PLATFORMS += SUNW,Sun-Fire-V440 + +LINKED_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%) +LINKED_LIB_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib) +LINKED_PRTDIAG_DIRS = \ + $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib/libprtdiag_psr.so.1) + +# +# Sun-Blade-100 and Sun-Blade-1000 platform library should install into +# SUNW,Sun-Blade-100. Sun-Blade-100 platforms can +# link to /usr/platform/SUNW,Sun-Blade-100/lib/libprtdiag_psr.so +# +PLATFORM= SUNW,Sun-Blade-100 + +.KEEP_STATE: + +PLATLIBS= $(PLATFORM:%=$(USR_PLAT_DIR)/%/lib/) + +install: all $(PLATLIBS) $(USR_PSM_LIBS) \ + $(LINKED_PRTDIAG_DIRS) + +# +# install rules for SUNW,Sun-Blade-100/lib/libprtdiag_psr.so.1 +# +$(PLATLIBS): + $(INS.dir) + +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +$(USR_PSM_LIB_DIR): + $(INS.dir) + +$(LINKED_DIRS): $(USR_PLAT_DIR) + -$(INS.dir.root.sys) + +$(LINKED_LIB_DIRS): $(LINKED_DIRS) + -$(INS.dir.root.sys) + +$(LINKED_PRTDIAG_DIRS): $(USR_PLAT_DIR) + -$(INS.slink6) + +# +# Rules for making message files +# +POFILE= libprtdiag_psr_desktop_picl.po +POFILES= desktop.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../common/picldiag.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po + diff --git a/usr/src/lib/libprtdiag_psr/sparc/javelin/Makefile b/usr/src/lib/libprtdiag_psr/sparc/javelin/Makefile new file mode 100644 index 0000000000..294cd3d4cd --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/javelin/Makefile @@ -0,0 +1,77 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/javelin/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= javelin.o + +include ../Makefile.com + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../libprtdiag/inc +LINTFLAGS += $(IFLAGS) +LDLIBS += -lkstat + +# +# Workgroup Server platform library should install into +# SUNW,Ultra-4. All other desktop platforms can +# link to /usr/platform/SUNW,Ultra-4/lib/libprtdiag_psr.so +# +PLATFORM=SUNW,Ultra-250 + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/javelin; $(MAKE) $(USR_PSM_LIB_DIR) + +# +# install rule +# +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +POFILE= libprtdiag_psr_javelin.po +POFILES= javelin.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/javelin.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po + diff --git a/usr/src/lib/libprtdiag_psr/sparc/javelin/common/javelin.c b/usr/src/lib/libprtdiag_psr/sparc/javelin/common/javelin.c new file mode 100644 index 0000000000..2c2d77b1fa --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/javelin/common/javelin.c @@ -0,0 +1,1375 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Javelin Platform specific functions. + * + * called when : + * machine_type == MTYPE_JAVELIN + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +extern int print_flag; + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (workgroup server systems only) + */ +int error_check(Sys_tree *tree, struct system_kstat_data *kstats); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); +int disp_fail_parts(Sys_tree *tree); +void display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void display_boardnum(int num); +void display_pci(Board_node *); +void display_io_cards(struct io_card *list); +void display_ffb(Board_node *, int); +void read_platform_kstats(Sys_tree *tree, + struct system_kstat_data *sys_kstat, + struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep); + +/* local functions */ +static int disp_envc_status(struct system_kstat_data *); +static void tazjav_disp_asic_revs(Sys_tree *); +static int tazmo_physical_slot(Prom_node *, Prom_node *, int, char *); +static Prom_node *dev_next_node_sibling(Prom_node *root, char *name); + + +int +error_check(Sys_tree *tree, struct system_kstat_data *kstats) +{ + int exit_code = 0; /* init to all OK */ + +#ifdef lint + kstats = kstats; +#endif + /* + * silently check for any types of machine errors + */ + print_flag = 0; + if (disp_fail_parts(tree) || disp_envc_status(kstats)) { + /* set exit_code to show failures */ + exit_code = 1; + } + print_flag = 1; + + return (exit_code); +} + +/* Search for and return the node's sibling */ +static Prom_node * +dev_next_node_sibling(Prom_node *root, char *name) +{ + if (root == NULL) + return (NULL); + + /* look at your siblings */ + if (dev_find_node(root->sibling, name) != NULL) + return (root->sibling); + + return (NULL); /* not found */ +} + +/* + * This function displays memory configurations specific to Tazmo/Javelin. + * The PROM device tree is read to obtain this information. + * Some of the information obtained is memory interleave factor, + * DIMM sizes, DIMM socket names. + */ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + Board_node *bnode; + Prom_node *memory; + Prom_node *bank; + Prom_node *dimm; + uint_t *preg; + uint_t interlv; + unsigned long size = 0; + int bank_count = 0; + char *sock_name; + char *status; + Prop *status_prop; + char interleave[8]; + int total_size = 0; +#ifdef lint + grps = grps; +#endif + + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " Memory "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + bnode = tree->bd_list; + memory = dev_find_node(bnode->nodes, "memory"); + preg = (uint_t *)(get_prop_val(find_prop(memory, "interleave"))); + if (preg) { + interlv = preg[4]; + log_printf("Memory Interleave Factor = %d-way\n\n", interlv, 0); + } + log_printf(" Interlv. Socket Size\n", 0); + log_printf("Bank Group Name (MB) Status\n", 0); + log_printf("---- ----- ------ ---- ------\n", 0); + + dimm = bnode->nodes; + for (bank = dev_find_node(bnode->nodes, "bank"); bank != NULL; + bank = dev_next_node(bank, "bank")) { + int bank_size = 0; + uint_t *reg_prop; + + preg = (uint_t *)(get_prop_val( + find_prop(bank, "bank-interleave"))); + + reg_prop = (uint_t *)(get_prop_val( + find_prop(bank, "reg"))); + + /* + * Skip empty banks + */ + if (((reg_prop[2]<<12) + (reg_prop[3]>>20)) == 0) { + bank_count++; + continue; + } + + if (preg) { + interlv = preg[2]; + (void) sprintf(interleave, " %d ", interlv); + bank_size = (preg[0]<<12) + (preg[1]>>20); + } else { + (void) sprintf(interleave, "%s", "none"); + preg = (uint_t *)(get_prop_val(find_prop(bank, "reg"))); + if (preg) { + bank_size = (preg[2]<<12) + (preg[3]>>20); + } + } + for (dimm = dev_find_node(bank, "dimm"); dimm != NULL; + dimm = dev_next_node_sibling(dimm, "dimm")) { + char dimm_status[16]; + + sock_name = (char *)(get_prop_val( + find_prop(dimm, "socket-name"))); + preg = (uint_t *)(get_prop_val(find_prop(dimm, "reg"))); + size = (preg[2]<<12) + (preg[3]>>20); + if ((status_prop = find_prop(dimm, "status")) == NULL) { + (void) sprintf(dimm_status, "%s", "OK"); + } else { + status = (char *)(get_prop_val(status_prop)); + (void) sprintf(dimm_status, "%s", status); + } + log_printf("%3d %5s %6s %4d %6s\n", + bank_count, interleave, sock_name, + size, dimm_status, 0); + } + total_size += bank_size; + bank_count++; + } + log_printf("\n", 0); +} + +/* + * disp_fail_parts + * + * Display the failed parts in the system. This function looks for + * the status property in all PROM nodes. On systems where + * the PROM does not supports passing diagnostic information + * thruogh the device tree, this routine will be silent. + */ +int +disp_fail_parts(Sys_tree *tree) +{ + int exit_code; + int system_failed = 0; + Board_node *bnode = tree->bd_list; + Prom_node *pnode; + char *fru; + char *sock_name; + char slot_str[MAXSTRLEN]; + + exit_code = 0; + + /* go through all of the boards looking for failed units. */ + while (bnode != NULL) { + /* find failed chips */ + pnode = find_failed_node(bnode->nodes); + if ((pnode != NULL) && !system_failed) { + system_failed = 1; + exit_code = 1; + if (print_flag == 0) { + return (exit_code); + } + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, "Failed Field " + "Replaceable Units (FRU) in System:\n"), 0); + log_printf("==========================" + "====================\n", 0); + } + + while (pnode != NULL) { + void *value; + char *name; /* node name string */ + char *type; /* node type string */ + + value = get_prop_val(find_prop(pnode, "status")); + name = get_node_name(pnode); + + /* sanity check of data retreived from PROM */ + if ((value == NULL) || (name == NULL)) { + pnode = next_failed_node(pnode); + continue; + } + + + log_printf(dgettext(TEXT_DOMAIN, "%s unavailable :\n"), + name, 0); + + log_printf(dgettext(TEXT_DOMAIN, "\tPROM fault " + "string: %s\n"), value, 0); + + log_printf(dgettext(TEXT_DOMAIN, "\tFailed Field " + "Replaceable Unit is "), 0); + + /* + * Determine whether FRU is CPU module, system + * board, or SBus card. + */ + if ((name != NULL) && (strstr(name, "sbus"))) { + + log_printf(dgettext(TEXT_DOMAIN, "SBus " + "Card %d\n"), get_sbus_slot(pnode), 0); + + } else if (((name = get_node_name(pnode)) != + NULL) && (strstr(name, "pci"))) { + + log_printf(dgettext(TEXT_DOMAIN, "system " + "board\n"), 0); + + } else if (((name = get_node_name(pnode)) != + NULL) && (strstr(name, "ffb"))) { + + log_printf(dgettext(TEXT_DOMAIN, "FFB " + "Card %d\n"), tazmo_physical_slot( + dev_find_node(bnode->nodes, "slot2dev"), + pnode, -1, slot_str), 0); + + } else if (((name = get_node_name(pnode->parent)) != + NULL) && (strstr(name, "pci"))) { + + (void) tazmo_physical_slot( + NULL, + pnode->parent, + get_pci_device(pnode), + slot_str); + log_printf(dgettext(TEXT_DOMAIN, "PCI Card " + "in %s\n"), slot_str, 0); + + } else if (((type = get_node_type(pnode)) != NULL) && + (strstr(type, "cpu"))) { + + log_printf( + dgettext(TEXT_DOMAIN, "UltraSPARC " + "module Module %d\n"), + get_id(pnode)); + + } else if (((type = get_node_type(pnode)) != NULL) && + (strstr(type, "memory-module"))) { + + fru = (char *)(get_prop_val( + find_prop(pnode, "fru"))); + sock_name = (char *)(get_prop_val( + find_prop(pnode, "socket-name"))); + log_printf( + dgettext(TEXT_DOMAIN, "%s in " + "socket %s\n"), fru, + sock_name, 0); + } + pnode = next_failed_node(pnode); + } + bnode = bnode->next; + } + + if (!system_failed) { + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, "No failures found " + "in System\n"), 0); + log_printf("===========================\n", 0); + } + + if (system_failed) + return (1); + else + return (0); +} + +void +display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) +{ +#ifdef lint + kstats = kstats; +#endif + /* Display failed units */ + (void) disp_fail_parts(tree); +} + + +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + /* + * Now display the last powerfail time and the fatal hardware + * reset information. We do this under a couple of conditions. + * First if the user asks for it. The second is iof the user + * told us to do logging, and we found a system failure. + */ + if (flag) { + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + (void) disp_envc_status(kstats); + + tazjav_disp_asic_revs(tree); + + platform_disp_prom_version(tree); + } + return; + +} + +/* ARGSUSED */ +void +display_boardnum(int num) +{ + log_printf("SYS ", 0); +} + + + +/* + * display_pci + * Display all the PCI IO cards on this board. + */ + +/* ARGSUSED */ +void +display_pci(Board_node *board) +{ + struct io_card *card_list = NULL; + struct io_card card; + void *value; + Prom_node *pci; + Prom_node *card_node; + + if (board == NULL) + return; + + /* Initialize all the common information */ + card.display = 1; + card.board = board->board_num; + (void) sprintf(card.bus_type, "PCI"); + + for (pci = dev_find_node(board->nodes, PCI_NAME); pci != NULL; + pci = dev_next_node(pci, PCI_NAME)) { + char *name; + Prom_node *prev_parent = NULL; + int prev_device = -1; + int pci_pci_bridge = 0; + + /* + * If we have reached a pci-to-pci bridge node, + * we are one level below the 'pci' nodes level + * in the device tree. To get back to that level, + * the search should continue with the sibling of + * the parent or else the remaining 'pci' cards + * will not show up in the output. + */ + if (find_prop(pci, "upa-portid") == NULL) { + if ((pci->parent->sibling != NULL) && + (strcmp(get_prop_val( + find_prop(pci->parent->sibling, + "name")), PCI_NAME) == 0)) + pci = pci->parent->sibling; + else { + pci = pci->parent->sibling; + continue; + } + } + + /* Skip all failed nodes for now */ + if (node_failed(pci)) + continue; + + /* Fill in frequency */ + value = get_prop_val(find_prop(pci, "clock-frequency")); + if (value == NULL) + card.freq = -1; + else + card.freq = ((*(int *)value) + 500000) / 1000000; + + /* Walk through the PSYCHO children */ + card_node = pci->child; + while (card_node != NULL) { + Prop *compat = NULL; + + /* If it doesn't have a name, skip it */ + name = (char *)get_prop_val( + find_prop(card_node, "name")); + if (name == NULL) { + card_node = card_node->sibling; + continue; + } + + /* + * If this is a PCI bridge, then display its + * children. + */ + if (strcmp(name, "pci") == 0) { + card_node = card_node->child; + pci_pci_bridge = 1; + continue; + } + + /* Get the slot number for this card */ + if (pci_pci_bridge) { + card.slot = tazmo_physical_slot( + dev_find_node(board->nodes, "slot2dev"), + pci, + get_pci_to_pci_device( + card_node->parent), + card.slot_str); + } else + card.slot = tazmo_physical_slot( + dev_find_node(board->nodes, + "slot2dev"), + pci, + get_pci_device(card_node), + card.slot_str); + + /* + * Check that duplicate devices are not reported + * on Tazmo. + */ + if ((card_node->parent == prev_parent) && + (get_pci_device(card_node) == prev_device) && + (pci_pci_bridge == 0)) + card.slot = -1; + prev_parent = card_node->parent; + prev_device = get_pci_device(card_node); + + + if (card.slot == -1 || strstr(name, "ebus")) { + card_node = card_node->sibling; + continue; + } + + /* XXX - Don't know how to get status for PCI cards */ + card.status[0] = '\0'; + + /* Get the model of this card */ + value = get_prop_val(find_prop(card_node, "model")); + if (value == NULL) + card.model[0] = '\0'; + else + (void) sprintf(card.model, "%s", + (char *)value); + + /* + * Check if further processing is necessary to display + * this card uniquely. + */ + distinguish_identical_io_cards(name, card_node, &card); + + + /* + * If we haven't figured out the frequency yet, + * try and get it from the card. + */ + value = get_prop_val(find_prop(pci, "clock-frequency")); + if (value != NULL && card.freq == -1) + card.freq = ((*(int *)value) + 500000) + / 1000000; + + + value = get_prop_val(find_prop(card_node, + "compatible")); + + /* + * On Tazmo, we would like to print out the last + * string of the "compatible" property if it exists. + * The IEEE 1275 spec. states that this last string + * will be the classcode name. + */ + if (value != NULL) { + char *tval; + int index; + const int always = 1; + + tval = (char *)value; + index = 0; + compat = find_prop(card_node, "compatible"); + while (always) { + if ((strlen(tval) + 1) == + (compat->size - index)) + break; + index += strlen(tval) + 1; + tval += strlen(tval) + 1; + } + value = (void *)tval; + } + + if (value != NULL) + (void) sprintf(card.name, "%s-%s", + (char *)name, (char *)value); + else + (void) sprintf(card.name, "%s", + (char *)name); + + if (card.freq != -1) + card_list = insert_io_card(card_list, &card); + + /* + * If we are done with the children of the pci bridge, + * we must continue with the remaining siblings of + * the pci-to-pci bridge. + */ + if ((card_node->sibling == NULL) && pci_pci_bridge) { + card_node = card_node->parent->sibling; + pci_pci_bridge = 0; + } else + card_node = card_node->sibling; + } + } + + display_io_cards(card_list); + free_io_cards(card_list); +} + + +/* + * Print out all the io cards in the list. Also print the column + * headers if told to do so. + */ +void +display_io_cards(struct io_card *list) +{ + static int banner = 0; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + if (banner == 0) { + log_printf(" Bus Freq\n", 0); + log_printf("Brd Type MHz Slot " + "Name " + "Model", 0); + log_printf("\n", 0); + log_printf("--- ---- ---- ---- " + "-------------------------------- " + "----------------------", 0); + log_printf("\n", 0); + banner = 1; + } + + for (p = list; p != NULL; p = p -> next) { + log_printf("SYS ", p->board, 0); + log_printf("%-4s ", p->bus_type, 0); + log_printf("%3d ", p->freq, 0); + log_printf("%3d ", p->slot, 0); + log_printf("%-32.32s", p->name, 0); + if (strlen(p->name) > 32) + log_printf("+ ", 0); + else + log_printf(" ", 0); + log_printf("%-22.22s", p->model, 0); + if (strlen(p->model) > 22) + log_printf("+", 0); + log_printf("\n", 0); + } +} + +/* + * display_ffb + * Display all FFBs on this board. It can either be in tabular format, + * or a more verbose format. + */ +void +display_ffb(Board_node *board, int table) +{ + Prom_node *ffb; + void *value; + struct io_card *card_list = NULL; + struct io_card card; + + if (board == NULL) + return; + + /* Fill in common information */ + card.display = 1; + card.board = board->board_num; + (void) sprintf(card.bus_type, "UPA"); + card.freq = sys_clk; + + for (ffb = dev_find_node(board->nodes, FFB_NAME); ffb != NULL; + ffb = dev_next_node(ffb, FFB_NAME)) { + if (table == 1) { + /* Print out in table format */ + + /* XXX - Get the slot number (hack) */ + card.slot = tazmo_physical_slot( + dev_find_node(board->nodes, "slot2dev"), + ffb, + -1, + card.slot_str); + + /* Find out if it's single or double buffered */ + (void) sprintf(card.name, "FFB"); + value = get_prop_val(find_prop(ffb, "board_type")); + if (value != NULL) + if ((*(int *)value) & FFB_B_BUFF) + (void) sprintf(card.name, + "FFB, Double Buffered"); + else + (void) sprintf(card.name, + "FFB, Single Buffered"); + + /* Print model number */ + card.model[0] = '\0'; + value = get_prop_val(find_prop(ffb, "model")); + if (value != NULL) + (void) sprintf(card.model, "%s", + (char *)value); + + card_list = insert_io_card(card_list, &card); + } else { + /* print in long format */ + char device[MAXSTRLEN]; + int fd = -1; + struct dirent *direntp; + DIR *dirp; + union strap_un strap; + struct ffb_sys_info fsi; + + /* Find the device node using upa address */ + value = get_prop_val(find_prop(ffb, "upa-portid")); + if (value == NULL) + continue; + + (void) sprintf(device, "%s@%x", FFB_NAME, + *(int *)value); + if ((dirp = opendir("/devices")) == NULL) + continue; + + while ((direntp = readdir(dirp)) != NULL) { + if (strstr(direntp->d_name, device) != NULL) { + (void) sprintf(device, "/devices/%s", + direntp->d_name); + fd = open(device, O_RDWR, 0666); + break; + } + } + (void) closedir(dirp); + + if (fd == -1) + continue; + + if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0) + continue; + + log_printf("FFB Hardware Configuration:\n", 0); + log_printf("-----------------------------------\n", 0); + + strap.ffb_strap_bits = fsi.ffb_strap_bits; + log_printf("\tBoard rev: %d\n", + (int)strap.fld.board_rev, 0); + log_printf("\tFBC version: " + "0x%x\n", fsi.fbc_version, 0); + log_printf("\tDAC: %s\n", + fmt_manf_id(fsi.dac_version, device), 0); + log_printf("\t3DRAM: %s\n", + fmt_manf_id(fsi.fbram_version, device), 0); + log_printf("\n", 0); + } + } + + display_io_cards(card_list); + free_io_cards(card_list); +} + +/* + * This module does the reading and interpreting of javelin system + * kstats. These kstats are created by the environ drivers. + */ +void +read_platform_kstats(Sys_tree *tree, struct system_kstat_data *sys_kstat, + struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep) +{ + kstat_ctl_t *kc; + struct envctrltwo_kstat_data *ecp; + kstat_t *ksp; + + if ((kc = kstat_open()) == NULL) { + return; + } +#ifdef lint + tree = tree; + bdp = bdp; + ep = ep; +#endif + + /* read the envctrltwo kstats */ + ecp = &sys_kstat->envc_data; + + /* Read the power supply kstats */ + ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0, + ENVCTRL_KSTAT_PSNAME2); + + if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) { + (void) memcpy(ecp->ps_kstats, ksp->ks_data, + ksp->ks_ndata * sizeof (envctrl_ps2_t)); + } else { + sys_kstat->envctrltwo_kstat_ok = B_FALSE; + return; + } + + ecp->num_ps_kstats = ksp->ks_ndata; + + /* Read the fan status kstats */ + ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0, + ENVCTRL_KSTAT_FANSTAT); + + if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) { + (void) memcpy(ecp->fan_kstats, ksp->ks_data, + ksp->ks_ndata * sizeof (envctrl_fan_t)); + } else { + sys_kstat->envctrltwo_kstat_ok = B_FALSE; + return; + } + + ecp->num_fan_kstats = ksp->ks_ndata; + + /* Read the enclosure kstats */ + ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0, + ENVCTRL_KSTAT_ENCL); + + if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) { + (void) memcpy(ecp->encl_kstats, ksp->ks_data, + ksp->ks_ndata * sizeof (envctrl_encl_t)); + } else { + sys_kstat->envctrltwo_kstat_ok = B_FALSE; + return; + } + + ecp->num_encl_kstats = ksp->ks_ndata; + + /* Read the temperature kstats */ + ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0, + ENVCTRL_KSTAT_TEMPERATURE); + + if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) { + (void) memcpy(ecp->temp_kstats, ksp->ks_data, + ksp->ks_ndata * sizeof (envctrl_temp_t)); + } else { + sys_kstat->envctrltwo_kstat_ok = B_FALSE; + return; + } + + ecp->num_temp_kstats = ksp->ks_ndata; + + /* Read the disk kstats */ + ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0, + ENVCTRL_KSTAT_DISK); + + if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) { + (void) memcpy(ecp->disk_kstats, ksp->ks_data, + ksp->ks_ndata * sizeof (envctrl_disk_t)); + } else { + sys_kstat->envctrltwo_kstat_ok = B_FALSE; + return; + } + + ecp->num_disk_kstats = ksp->ks_ndata; + + sys_kstat->envctrltwo_kstat_ok = 1; + return; + +} + +/* + * Walk the PROM device tree and build the system tree and root tree. + * Nodes that have a board number property are placed in the board + * structures for easier processing later. Child nodes are placed + * under their parents. ffb (Fusion Frame Buffer) nodes are handled + * specially, because they do not contain board number properties. + * This was requested from OBP, but was not granted. So this code + * must parse the MID of the FFB to find the board#. + */ +Prom_node * +walk(Sys_tree *tree, Prom_node *root, int id) +{ + register int curnode; + Prom_node *pnode; + char *name; + char *type; + char *model; + int board_node = 0; + + /* allocate a node for this level */ + if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) == + NULL) { + perror("malloc"); + exit(2); /* program errors cause exit 2 */ + } + + /* assign parent Prom_node */ + pnode->parent = root; + pnode->sibling = NULL; + pnode->child = NULL; + + /* read properties for this node */ + dump_node(pnode); + + /* + * Place a node in a 'board' if it has 'board'-ness. The definition + * is that all nodes that are children of root should have a + * board# property. But the PROM tree does not exactly follow + * this. This is where we start hacking. The name 'ffb' can + * change, so watch out for this. + * + * The UltraSPARC, sbus, pci and ffb nodes will exit in + * the desktops and will not have board# properties. These + * cases must be handled here. + * + * PCI to PCI bridges also have the name "pci", but with different + * model property values. They should not be put under 'board'. + */ + name = get_node_name(pnode); + type = get_node_type(pnode); + model = (char *)get_prop_val(find_prop(pnode, "model")); +#ifdef DEBUG + if (name != NULL) + printf("name=%s ", name); + if (type != NULL) + printf("type=%s ", type); + if (model != NULL) + printf("model=%s", model); + printf("\n"); + + if (model == NULL) + model = ""; +#endif + if (type == NULL) + type = ""; + if (name != NULL) { + if (has_board_num(pnode)) { + add_node(tree, pnode); + board_node = 1; +#ifdef DEBUG + printf("ADDED BOARD name=%s type=%s model=%s\n", + name, type, model); +#endif + } else if ((strcmp(name, FFB_NAME) == 0) || + (strcmp(type, "cpu") == 0) || + + ((strcmp(name, "pci") == 0) && (model != NULL) && + (strcmp(model, "SUNW,psycho") == 0)) || + + ((strcmp(name, "pci") == 0) && (model != NULL) && + (strcmp(model, "SUNW,sabre") == 0)) || + + (strcmp(name, "counter-timer") == 0) || + (strcmp(name, "sbus") == 0) || + (strcmp(name, "memory") == 0) || + (strcmp(name, "mc") == 0) || + (strcmp(name, "associations") == 0)) { + add_node(tree, pnode); + board_node = 1; +#ifdef DEBUG + printf("ADDED BOARD name=%s type=%s model=%s\n", + name, type, model); +#endif + } +#ifdef DEBUG + else + printf("node not added: name=%s type=%s\n", name, type); +#endif + } + + if (curnode = child(id)) { + pnode->child = walk(tree, pnode, curnode); + } + + if (curnode = next(id)) { + if (board_node) { + return (walk(tree, root, curnode)); + } else { + pnode->sibling = walk(tree, root, curnode); + } + } + + if (board_node) { + return (NULL); + } else { + return (pnode); + } +} + +/* + * local functions + */ + +/* + * disp_envc_status + * + * This routine displays the environmental status passed up from + * device drivers via kstats. The kstat names are defined in + * kernel header files included by this module. + * This is a Javelin specific environmental information display routine. + */ +static int +disp_envc_status(struct system_kstat_data *sys_kstats) +{ + struct envctrltwo_kstat_data *ecp; + envctrl_ps2_t ps_ks; + envctrl_fan_t fans_ks; + envctrl_encl_t encl_ks; + envctrl_temp_t temp_ks; + envctrl_disk_t disk_ks; + char state[48]; + uchar_t val, fsp_value; + uchar_t disk_pr, disk_fl; + int i; + int exit_code = 0; + + if (sys_kstats->envctrltwo_kstat_ok == 0) { + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, "Environmental information " + "is not available\n"), 0); + log_printf(dgettext(TEXT_DOMAIN, "Environmental driver may " + "not be installed\n"), 0); + log_printf("\n", 0); + return (1); + } + + ecp = &sys_kstats->envc_data; + + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " Environmental Status "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + + log_printf("System Temperatures (Celsius):\n", 0); + log_printf("------------------------------\n", 0); + + for (i = 0; i < ecp->num_temp_kstats; i++) { + temp_ks = ecp->temp_kstats[i]; + log_printf("%10s %d", temp_ks.label, temp_ks.value); + if (temp_ks.value >= temp_ks.shutdown_threshold) { + log_printf(" CRITICAL\n", 0); + exit_code = 1; + } else if (temp_ks.value >= temp_ks.warning_threshold) { + log_printf(" WARNING\n", 0); + exit_code = 1; + } else if (temp_ks.value < temp_ks.min) { + log_printf(" WARNING\n", 0); + exit_code = 1; + } else + log_printf("\n", 0); + } + + log_printf("\n", 0); + log_printf("=================================\n", 0); + log_printf("\n", 0); + encl_ks = ecp->encl_kstats[0]; + val = encl_ks.value & ENVCTRL_UE250_FSP_KEYMASK; + fsp_value = encl_ks.value; + switch (val) { + case ENVCTRL_UE250_FSP_KEYOFF: + (void) sprintf(state, "%s", "Off"); + break; + case ENVCTRL_UE250_FSP_KEYON: + (void) sprintf(state, "%s", "On"); + break; + case ENVCTRL_UE250_FSP_KEYDIAG: + (void) sprintf(state, "%s", "Diagnostic"); + break; + case ENVCTRL_UE250_FSP_KEYLOCKED: + (void) sprintf(state, "%s", "Secure"); + break; + default: + (void) sprintf(state, "%s", "Broken!"); + exit_code = 1; + break; + } + log_printf("Front Status Panel:\n", 0); + log_printf("-------------------\n", 0); + log_printf("Keyswitch position is in %s mode.\n", state); + log_printf("\n", 0); + val = fsp_value & (ENVCTRL_UE250_FSP_DISK_ERR | + ENVCTRL_UE250_FSP_PS_ERR | ENVCTRL_UE250_FSP_TEMP_ERR | + ENVCTRL_UE250_FSP_GEN_ERR | ENVCTRL_UE250_FSP_ACTIVE); + log_printf("System LED Status: DISK ERROR POWER \n", 0); + log_printf(" [%3s] [ ON] \n", + val & ENVCTRL_UE250_FSP_DISK_ERR ? "ON" : "OFF"); + log_printf(" POWER SUPPLY ERROR ACTIVITY \n", 0); + log_printf(" [%3s] [%3s] \n", + val & ENVCTRL_UE250_FSP_PS_ERR ? "ON" : "OFF", + val & ENVCTRL_UE250_FSP_ACTIVE ? "ON" : "OFF"); + log_printf(" GENERAL ERROR THERMAL ERROR \n", 0); + log_printf(" [%3s] [%3s] \n", + val & ENVCTRL_UE250_FSP_GEN_ERR ? "ON" : "OFF", + val & ENVCTRL_UE250_FSP_TEMP_ERR ? "ON" : "OFF"); + if (val & (ENVCTRL_UE250_FSP_DISK_ERR | ENVCTRL_UE250_FSP_PS_ERR | + ENVCTRL_UE250_FSP_GEN_ERR | ENVCTRL_UE250_FSP_TEMP_ERR)) { + exit_code = 1; + } + + log_printf("\n", 0); + log_printf("=================================\n", 0); + log_printf("\n", 0); + disk_pr = disk_fl = 0; + for (i = 0; i < ecp->num_disk_kstats; i++) { + disk_ks = ecp->disk_kstats[i]; + if (disk_ks.slot == -1) + continue; + disk_pr |= 1 << disk_ks.slot; + if (disk_ks.disk_ok == 0) + disk_fl |= 1 << disk_ks.slot; + } + + log_printf("Disk LED Status: OK = GREEN ERROR = YELLOW\n", 0); + log_printf(" DISK 5: %7s DISK 3: %7s DISK 1: %7s\n", + disk_pr & ENVCTRL_DISK_5 ? + disk_fl & ENVCTRL_DISK_5 ? "[ERROR]" : "[OK]" : "[EMPTY]", + disk_pr & ENVCTRL_DISK_3 ? + disk_fl & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]" : "[EMPTY]", + disk_pr & ENVCTRL_DISK_1 ? + disk_fl & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]" : "[EMPTY]"); + log_printf(" DISK 4: %7s DISK 2: %7s DISK 0: %7s\n", + disk_pr & ENVCTRL_DISK_4 ? + disk_fl & ENVCTRL_DISK_4 ? "[ERROR]" : "[OK]" : "[EMPTY]", + disk_pr & ENVCTRL_DISK_2 ? + disk_fl & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]" : "[EMPTY]", + disk_pr & ENVCTRL_DISK_0 ? + disk_fl & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]" : "[EMPTY]"); + + log_printf("\n", 0); + log_printf("=================================\n", 0); + log_printf("\n", 0); + log_printf("Fan Bank :\n", 0); + log_printf("----------\n", 0); + log_printf("\n", 0); + + fans_ks = ecp->fan_kstats[0]; + log_printf("Bank Speed Status\n", 0); + log_printf(" (0-255) \n", 0); + log_printf("---- ----- ------\n", 0); + if (fans_ks.fans_ok == B_TRUE) + log_printf(" SYS %5d OK\n", fans_ks.fanspeed); + else if (fans_ks.fans_ok != B_TRUE) { + log_printf(" SYS %5d FAILED\n", fans_ks.fanspeed); + exit_code = 1; + } + + log_printf("\n", 0); + log_printf("=================================\n", 0); + log_printf("\n", 0); + log_printf("Power Supplies:\n", 0); + log_printf("---------------\n", 0); + log_printf("\n", 0); + log_printf("Supply Status\n", 0); + log_printf("------ ------\n", 0); + + for (i = 0; i < ecp->num_ps_kstats; i++) { + ps_ks = ecp->ps_kstats[i]; + if (ps_ks.ps_ok == B_TRUE) + (void) sprintf(state, "%s", " OK "); + else if (ps_ks.ps_ok != B_TRUE) { + (void) sprintf(state, "%s", "FAILED: DC " + "Power Failure"); + exit_code = 1; + } + + log_printf(" %2d %s\n", ps_ks.slot, state); + } + + return (exit_code); +} + +void +tazjav_disp_asic_revs(Sys_tree *tree) +{ + Board_node *bnode; + Prom_node *pnode; + char *name; + int *version; + char *model; + + /* Print the header */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(" HW Revisions ", 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + + bnode = tree->bd_list; + + log_printf("ASIC Revisions:\n", 0); + log_printf("---------------\n", 0); + + /* Find sysio and print rev */ + for (pnode = dev_find_node(bnode->nodes, "sbus"); pnode != NULL; + pnode = dev_next_node(pnode, "sbus")) { + version = (int *)get_prop_val(find_prop(pnode, "version#")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) { + log_printf("SBus: %s Rev %d\n", name, *version, 0); + } + } + + /* Find Psycho and print rev */ + for (pnode = dev_find_node(bnode->nodes, "pci"); pnode != NULL; + pnode = dev_next_node(pnode, "pci")) { + Prom_node *parsib = pnode->parent->sibling; + + if (find_prop(pnode, "upa-portid") == NULL) { + if ((parsib != NULL) && + (strcmp(get_prop_val( + find_prop(parsib, "name")), + PCI_NAME) == 0)) + pnode = parsib; + else { + pnode = parsib; + continue; + } + } + + version = (int *)get_prop_val(find_prop(pnode, "version#")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) + if (get_pci_bus(pnode) == 0) + log_printf("STP2223BGA: Rev %d\n", *version, 0); + } + + /* Find Cheerio and print rev */ + for (pnode = dev_find_node(bnode->nodes, "ebus"); pnode != NULL; + pnode = dev_next_node(pnode, "ebus")) { + version = (int *)get_prop_val(find_prop(pnode, "revision-id")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) + log_printf("STP2003QFP: Rev %d\n", *version, 0); + } + + /* Find System Controller and print rev */ + for (pnode = dev_find_node(bnode->nodes, "sc"); pnode != NULL; + pnode = dev_next_node(pnode, "sc")) { + version = (int *)get_prop_val(find_prop(pnode, "version#")); + model = (char *)get_prop_val(find_prop(pnode, "model")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) { + if ((strcmp(model, "SUNW,sc-marvin") == 0)) + log_printf("STP2205BGA: Rev %d\n", *version, 0); + } + } + + /* Find the FEPS and print rev */ + for (pnode = dev_find_node(bnode->nodes, "SUNW,hme"); pnode != NULL; + pnode = dev_next_node(pnode, "SUNW,hme")) { + version = (int *)get_prop_val(find_prop(pnode, "hm-rev")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) { + log_printf("FEPS: %s Rev ", name); + if (*version == 0xa0) { + log_printf("2.0\n", 0); + } else if (*version == 0x20) { + log_printf("2.1\n", 0); + } else { + log_printf("%x\n", *version, 0); + } + } + } + log_printf("\n", 0); + + if (dev_find_node(bnode->nodes, FFB_NAME) != NULL) { + display_ffb(bnode, 0); + } +} + + +/* + * Determine the physical PCI slot based on which Psycho is the parent + * of the PCI card. + */ +static int +tazmo_physical_slot(Prom_node *slotd, Prom_node *parent, int device, char *str) +{ + int *upa_id = NULL; + int *reg = NULL; + int offset; + char controller[MAXSTRLEN]; + char *name; + Prop *prop; + char *devpath_p; + char slotx[16] = ""; + int *slot_names_mask; + char *slot_names; + int shift = 0; + int slot; + int slots, start_slot; + + /* + * If slotd != NULL, then we must return the physical PCI slot + * number based on the information in the slot2dev associations + * node. This routine is called from display_pci() with slotd + * != NULL. If so, we return without obtaining the slot name. + * If slotd == NULL, we look for the slot name through the + * slot-names property in the bus node. + */ + + if (slotd != NULL) { + (void) strcpy(str, ""); + if ((prop = find_prop(parent, "upa-portid")) != NULL) + upa_id = (int *)(get_prop_val(prop)); + if ((prop = find_prop(parent, "reg")) != NULL) + reg = (int *)(get_prop_val(prop)); + if ((prop = find_prop(parent, "name")) != NULL) + name = (char *)(get_prop_val(prop)); + if ((upa_id == NULL) || (reg == NULL)) { + return (-1); + } + offset = reg[1]; + if (strcmp(name, "pci") == 0) { + (void) sprintf(controller, "/pci@%x,%x/*@%x,*", + *upa_id, offset, device); + slots = 20; + } else if (strcmp(name, "SUNW,ffb") == 0) { + (void) sprintf(controller, "/*@%x,0", *upa_id); + slots = 2; + } + + /* + * Javelin and future projects will use 0 based + * numbering for slots. + */ + start_slot = 0; + slots = slots - 1; + for (slot = start_slot; slot <= slots; slot++) { + if (strcmp(name, "pci") == 0) + (void) sprintf(slotx, "pci-slot#%d", slot); + else if (strcmp(name, "SUNW,ffb") == 0) + (void) sprintf(slotx, "graphics#%d", slot); + if ((prop = find_prop(slotd, slotx)) != NULL) + if ((devpath_p = (char *)(get_prop_val + (prop))) != NULL) + if (strcmp(devpath_p, controller) == + NULL) + return (slot); + } + return (-1); + } + + /* + * Get slot-names property from parent node. + * This property consists of a 32 bit mask indicating which + * devices are relevant to this bus node. Following are a + * number of strings depending on how many bits are set in the + * bit mask; the first string gives the label that is printed + * on the chassis for the smallest device number, and so on. + */ + + prop = find_prop(parent, "slot-names"); + if (prop == NULL) { + (void) strcpy(str, ""); + return (-1); + } + slot_names_mask = (int *)(get_prop_val(prop)); + slot_names = (char *)slot_names_mask; + + slot = 1; + slot_names += 4; /* Skip the 4 byte bitmask */ + + while (shift < 32) { + /* + * Shift through the bitmask looking to see if the + * bit corresponding to "device" is set. If so, copy + * the correcsponding string to the provided pointer. + */ + if (*slot_names_mask & slot) { + if (shift == device) { + (void) strcpy(str, slot_names); + return (0); + } + slot_names += strlen(slot_names)+1; + } + shift++; + slot = slot << 1; + } + return (-1); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/littleneck/Makefile b/usr/src/lib/libprtdiag_psr/sparc/littleneck/Makefile new file mode 100644 index 0000000000..2520cbdc32 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/littleneck/Makefile @@ -0,0 +1,84 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/littleneck/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= littleneck.o + +include ../Makefile.com + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../libprtdiag/inc +IFLAGS += -I $(SRC)/cmd/picl/plugins/sun4u/psvc/psvcobj +IFLAGS += -I$(UTSBASE)/sun4u + +LDLIBS += -lpicl + +LINTFLAGS += $(IFLAGS) + +# +# Workgroup Server platform library should install into +# SUNW,Ultra-4. All other desktop platforms can +# link to /usr/platform/SUNW,Ultra-4/lib/libprtdiag_psr.so +# +PLATFORM=SUNW,Sun-Fire-280R + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/littleneck; pwd; $(MAKE) $(USR_PSM_LIB_DIR) + +# +# install rule +# +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +# +# used for message files +# +POFILE= libprtdiag_psr_littleneck.po +POFILES= littleneck.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/*.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po + diff --git a/usr/src/lib/libprtdiag_psr/sparc/littleneck/common/littleneck.c b/usr/src/lib/libprtdiag_psr/sparc/littleneck/common/littleneck.c new file mode 100644 index 0000000000..5eb6c4f1e4 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/littleneck/common/littleneck.c @@ -0,0 +1,1072 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Littleneck Platform specific functions. + * + * called when : + * machine_type == MTYPE_LITTLENECK + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <kstat.h> +#include <string.h> +#include <assert.h> +#include <libintl.h> +#include <note.h> +#include <syslog.h> + +#include <sys/openpromio.h> +#include <sys/sysmacros.h> + +#include <pdevinfo.h> +#include <display.h> +#include <pdevinfo_sun4u.h> +#include <display_sun4u.h> +#include <libprtdiag.h> + +#include <picl.h> +#include "workfile.c" + +#define LNECK_MAX_PS 2 +#define LNECK_MAX_DISKS 2 +#define LNECK_MAX_FANS 1 + +#ifndef SCHIZO_COMPAT_PROP +#define SCHIZO_COMPAT_PROP "pci108e,8001" +#endif + +/* Count of failed PSU's found */ +int ps_failure = 0; + +/* + * Ignore first entry into disp_envc_status() + * from libprtdiag/common/display_sun4u.c + */ +int print_flag = 0; + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (workgroup server systems only) + */ +int error_check(Sys_tree *tree, struct system_kstat_data *kstats); +void display_cpu_devices(Sys_tree *tree); +void display_pci(Board_node *board); +void display_io_cards(struct io_card *list); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void display_ffb(Board_node *board, int table); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); + +/* local functions */ +static int disp_envc_status(void); +static int lneck_env_print_temps(picl_nodehdl_t); +static int lneck_env_print_keyswitch(picl_nodehdl_t); +static int lneck_env_print_FSP_LEDS(picl_nodehdl_t); +static int lneck_env_print_disk(picl_nodehdl_t); +static int lneck_env_print_fans(picl_nodehdl_t); +static int lneck_env_print_ps(picl_nodehdl_t); + +static void lneck_display_hw_revisions(Prom_node *root, + Board_node *bnode); +static void display_schizo_revisions(Board_node *bdlist); + +/* + * Defining the error_check function in order to return the + * appropriate error code. + */ +/*ARGSUSED0*/ +int +error_check(Sys_tree *tree, struct system_kstat_data *kstats) +{ + int exit_code = 0; /* init to all OK */ + /* silently check for any types of machine errors */ + print_flag = 0; + if (disp_fail_parts(tree) || disp_envc_status()) + /* set exit_code to show failures */ + exit_code = 1; + + print_flag = 1; + + return (exit_code); +} + +void +display_cpu_devices(Sys_tree *tree) +{ + Board_node *bnode; + + /* + * Display the table header for CPUs . Then display the CPU + * frequency, cache size, and processor revision of all cpus. + */ + log_printf(dgettext(TEXT_DOMAIN, + "\n" + "========================= CPUs " + "===============================================" + "\n" + "\n" + " Run E$ CPU CPU \n" + "Brd CPU MHz MB Impl. Mask \n" + "--- --- ---- ---- ------- ---- \n")); + + /* Now display all of the cpus on each board */ + bnode = tree->bd_list; + while (bnode != NULL) { + display_cpus(bnode); + bnode = bnode->next; + } + + log_printf("\n"); +} + + +/* + * Display the CPUs present on this board. + */ +void +display_cpus(Board_node *board) +{ + Prom_node *cpu; + char cpu_name[] = "cpu"; + + /* + * display the CPUs' operating frequency, cache size, impl. field + * and mask revision. + */ + + for (cpu = dev_find_type(board->nodes, cpu_name); cpu != NULL; + cpu = dev_next_type(cpu, cpu_name)) { + int freq; /* CPU clock frequency */ + int ecache_size; /* External cache size */ + int *mid; + int *impl; + int *mask; + + mid = (int *)get_prop_val(find_prop(cpu, "portid")); + freq = LNECK_CLK_FREQ_TO_MHZ(get_cpu_freq(cpu)); + ecache_size = get_ecache_size(cpu); + impl = (int *)get_prop_val(find_prop(cpu, "implementation#")); + mask = (int *)get_prop_val(find_prop(cpu, "mask#")); + + /* Do not display a failed CPU node */ + if ((freq != 0) && (node_failed(cpu) == 0)) { + /* Board number */ + switch (*mid) { + case 1: + log_printf(dgettext(TEXT_DOMAIN, + " B ")); + break; + case 0: + log_printf(dgettext(TEXT_DOMAIN, + " A ")); + break; + default: + log_printf(dgettext(TEXT_DOMAIN, "X ")); + } + + /* CPU MID */ + log_printf("%2d ", *mid); + + /* Module number */ + + /* Running frequency */ + log_printf("%4d ", freq); + + /* Ecache size */ + if (ecache_size == 0) + log_printf("N/A "); + else + log_printf("%4.1f ", + (float)ecache_size / (float)(1<<20)); + + /* Implementation */ + if (impl == NULL) { + log_printf(dgettext(TEXT_DOMAIN, "%6s "), + " N/A"); + } else { + if (IS_CHEETAH(*impl)) + log_printf("%-7s ", "US-III", 0); + else if (IS_CHEETAH_PLUS(*impl)) + log_printf("%-7s ", "US-III+", 0); + else + log_printf("%-7x ", *impl, 0); + } + + /* CPU Mask */ + if (mask == NULL) { + log_printf(dgettext(TEXT_DOMAIN, " N/A ")); + } else { + log_printf(dgettext(TEXT_DOMAIN, " %d.%d "), + (*mask >> 4) & 0xf, *mask & 0xf); + } + + log_printf("\n"); + } + } +} + +/*ARGSUSED0*/ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + Board_node *bnode = tree->bd_list; + + log_printf(dgettext(TEXT_DOMAIN, + "========================= Memory Configuration" + " ===============================\n" + "\n Logical Logical Logical " + "\n MC Bank Bank Bank DIMM " + "Interleave Interleaved" + "\n Brd ID num size Status Size " + "Factor with" + "\n---- --- ---- ------ ----------- ------ " + "---------- -----------")); + + while (bnode != NULL) { + if (get_us3_mem_regs(bnode)) { + log_printf(dgettext(TEXT_DOMAIN, + "\nFailed to get memory information.\n")); + return; + } + bnode = bnode->next; + } + + /* Display what we have found */ + display_us3_banks(); +} + +/*ARGSUSED2*/ +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + /* + * Now display the last powerfail time and the fatal hardware + * reset information. We do this under a couple of conditions. + * First if the user asks for it. The second is iof the user + * told us to do logging, and we found a system failure. + */ + + if (flag) { + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + (void) disp_envc_status(); + + /* Hardware revision function calls */ + lneck_display_hw_revisions(root, tree->bd_list); + log_printf("\n"); + } + return; + +} + +/* + * display_pci + * Display all the PCI IO cards on this board. + */ +void +display_pci(Board_node *board) +{ + struct io_card *card_list = NULL; + struct io_card card; + void *value; + Prom_node *pci; + Prom_node *card_node; + + char *slot_name_arr[LNECK_MAX_SLOTS_PER_IO_BD] = {NULL}; + int i; + + if (board == NULL) + return; + + memset(&card, 0, sizeof (struct io_card)); + /* Initialize all the common information */ + card.display = TRUE; + card.board = board->board_num; + + /* + * Search for each pci instance, then find/display all nodes under + * each instance node found. + */ + for (pci = dev_find_node_by_compat(board->nodes, SCHIZO_COMPAT_PROP); + pci != NULL; + pci = dev_next_node_by_compat(pci, SCHIZO_COMPAT_PROP)) { + (void) snprintf(card.bus_type, MAXSTRLEN, + dgettext(TEXT_DOMAIN, "PCI")); + /* + * Get slot-name properties from parent node and + * store them in an array. + */ + value = (char *)get_prop_val( + find_prop(pci, "slot-names")); + + if (value != NULL) { + /* array starts after first int */ + slot_name_arr[0] = (char *)value + sizeof (int); + for (i = 1; i < LNECK_MAX_SLOTS_PER_IO_BD; i++) { + slot_name_arr[i] = (char *)slot_name_arr[i - 1] + + strlen(slot_name_arr[i - 1]) +1; + } + } + /* + * Search for Children of this node ie. Cards. + * Note: any of these cards can be a pci-bridge + * that itself has children. If we find a + * pci-bridge we need to handle it specially. + */ + card_node = pci->child; + /* Generate the list of pci cards on pci instance: pci */ + fill_pci_card_list(pci, card_node, &card, &card_list, + slot_name_arr); + } /* end-for */ + + display_io_cards(card_list); + free_io_cards(card_list); + log_printf("\n"); +} + +/* + * Print out all the io cards in the list. Also print the column + * headers if told to do so. + */ +void +display_io_cards(struct io_card *list) +{ + static int banner = 0; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) { + return; + } + + if (banner == FALSE) { + log_printf(dgettext(TEXT_DOMAIN, + " Bus Max\n" + " IO Port Bus Freq Bus Dev,\n" + "Brd Type ID Side Slot MHz Freq Func State " + "Name " +#ifdef DEBUG + "Model Notes\n")); +#else + "Model\n")); +#endif + /* ---------Node Brd IO Port Bus Slot Bus Max Dev Stat */ + log_printf(dgettext(TEXT_DOMAIN, + "---- ---- ---- ---- ---- ---- ---- ---- ----- " + "-------------------------------- " +#ifdef DEBUG + "---------------------- " +#endif + "----------------------\n")); + banner = TRUE; + } + + for (p = list; p != NULL; p = p -> next) { + log_printf(dgettext(TEXT_DOMAIN, "I/O ")); + log_printf(dgettext(TEXT_DOMAIN, "%-4s "), p->bus_type); + log_printf(dgettext(TEXT_DOMAIN, "%-3d "), + p->schizo_portid); + log_printf(dgettext(TEXT_DOMAIN, "%c "), p->pci_bus); + log_printf(dgettext(TEXT_DOMAIN, "%-1s "), p->slot_str); + log_printf(dgettext(TEXT_DOMAIN, "%-3d "), p->freq); + switch (p->pci_bus) { + case 'A': + log_printf(dgettext(TEXT_DOMAIN, " 66 ")); + break; + case 'B': + log_printf(dgettext(TEXT_DOMAIN, " 33 ")); + break; + default: + log_printf(dgettext(TEXT_DOMAIN, " - ")); + break; + } + + log_printf(dgettext(TEXT_DOMAIN, "%-1d,%-1d "), + p->dev_no, p->func_no); + log_printf(dgettext(TEXT_DOMAIN, "%-5s "), p->status); + log_printf(dgettext(TEXT_DOMAIN, "%-32.32s"), p->name); + if (strlen(p->name) > 32) + log_printf(dgettext(TEXT_DOMAIN, "+ ")); + else + log_printf(dgettext(TEXT_DOMAIN, " ")); + log_printf(dgettext(TEXT_DOMAIN, "%-22.22s"), p->model); + if (strlen(p->model) > 22) + log_printf(dgettext(TEXT_DOMAIN, "+")); +#ifdef DEBUG + log_printf("%s ", p->notes); +#endif + log_printf("\n"); + } +} + +/* + * display_ffb + * + * There are no FFB's on a Littleneck, however in the generic library, + * the display_ffb() function is implemented so we have to define an + * empty function here. + */ +/*ARGSUSED0*/ +void +display_ffb(Board_node *board, int table) +{} + + +/* + * local functions + */ + +/* + * disp_fail_parts + * + * Display the failed parts in the system. This function looks for + * the status property in all PROM nodes. On systems where + * the PROM does not support passing diagnostic information + * through the device tree, this routine will be silent. + */ +int +disp_fail_parts(Sys_tree *tree) +{ + int exit_code = 0; + int system_failed = 0; + Board_node *bnode = tree->bd_list; + Prom_node *pnode; + + /* go through all of the boards looking for failed units. */ + while (bnode != NULL) { + /* find failed chips */ + pnode = find_failed_node(bnode->nodes); + if ((pnode != NULL) && !system_failed) { + system_failed = 1; + exit_code = 1; + if (print_flag == 0) { + return (exit_code); + } + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, "Failed Field " + "Replaceable Units (FRU) in System:\n")); + log_printf("==========================" + "====================\n"); + } + while (pnode != NULL) { + void *value; + char *name; /* node name string */ + char *type; /* node type string */ + char *board_type = NULL; + + value = get_prop_val(find_prop(pnode, "status")); + name = get_node_name(pnode); + + /* sanity check of data retrieved from PROM */ + if ((value == NULL) || (name == NULL)) { + pnode = next_failed_node(pnode); + continue; + } + + /* Find the board type of this board */ + if (bnode->board_type == CPU_BOARD) { + board_type = "CPU"; + } else { + board_type = "IO"; + } + + log_printf(dgettext(TEXT_DOMAIN, "%s unavailable " + "on %s Board #%d\n"), name, board_type, + bnode->board_num); + + log_printf(dgettext(TEXT_DOMAIN, + "\tPROM fault string: %s\n"), value); + + log_printf(dgettext(TEXT_DOMAIN, + "\tFailed Field Replaceable Unit is ")); + + /* + * Determine whether FRU is CPU module, system + * board, or SBus card. + */ + if ((name != NULL) && (strstr(name, "sbus"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "SBus Card %d\n"), + get_sbus_slot(pnode)); + + } else if (((name = get_node_name(pnode->parent)) != + NULL) && (strstr(name, "pci"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "PCI Card %d"), + get_pci_device(pnode)); + + } else if (((type = get_node_type(pnode)) != NULL) && + (strstr(type, "cpu"))) { + + log_printf(dgettext(TEXT_DOMAIN, "UltraSPARC " + "module Board %d Module %d\n"), 0, + get_id(pnode)); + + } else { + log_printf(dgettext(TEXT_DOMAIN, + "%s board %d\n"), board_type, + bnode->board_num); + } + pnode = next_failed_node(pnode); + } + bnode = bnode->next; + } + + if (!system_failed) { + log_printf(dgettext(TEXT_DOMAIN, + "No failures found in System\n")); + log_printf("===========================\n\n"); + return (0); + } else { + return (1); + } +} + + +/* + * disp_envc_status + * + * This routine displays the environmental status passed up from + * device drivers via the envlibobj.so library. + * This is a Littleneck specific environmental information display routine. + */ +static int +disp_envc_status(void) +{ + int err; + char *system = "SYSTEM"; + picl_nodehdl_t system_node, root; + + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, "=========================" + " Environmental Status =========================\n\n")); + + err = picl_initialize(); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "Cannot print environmental information\n" + "picl_initialize failed\n" + "%s\n"), picl_strerror(err)); + } + + if (err == PICL_SUCCESS) { + err = picl_get_root(&root); + err = find_child_device(root, system, &system_node); + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "Cannot print environmental information\n" + "find_child_device for the SYSTEM node " + "failed\n" + "%s\n"), picl_strerror(err)); + } + + if ((err = lneck_env_print_temps(system_node)) != + PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "Temperature Checking failed: %s\n"), + picl_strerror(err)); + } + if ((err = lneck_env_print_keyswitch(system_node)) != + PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "Keyswitch information checking failed: %s\n"), + picl_strerror(err)); + } + if ((err = lneck_env_print_FSP_LEDS(system_node)) != + PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "FSP LED information checking failed: %s\n"), + picl_strerror(err)); + } + if ((err = lneck_env_print_disk(system_node)) != + PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "Disk information checking failed: %s\n"), + picl_strerror(err)); + } + if ((err = lneck_env_print_fans(system_node)) != + PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "Fan information checking failed: %s\n"), + picl_strerror(err)); + } + if ((err = lneck_env_print_ps(system_node)) != + PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "Power Supply information checking failed: " + "%s\n"), picl_strerror(err)); + } else if (ps_failure != 0) + err = PICL_FAILURE; + } + return (err); +} + +int +lneck_env_print_ps(picl_nodehdl_t system_node) +{ + int i, err = 0; + int32_t number; + picl_nodehdl_t *ps; + picl_nodehdl_t ps_fail[2], ps_type[2]; + char name[PICL_PROPNAMELEN_MAX]; + boolean_t type; + char fault_state[PICL_PROPNAMELEN_MAX]; + + log_printf(dgettext(TEXT_DOMAIN, + "Power Supplies:\n" + "---------------\n" + "Supply Status PS Type\n" + "------ ------ ---------------\n")); + err = fill_device_array_from_id(system_node, "PSVC_PS", &number, + &ps); + if (err != PICL_SUCCESS) { + return (err); + } + + for (i = 0; i < LNECK_MAX_PS; i++) { + err = picl_get_propval_by_name(ps[i], PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + if (err == PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, "%6-s"), name); + } else continue; + + err = picl_get_propval_by_name(ps[i], "FaultInformation", + fault_state, PICL_PROPNAMELEN_MAX); + if (err == PICL_SUCCESS) { + if ((strlen(fault_state) == 0) || + (strcmp(fault_state, "NO_FAULT") == 0)) { + strcpy(fault_state, "OK"); + } else + /* + * Bump up count if fault_state !OK + */ + ps_failure++; + + log_printf(dgettext(TEXT_DOMAIN, " [%-6s] "), + fault_state); + } else { + return (err); + } + + err = fill_device_from_id(ps[i], "PSVC_DEV_FAULT_SENSOR", + &ps_fail[i]); + if (err != PICL_SUCCESS) { + return (err); + } + + err = fill_device_from_id(ps[i], "PSVC_DEV_TYPE_SENSOR", + &ps_type[i]); + if (err != PICL_SUCCESS) { + return (err); + } + err = picl_get_propval_by_name(ps_type[i], "Gpio-value", &type, + sizeof (boolean_t)); + if (err == PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, " [%13s]"), + type == 0 ? "Quahog/Razor" : "Sun-Fire-280R"); + if (type == 0) { + log_printf(dgettext(TEXT_DOMAIN, + "WARNING: PS is of the wrong type\n")); + } else log_printf("\n"); + } else { + return (err); + } + + } + + log_printf(dgettext(TEXT_DOMAIN, + "\n" + "=================================" + "\n" + "\n")); + + /* + * Do not display an error message just because PS1 is + * not present. + */ + if (err == PICL_INVALIDHANDLE) { + err = PICL_SUCCESS; + } + + return (err); +} + +int +lneck_env_print_fans(picl_nodehdl_t system_node) { + int i, err = 0; + int32_t number; + picl_nodehdl_t *fans; + picl_nodehdl_t fan_fault[1]; + char fault_state[PICL_PROPNAMELEN_MAX]; + char name[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_FAN", &number, + &fans); + if (err != PICL_SUCCESS) { + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "\n" + "=================================\n" + "\n" + "Fan Bank :\n" + "----------\n" + "\n" + "Bank Status\n" + "---- -------\n")); + + for (i = 0; i < LNECK_MAX_FANS; i++) { + err = picl_get_propval_by_name(fans[i], PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + if (err == PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, "%16-s"), name); + } else continue; + + err = fill_device_from_id(fans[i], "PSVC_DEV_FAULT_SENSOR", + &fan_fault[i]); + if (err != PICL_SUCCESS) { + return (err); + } + + err = picl_get_propval_by_name(fans[i], "FaultInformation", + &fault_state, PICL_PROPNAMELEN_MAX); + + if (err == PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, " [%3s]\n"), + fault_state); + } else { + return (err); + } + } + log_printf(dgettext(TEXT_DOMAIN, + "\n" + "=================================" + "\n" + "\n")); + + return (err); +} + +int +lneck_env_print_disk(picl_nodehdl_t system_node) { + int i, err = 0; + int32_t number; + picl_nodehdl_t *disks; + char fault_state[PICL_PROPNAMELEN_MAX]; + char name[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_DISK", &number, + &disks); + if (err != PICL_SUCCESS) { + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "Disk Status:\n" + " Presence Fault Value\n" + " -------- -----------\n")); + + for (i = 0; i < LNECK_MAX_DISKS; i++) { + err = picl_get_propval_by_name(disks[i], PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + switch (err) { + case PICL_SUCCESS: + log_printf(dgettext(TEXT_DOMAIN, + "DISK %2d: [PRESENT]"), i); + break; + case PICL_INVALIDHANDLE: + log_printf(dgettext(TEXT_DOMAIN, + "DISK %2d: [EMPTY ]\n"), i); + continue; + default: + return (err); + } + err = picl_get_propval_by_name(disks[i], "FaultInformation", + &fault_state, PICL_PROPNAMELEN_MAX); + if (err == PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, " [%3s]"), + fault_state); + } else { + if (err != PICL_INVALIDHANDLE) + return (err); + } + log_printf("\n"); + } + + if (err == PICL_INVALIDHANDLE) { + err = PICL_SUCCESS; + } + + return (err); +} + +int +lneck_env_print_FSP_LEDS(picl_nodehdl_t system_node) { + int err; + int32_t number; + picl_nodehdl_t *fsp_led; + char fault_state[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_FSP_LED", &number, + &fsp_led); + if (err != PICL_SUCCESS) { + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "System LED Status: POWER GEN FAULT\n" + " [ ON]")); + err = picl_get_propval_by_name(fsp_led[0], "State", &fault_state, + PICL_PROPNAMELEN_MAX); + if (err == PICL_SUCCESS) { + log_printf(" [%3s]", fault_state); + } else { + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "\n" + "\n" + "=================================" + "\n" + "\n")); + + return (err); +} + +int +lneck_env_print_keyswitch(picl_nodehdl_t system_node) { + int err = 0; + picl_nodehdl_t *keyswitch; + int32_t number; + char ks_pos[PICL_PROPNAMELEN_MAX]; + + err = fill_device_array_from_id(system_node, "PSVC_KEYSWITCH", &number, + &keyswitch); + if (err != PICL_SUCCESS) { + return (err); + } + err = picl_get_propval_by_name(keyswitch[0], "State", ks_pos, + PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + return (err); + } + + log_printf(dgettext(TEXT_DOMAIN, + "Front Status Panel:\n" + "-------------------\n" + "Keyswitch position: %s\n"), ks_pos); + log_printf("\n"); + + return (err); +} + +int +lneck_env_print_temps(picl_nodehdl_t system_node) { + int i, err = 0; + picl_nodehdl_t *system_ts_nodes; + int32_t temp, number; + + err = fill_device_array_from_id(system_node, "PSVC_TS", &number, + &system_ts_nodes); + if (err != PICL_SUCCESS) { + return (err); + } + + + log_printf(dgettext(TEXT_DOMAIN, + "System Temperatures (Celsius):\n" + "------------------------------\n" + "cpu0 1 \n" + "---------\n")); + + for (i = 0; i < 2; i++) { + err = picl_get_propval_by_name(system_ts_nodes[i], + "Temperature", &temp, sizeof (temp)); + if (err == PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, " %02d"), temp); + } else { + if (err == PICL_INVALIDHANDLE) { + err = PICL_SUCCESS; + log_printf(dgettext(TEXT_DOMAIN, " xx")); + } else { + return (err); + } + } + } + + log_printf("\n"); + log_printf("\n"); + log_printf(dgettext(TEXT_DOMAIN, + "=================================\n")); + log_printf("\n"); + + return (err); +} + +static void +lneck_display_hw_revisions(Prom_node *root, Board_node *bdlist) +{ + Prom_node *pnode; + char *value; + + log_printf(dgettext(TEXT_DOMAIN, "\n" + "========================= HW Revisions " + "=======================================\n\n")); + + log_printf(dgettext(TEXT_DOMAIN, + "System PROM revisions:\n" + "----------------------\n")); + + pnode = dev_find_node(root, "openprom"); + if (pnode != NULL) { + value = (char *)get_prop_val(find_prop(pnode, "version")); + log_printf(value); + } + + log_printf(dgettext(TEXT_DOMAIN, "\n\n" + "IO ASIC revisions:\n" + "------------------\n" + " Port\n" + "Model ID Status Version\n" + "-------- ---- ------ -------\n")); + + display_schizo_revisions(bdlist); +} + +static void +display_schizo_revisions(Board_node *bdlist) +{ + Prom_node *pnode; + int *int_val; + int portid; + int prev_portid = -1; + char *status_a = NULL; + char *status_b = NULL; + int revision; +#ifdef DEBUG + uint32_t a_notes, b_notes; +#endif + int pci_bus; + Board_node *bnode; + bnode = bdlist; + + while (bnode != NULL) { + /* + * search this board node for all Schizos + */ + + for (pnode = dev_find_node_by_compat(bnode->nodes, + SCHIZO_COMPAT_PROP); pnode != NULL; + pnode = dev_next_node_by_compat(pnode, + SCHIZO_COMPAT_PROP)) { + + /* + * get the reg property to determine + * whether we are looking at side A or B + */ + + int_val = (int *)get_prop_val + (find_prop(pnode, "reg")); + if (int_val != NULL) { + int_val ++; /* second integer in array */ + pci_bus = ((*int_val) & 0x7f0000); + } + + /* get portid */ + int_val = (int *)get_prop_val + (find_prop(pnode, "portid")); + if (int_val == NULL) + continue; + + portid = *int_val; + + /* + * If this is a new portid and it is PCI bus B, + * we skip onto the PCI bus A. + */ + if ((portid != prev_portid) && (pci_bus == 0x700000)) { + prev_portid = portid; + /* status */ + status_b = (char *)get_prop_val + (find_prop(pnode, "status")); +#ifdef DEBUG + b_notes = pci_bus; +#endif + continue; /* skip to the next schizo */ + } + + /* + * This must be side A of the same Schizo. + * Gather all its props and display them. + */ +#ifdef DEBUG + a_notes = pci_bus; +#endif + + prev_portid = portid; + + int_val = (int *)get_prop_val + (find_prop(pnode, "version#")); + if (int_val != NULL) + revision = *int_val; + else + revision = -1; + + status_a = (char *)get_prop_val(find_prop + (pnode, "status")); + + log_printf(dgettext(TEXT_DOMAIN, "Schizo ")); + + log_printf(dgettext(TEXT_DOMAIN, "%-3d "), portid, 0); + + + log_printf((status_a == NULL && status_b == NULL) ? + dgettext(TEXT_DOMAIN, " ok ") : + dgettext(TEXT_DOMAIN, " fail ")); + + log_printf(dgettext(TEXT_DOMAIN, " %4d "), + revision); +#ifdef DEBUG + log_printf(" 0x%x 0x%x", a_notes, b_notes); +#endif + log_printf("\n"); + } + bnode = bnode->next; + } +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/littleneck/common/workfile.c b/usr/src/lib/libprtdiag_psr/sparc/littleneck/common/workfile.c new file mode 100644 index 0000000000..c27d5ce5c2 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/littleneck/common/workfile.c @@ -0,0 +1,955 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2000, 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Littleneck Platform specific functions. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libprtdiag.h> +#include <sys/mc.h> + + +static Prom_node *dev_next_node_by_compat(Prom_node *root, char *model); +static Prom_node *dev_find_node_by_compat(Prom_node *root, char *model); + +static Board_node *littleneck_insert_board(Sys_tree *root, int board); +static Board_node *littleneck_find_board(Sys_tree *root, int board); + +void print_us3_memory_line(int portid, + int bank_id, + uint64_t bank_size, + char *bank_status, + uint64_t dimm_size, + uint32_t intlv, + int seg_id); + +void add_node(Sys_tree *root, Prom_node *pnode); +int do_prominfo(int syserrlog, + char *pgname, + int log_flag, + int prt_flag); + +void *get_prop_val(Prop *prop); +Prop *find_prop(Prom_node *pnode, char *name); +char *get_node_name(Prom_node *pnode); +char *get_node_type(Prom_node *pnode); + +void fill_pci_card_list(Prom_node *pci_instance, + Prom_node *pci_card_node, + struct io_card *pci_card, + struct io_card **pci_card_list, + char **pci_slot_name_arr); + +static Prom_node *next_pci_card(Prom_node *curr_card, int *is_bridge, + int is_pcidev, Prom_node *curr_bridge, + Prom_node * parent_bridge, Prom_node *pci); + + +#define LNECK_MAX_SLOTS_PER_IO_BD 9 +#define LNECK_CLK_FREQ_TO_MHZ(x) (((x) + 500000) / 1000000) + +/* This is stuff to get the HW Revisions stuff to work */ + +#define LNECK_SAFARI_ID_MASK 0x1F /* 5 bits */ +#define LNECK_NODE_MASK 0x1F /* 5 bits */ +#define LNECK_PORTID_NODE_SHIFT 5 +#define LNECK_MIN_CPU_SAFARI_ID 0 /* 0x00 */ +#define LNECK_MAX_CPU_SAFARI_ID 23 /* 0x17 */ +#define LNECK_MIN_IO_SAFARI_ID 24 /* 0x18 */ +#define LNECK_MAX_IO_SAFARI_ID 31 /* 0x1F */ +#define NUM_MBANKS_PER_MC 4 + +/* + * LNECK_PORTID_TO_SAFARI_ID + * + * Calculates the Safari Agent ID from the portid. + */ +#define LNECK_PORTID_TO_SAFARI_ID(portid) ((portid) & LNECK_SAFARI_ID_MASK) + +/* + * LNECK_PORTID_IS_CPU_TYPE + * + * If the portid associated with a CPU board is passed in, TRUE is returned, + * otherwise FALSE. + */ +#define LNECK_PORTID_IS_CPU_TYPE(portid) \ + (((((portid) & LNECK_SAFARI_ID_MASK) >= LNECK_MIN_CPU_SAFARI_ID) && \ + (((portid) & LNECK_SAFARI_ID_MASK) <= LNECK_MAX_CPU_SAFARI_ID)) ? \ + TRUE: FALSE) + +/* + * LNECK_PORTID_TO_NODEID + * + * Calculates the SSM NodeID from the portid + */ +#define LNECK_PORTID_TO_NODEID(portid) (((portid) >> LNECK_PORTID_NODE_SHIFT) \ + & LNECK_NODE_MASK) + +/* + * LNECK_CPU_BD_PORTID_TO_BD_NUM + * + * If the portid associated with a CPU board is passed in, the board number + * associated with this portid is returned, otherwise -1. + */ +#define LNECK_CPU_BD_PORTID_TO_BD_NUM(portid) \ + ((LNECK_PORTID_IS_CPU_TYPE(portid)) ? \ + (((portid) & LNECK_SAFARI_ID_MASK) / 4) : (-1)) + +/* + * LNECK_PORTID_IS_IO_TYPE + * + * If the portid associated with an IO board is passed in, TRUE is returned, + * otherwise FALSE. + */ +#define LNECK_PORTID_IS_IO_TYPE(portid) \ + (((((portid) & LNECK_SAFARI_ID_MASK) >= LNECK_MIN_IO_SAFARI_ID) && \ + (((portid) & LNECK_SAFARI_ID_MASK) <= LNECK_MAX_IO_SAFARI_ID)) ? \ + TRUE: FALSE) + +/* + * LNECK_IO_BD_PORTID_TO_BD_NUM + * + * If the portid associated with an IO board is passed in, the board number + * associated with this portid is returned, otherwise -1. + */ +#define LNECK_IO_BD_PORTID_TO_BD_NUM(portid) \ + (LNECK_PORTID_IS_IO_TYPE(portid) ? \ + (((((portid) & LNECK_SAFARI_ID_MASK) - 24) / 2) + 6) : (-1)) + +/* + * LNECK_PORTID_TO_BOARD_NUM + * + * If a valid portid is passed in, this macro returns the board number + * associated with it, otherwise it returns -1. + */ +#define LNECK_PORTID_TO_BOARD_NUM(portid) \ + ((LNECK_PORTID_IS_CPU_TYPE(portid)) ? \ + (LNECK_CPU_BD_PORTID_TO_BD_NUM(portid)) : \ + ((LNECK_PORTID_IS_IO_TYPE(portid)) ? \ + LNECK_IO_BD_PORTID_TO_BD_NUM(portid) : (-1))) + + +/* + * Start from the current node and return the next node besides + * the current one which has the requested model property. + */ +static Prom_node * +dev_next_node_by_compat(Prom_node *root, char *compat) +{ + Prom_node *node; + + if (root == NULL) + return (NULL); + + /* look at your children first */ + if ((node = dev_find_node_by_compat(root->child, compat)) != NULL) + return (node); + + /* now look at your siblings */ + if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL) + return (node); + + return (NULL); /* not found */ +} + +/* + * Do a depth-first walk of a device tree and + * return the first node with the matching model. + */ +static Prom_node * +dev_find_node_by_compat(Prom_node *root, char *compat) +{ + Prom_node *node; + char *compatible; + char *name; + + if (root == NULL) + return (NULL); + + if (compat == NULL) + return (NULL); + + name = get_node_name(root); + if (name == NULL) + name = ""; + + compatible = (char *)get_prop_val(find_prop(root, "compatible")); + + if (compatible == NULL) + return (NULL); + + if ((strcmp(name, "pci") == 0) && (compatible != NULL) && + (strcmp(compatible, compat) == 0)) { + return (root); /* found a match */ + } + + /* look at your children first */ + if ((node = dev_find_node_by_compat(root->child, compat)) != NULL) + return (node); + + /* now look at your siblings */ + if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL) + return (node); + + return (NULL); /* not found */ +} + +int32_t +find_child_device(picl_nodehdl_t parent, char *child_name, + picl_nodehdl_t *child) +{ + int32_t err; + char name[PICL_PROPNAMELEN_MAX]; + + err = picl_get_propval_by_name(parent, PICL_PROP_CHILD, &(*child), + sizeof (picl_nodehdl_t)); + switch (err) { + case PICL_SUCCESS: + break; + case PICL_PROPNOTFOUND: + err = PICL_INVALIDHANDLE; + return (err); + default: +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "Failed picl_get_propval_by_name with %s\n"), + picl_strerror(err)); +#endif + return (err); + } + + err = picl_get_propval_by_name(*child, PICL_PROP_NAME, name, + PICL_PROPNAMELEN_MAX); + +#ifdef WORKFILE_DEBUG + if (err != PICL_SUCCESS) { + log_printf(dgettext(TEXT_DOMAIN, + "failed the get name for root\n")); + log_printf(dgettext(TEXT_DOMAIN, "%s\n"), picl_strerror(err)); + } +#endif + + if (strcmp(name, child_name) == 0) + return (err); + + while (err != PICL_PROPNOTFOUND) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, "child name is %s\n"), name); +#endif + err = picl_get_propval_by_name(*child, PICL_PROP_PEER, + &(*child), sizeof (picl_nodehdl_t)); + switch (err) { + case PICL_SUCCESS: + err = picl_get_propval_by_name(*child, PICL_PROP_NAME, + name, PICL_PROPNAMELEN_MAX); + if (strcmp(name, child_name) == 0) + return (err); + break; + case PICL_PROPNOTFOUND: + break; + default: +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "Failed picl_get_propval_by_name with %s\n"), + picl_strerror(err)); +#endif + return (err); + } + } + err = PICL_INVALIDHANDLE; + return (err); +} + +int32_t +fill_device_from_id(picl_nodehdl_t device_id, char *assoc_id, + picl_nodehdl_t *device) +{ + int32_t err; + picl_prophdl_t tbl_hdl; + picl_prophdl_t reference_property; + + err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl, + sizeof (picl_prophdl_t)); + if (err != PICL_SUCCESS) { +#ifdef WORKFILE_DEBUG + if (err != PICL_INVALIDHANDLE) { + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_from_id failure in " + "picl_get_propval_by_name err is %s\n"), + picl_strerror(err)); + } +#endif + return (err); + } + + err = picl_get_next_by_row(tbl_hdl, &reference_property); + if (err != PICL_SUCCESS) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_from_id failure in picl_get_next_by_row" + " err is %s\n"), picl_strerror(err)); +#endif + return (err); + } + + /* get node associated with reference property */ + err = picl_get_propval(reference_property, &(*device), + sizeof (picl_nodehdl_t)); + +#ifdef WORKFILE_DEBUG + if (err != 0) { + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_from_id failure in picl_get_propval" + " err is %s\n"), picl_strerror(err)); + } +#endif + + return (err); +} + +int32_t +fill_device_array_from_id(picl_nodehdl_t device_id, char *assoc_id, + int32_t *number_of_devices, picl_nodehdl_t *device_array[]) +{ + int32_t err; + int i; + picl_prophdl_t tbl_hdl; + picl_prophdl_t entry; + int devs = 0; + + err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl, + sizeof (picl_prophdl_t)); + if ((err != PICL_SUCCESS) && (err != PICL_INVALIDHANDLE)) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure in " + "picl_get_propval_by_name err is %s\n"), picl_strerror(err)); +#endif + return (err); + } + + entry = tbl_hdl; + while (picl_get_next_by_row(entry, &entry) == 0) + ++devs; + + *device_array = calloc((devs), sizeof (picl_nodehdl_t)); + if (*device_array == NULL) { + +#ifdef WORFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure getting memory" + " for array\n")); +#endif + return (PICL_FAILURE); + } + + entry = tbl_hdl; + for (i = 0; i < devs; i++) { + err = picl_get_next_by_row(entry, &entry); + if (err != 0) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure in " + "picl_get_next_by_row err is %s\n"), + picl_strerror(err)); +#endif + return (err); + } + + /* get node associated with reference property */ + err = picl_get_propval(entry, &((*device_array)[i]), + sizeof (picl_nodehdl_t)); + if (err != 0) { +#ifdef WORKFILE_DEBUG + log_printf(dgettext(TEXT_DOMAIN, + "fill_device_array_from_id failure in " + "picl_get_propval err is %s\n"), picl_strerror(err)); +#endif + + return (err); + } + } + *number_of_devices = devs; + return (err); +} + +/* + * Add a board to the system list in order (sorted by board#). + * Initialize all pointer fields to NULL. + */ +static Board_node * +littleneck_insert_board(Sys_tree *root, int board) +{ + Board_node *bnode; + Board_node *temp = root->bd_list; + + if ((bnode = (Board_node *) malloc(sizeof (Board_node))) == NULL) { + perror("malloc"); + exit(1); + } + + bnode->nodes = NULL; + bnode->next = NULL; + bnode->board_num = board; + + bnode->board_type = UNKNOWN_BOARD; + + if (temp == NULL) + root->bd_list = bnode; + + else if (temp->board_num > board) { + bnode->next = temp; + root->bd_list = bnode; + + } else { + while ((temp->next != NULL) && (board > temp->next->board_num)) + temp = temp->next; + + bnode->next = temp->next; + temp->next = bnode; + } + root->board_cnt++; + + return (bnode); +} + +/* + * Find the requested board struct in the system device tree. + * + * This function overrides the functionality of the generic find_board() + * function in libprtdiag, but since we need to pass another parameter, + * we cannot simply overlay the symbol table. + */ +static Board_node * +littleneck_find_board(Sys_tree *root, int board) +{ + Board_node *bnode = root->bd_list; + + while ((bnode != NULL) && (board != bnode->board_num)) { + bnode = bnode->next; + } + return (bnode); +} + +/* + * add_node + * + * This function adds a board node to the board structure where that + * that node's physical component lives. + */ +void +add_node(Sys_tree *root, Prom_node *pnode) +{ + int board = -1; + int portid = -1; + + void *value = NULL; + Board_node *bnode = NULL; + Prom_node *p = NULL; + + /* Get the board number of this board from the portid prop */ + value = get_prop_val(find_prop(pnode, "portid")); + if (value != NULL) { + portid = *(int *)value; + } + + board = LNECK_PORTID_TO_BOARD_NUM(portid); + + /* find the board node with the same board number */ + if ((bnode = littleneck_find_board(root, board)) == NULL) { + bnode = littleneck_insert_board(root, board); + } + + /* now attach this prom node to the board list */ + /* Insert this node at the end of the list */ + pnode->sibling = NULL; + if (bnode->nodes == NULL) + bnode->nodes = pnode; + else { + p = bnode->nodes; + while (p->sibling != NULL) + p = p->sibling; + p->sibling = pnode; + } +} + +/* + * This function provides formatting of the memory config + * information that get_us3_mem_regs() and display_us3_banks() code has + * gathered. It overrides the generic print_us3_memory_line() code + * which prints an error message. + */ +void +print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, + char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id) +{ + int mcid; + char board_id[2]; + board_id[0] = 'A'; /* it will usually be, A, there's only one mc! */ + board_id[1] = 'B'; /* redundant. */ + mcid = LNECK_PORTID_TO_SAFARI_ID(portid); + + log_printf(dgettext(TEXT_DOMAIN, + "\n C%-1c %2d %2d %4lldMB %11-s %4lldMB " + " %2d-way %d"), + board_id[portid], mcid, (bank_id % 4), bank_size, + bank_status, dimm_size, intlv, seg_id, 0); +} + +/* + * We call do_devinfo() in order to use the libdevinfo device tree + * instead of OBP's device tree. + */ +int +do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag) +{ + return (do_devinfo(syserrlog, pgname, log_flag, prt_flag)); +} + +/* + * return the property value for the Prop + * passed in. (When using libdevinfo) + */ +void * +get_prop_val(Prop *prop) +{ + if (prop == NULL) + return (NULL); + + return ((void *)(prop->value.val_ptr)); +} + +/* + * Search a Prom node and retrieve the property with the correct + * name. (When using libdevinfo) + */ +Prop * +find_prop(Prom_node *pnode, char *name) +{ + Prop *prop; + + if (pnode == NULL) + return (NULL); + + if (pnode->props == NULL) + return (NULL); + + prop = pnode->props; + if (prop == NULL) + return (NULL); + + if (prop->name.val_ptr == NULL) + return (NULL); + + while ((prop != NULL) && (strcmp((char *)(prop->name.val_ptr), name))) { + prop = prop->next; + } + return (prop); +} + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the name property. + * (When using libdevinfo) + */ +char * +get_node_name(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("name", (char *)prop->name.val_ptr) == 0) + return (prop->value.val_ptr); + prop = prop->next; + } + return (NULL); +} + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the device_type property. + * (When using libdevinfo) + */ +char * +get_node_type(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("device_type", (char *)prop->name.val_ptr) == 0) + return (prop->value.val_ptr); + prop = prop->next; + } + return (NULL); +} + + +/* + * Fills in the i/o card list to be displayed later in display_pci(); + */ +void +fill_pci_card_list(Prom_node * pci_instance, Prom_node * pci_card_node, + struct io_card *pci_card, + struct io_card **pci_card_list, char **slot_name_arr) +{ + Prom_node *pci_bridge_node; + Prom_node *pci_parent_bridge; + int *int_val; + int pci_bridge = FALSE; + int pci_bridge_dev_no = -1; + int portid; + int pci_bus; + char buf[MAXSTRLEN]; + char *slot_name = NULL; /* info in "slot-names" prop */ + char *child_name; + char *name; + char *type; + void *value; + + while (pci_card_node != NULL) { + int is_pci = FALSE; + type = NULL; + name = NULL; + /* If it doesn't have a name, skip it */ + name = (char *)get_prop_val( + find_prop(pci_card_node, "name")); + if (name == NULL) { + pci_card_node = pci_card_node->sibling; + continue; + } + /* + * Get the portid of the schizo that this card + * lives under. + */ + portid = -1; + value = get_prop_val(find_prop(pci_instance, "portid")); + if (value != NULL) { + portid = *(int *)value; + } + pci_card->schizo_portid = portid; + /* + * Find out whether this is PCI bus A or B + * using the 'reg' property. + */ + int_val = (int *)get_prop_val(find_prop(pci_instance, "reg")); + + if (int_val != NULL) { + int_val++; /* skip over first integer */ + pci_bus = ((*int_val) & 0x7f0000); + if (pci_bus == 0x600000) + pci_card->pci_bus = 'A'; + else if (pci_bus == 0x700000) + pci_card->pci_bus = 'B'; + else + pci_card->pci_bus = '-'; + } else { + pci_card->pci_bus = '-'; + } + /* + * get dev# and func# for this card from the + * 'reg' property. + */ + int_val = (int *)get_prop_val( + find_prop(pci_card_node, "reg")); + if (int_val != NULL) { + pci_card->dev_no = (((*int_val) & 0xF800) >> 11); + pci_card->func_no = (((*int_val) & 0x700) >> 8); + } else { + pci_card->dev_no = -1; + pci_card->func_no = -1; + } + + /* We only have one slot on bus A */ + if ((pci_card->pci_bus == 'A') && (pci_card->dev_no != 1) && + !pci_bridge) { + pci_card_node = pci_card_node->sibling; + continue; + } + if ((pci_card->pci_bus == 'B') && (pci_card->dev_no > 4)) { + pci_card_node = pci_card_node->sibling; + continue; + } + + type = (char *)get_prop_val( + find_prop(pci_card_node, "device_type")); + /* + * If this is a pci-bridge, then store its dev# + * as its children nodes need this to get their slot#. + * We set the pci_bridge flag so that we know we are + * looking at a pci-bridge node. This flag gets reset + * every time we enter this while loop. + */ + + /* + * Check for a PCI-PCI Bridge for PCI and cPCI + * IO Boards using the name and type properties. + */ + if ((type != NULL) && (strncmp(name, "pci", 3) == 0) && + (strcmp(type, "pci") == 0)) { + pci_bridge_node = pci_card_node; + is_pci = TRUE; + if (!pci_bridge) { + pci_bridge_dev_no = pci_card->dev_no; + pci_parent_bridge = pci_bridge_node; + pci_bridge = TRUE; + } + } + + /* + * Get slot-names property from slot_names_arr. + * If we are the child of a pci_bridge we use the + * dev# of the pci_bridge as an index to get + * the slot number. We know that we are a child of + * a pci-bridge if our parent is the same as the last + * pci_bridge node found above. + */ + if (pci_card->dev_no != -1) { + /* + * We compare this cards parent node with the + * pci_bridge_node to see if it's a child. + */ + if (pci_card_node->parent != pci_instance && + pci_bridge) { + /* use dev_no of pci_bridge */ + slot_name = + slot_name_arr[pci_bridge_dev_no -1]; + } else { + slot_name = + slot_name_arr[pci_card->dev_no-1]; + } + if (slot_name != NULL && + strlen(slot_name) != 0) { + /* Slot num is last char in string */ + (void) snprintf(pci_card->slot_str, MAXSTRLEN, + "%c", slot_name[strlen(slot_name) - 1]); + } else { + (void) snprintf(pci_card->slot_str, MAXSTRLEN, + "-"); + } + + } else { + (void) snprintf(pci_card->slot_str, MAXSTRLEN, + "%c", '-'); + } + + /* + * Check for failed status. + */ + if (node_failed(pci_card_node)) + strcpy(pci_card->status, "fail"); + else + strcpy(pci_card->status, "ok"); + + /* Get the model of this pci_card */ + value = get_prop_val(find_prop(pci_card_node, "model")); + if (value == NULL) + pci_card->model[0] = '\0'; + else { + (void) snprintf(pci_card->model, MAXSTRLEN, "%s", + (char *)value); + /* Skip sgsbbc nodes, they are not cards */ + if (strcmp(pci_card->model, "SUNW,sgsbbc") == 0) { + pci_card_node = pci_card_node->sibling; + continue; + } + } + /* + * The card may have a "clock-frequency" but we + * are not interested in that. Instead we get the + * "clock-frequency" of the PCI Bus that the card + * resides on. PCI-A can operate at 33Mhz or 66Mhz + * depending on what card is plugged into the Bus. + * PCI-B always operates at 33Mhz. + */ + int_val = get_prop_val(find_prop(pci_instance, + "clock-frequency")); + if (int_val != NULL) { + pci_card->freq = LNECK_CLK_FREQ_TO_MHZ(*int_val); + } else { + pci_card->freq = -1; + } + + /* + * Figure out how we want to display the name + */ + value = get_prop_val(find_prop(pci_card_node, + "compatible")); + if (value != NULL) { + /* use 'name'-'compatible' */ + (void) snprintf(buf, MAXSTRLEN, "%s-%s", name, + (char *)value); + } else { + /* just use 'name' */ + (void) snprintf(buf, MAXSTRLEN, "%s", name); + } + name = buf; + + /* + * If this node has children, add the device_type + * of the child to the name value of this pci_card-> + */ + child_name = (char *)get_node_name(pci_card_node->child); + if ((pci_card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(pci_card_node->child, + "device_type")); + if (value != NULL) { + /* add device_type of child to name */ + (void) snprintf(pci_card->name, MAXSTRLEN, + "%s/%s (%s)", + name, child_name, + (char *)value); + } else { + /* just add childs name */ + (void) snprintf(pci_card->name, MAXSTRLEN, + "%s/%s", name, + child_name); + } + } else { + (void) snprintf(pci_card->name, MAXSTRLEN, + "%s", (char *)name); + } + + /* + * If this is a pci-bridge, then add the word + * 'pci-bridge' to its model. If we can't find + * a model, then we just describe what the device + * is based on some properties. + */ + if (pci_bridge) { + if (strlen(pci_card->model) == 0) { + if (pci_card_node->parent == pci_bridge_node) + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s", "device on pci-bridge"); + else if (pci_card_node->parent + == pci_parent_bridge) + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s", "pci-bridge/pci-bridge"); + else + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s", "PCI-BRIDGE"); + } + else + (void) snprintf(pci_card->model, MAXSTRLEN, + "%s/pci-bridge", pci_card->model); + } + /* insert this pci_card in the list to be displayed later */ + + *pci_card_list = insert_io_card(*pci_card_list, pci_card); + + /* + * If we are dealing with a pci-bridge, we need to move + * down to the children of this bridge if there are any. + * + * If we are not, we are either dealing with a regular + * card (in which case we move onto the sibling of this + * card) or we are dealing with a child of a pci-bridge + * (in which case we move onto the child's siblings or + * if there are no more siblings for this child, we + * move onto the parents siblings). + */ + pci_card_node = next_pci_card(pci_card_node, &pci_bridge, + is_pci, pci_bridge_node, + pci_parent_bridge, pci_instance); + } /* end-while */ +} + +/* + * Helper function for fill_pci_card_list(). Indicates which + * card node to go to next. + * Parameters: + * ----------- + * Prom_node * curr_card: pointer to the current card node + * + * int * is_bridge: indicates whether or not the card (is | is on) + * a pci bridge + * + * int is_pcidev: indicates whether or not the current card + * is a pci bridge + * + * Prom_node * curr_bridge: pointer to the current pci bridge. Eg: + * curr_card->parent. + * + * Prom_node * parent_bridge: pointer to the first pci bridge encountered. + * we could have nested pci bridges, this would + * be the first one. + * + * Prom_node * pci: pointer to the pci instance that we are attached to. + * This would be parent_bridge->parent, or + * curr_node->parent, if curr_node is not on a pci bridge. + */ +static Prom_node * +next_pci_card(Prom_node *curr_card, int *is_bridge, int is_pcidev, + Prom_node *curr_bridge, Prom_node *parent_bridge, + Prom_node *pci) +{ + Prom_node * curr_node = curr_card; + if (*is_bridge) { + /* + * is_pcidev is used to prevent us from following the + * children of something like a scsi device. + */ + if (curr_node->child != NULL && is_pcidev) { + curr_node = curr_node->child; + } else { + curr_node = curr_node->sibling; + if (curr_node == NULL) { + curr_node = curr_bridge->sibling; + while (curr_node == NULL && + curr_bridge != parent_bridge && + curr_bridge != NULL) { + curr_node = + curr_bridge->parent->sibling; + curr_bridge = curr_bridge->parent; + if (curr_node != NULL && + curr_node->parent == pci) + break; + } + if (curr_bridge == NULL || + curr_node == NULL || + curr_node->parent == pci || + curr_bridge == parent_bridge || + curr_node == parent_bridge) { + *is_bridge = FALSE; + } + } + } + + } else { + curr_node = curr_node->sibling; + } + return (curr_node); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/lw8/Makefile b/usr/src/lib/libprtdiag_psr/sparc/lw8/Makefile new file mode 100644 index 0000000000..b11de316ea --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/lw8/Makefile @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/lw8/Makefile + +PRTDIAG_DIRS= picl + +all := TARGET= all +lint := TARGET= lint +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +_msg := TARGET= _msg + +.KEEP_STATE: + +all lint clean clobber install _msg : $(PRTDIAG_DIRS) + +$(PRTDIAG_DIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + diff --git a/usr/src/lib/libprtdiag_psr/sparc/lw8/common/lw8.c b/usr/src/lib/libprtdiag_psr/sparc/lw8/common/lw8.c new file mode 100644 index 0000000000..fcf98a9f51 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/lw8/common/lw8.c @@ -0,0 +1,3607 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <alloca.h> +#include <errno.h> +#include <libintl.h> +#include <sys/utsname.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/openpromio.h> +#include <sys/ddi.h> +#include <syslog.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <locale.h> +#include <picl.h> +#include "pdevinfo.h" +#include "display.h" +#include "display_sun4u.h" +#include "picldefs.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +#define EM_INIT_FAIL dgettext(TEXT_DOMAIN,\ + "picl_initialize failed: %s\n") +#define EM_GET_ROOT_FAIL dgettext(TEXT_DOMAIN,\ + "Getting root node failed: %s\n") +#define EM_PRTDIAG_FAIL dgettext(TEXT_DOMAIN, "Prtdiag failed!\n") + +#define SIGN_ON_MSG dgettext(TEXT_DOMAIN,\ + "System Configuration: Sun Microsystems ") +#define SYSCLK_FREQ_MSG dgettext(TEXT_DOMAIN,\ + "System clock frequency: %d MHZ\n") +#define MEM_SIZE_MSG dgettext(TEXT_DOMAIN, "Memory size: ") +#define FFB_DOUBLE_BUF dgettext(TEXT_DOMAIN, "FFB, Double Buffered") +#define FFB_SINGLE_BUF dgettext(TEXT_DOMAIN, "FFB, Single Buffered") + +#define DEFAULT_BOARD_NUM 0 +#define DEFAULT_PORTID 0 +#define CLK_FREQ_66MHZ 66 +#define USB -1 +#define HUB -2 + +/* bus id */ +#define SBUS_TYPE 0 +#define PCI_TYPE 1 +#define UPA_TYPE 2 + +#define UPA_NAME "upa" + +/* + * PICL classes + */ +#define PICL_CLASS_OPTIONS "options" + +/* + * Property names + */ + +#define OBP_PROP_REG "reg" +#define OBP_PROP_CLOCK_FREQ "clock-frequency" +#define OBP_PROP_BOARD_NUM "board#" +#define OBP_PROP_REVISION_ID "revision-id" +#define OBP_PROP_VERSION_NUM "version#" +#define OBP_PROP_BOARD_TYPE "board_type" +#define OBP_PROP_ECACHE_SIZE "ecache-size" +#define OBP_PROP_L2_CACHE_SIZE "l2-cache-size" +#define OBP_PROP_L3_CACHE_SIZE "l3-cache-size" +#define OBP_PROP_IMPLEMENTATION "implementation#" +#define OBP_PROP_MASK "mask#" +#define OBP_PROP_COMPATIBLE "compatible" +#define OBP_PROP_STATUS "status" +#define OBP_PROP_BANNER_NAME "banner-name" +#define OBP_PROP_MODEL "model" +#define OBP_PROP_66MHZ_CAPABLE "66mhz-capable" +#define OBP_PROP_FBC_REG_ID "fbc_reg_id" +#define OBP_PROP_VERSION "version" + +#define PROP_POWERFAIL_TIME "powerfail-time" +#define PICL_PROP_LOW_WARNING_THRESHOLD "LowWarningThreshold" + +#define DEFAULT_LINE_WIDTH 85 +#define HEADING_SYMBOL "=" + +#define MAX_IWAYS 32 + +typedef struct bank_list { + picl_nodehdl_t nodeh; + uint32_t iway_count; + uint32_t iway[MAX_IWAYS]; + struct bank_list *next; +} bank_list_t; + +typedef struct { + uint64_t base; + uint64_t size; + int ifactor; + int bank_count; +} seg_info_t; + +static struct io_card *io_card_list = NULL; /* The head of the IO card list */ +static bank_list_t *mem_banks = NULL; +static int mem_xfersize; +static int no_xfer_size = 0; + +static char *io_device_table[] = { + "block", + "disk", + "cdrom", + "floppy", + "tape", + "network", + "display", + "serial", + "parallel", + "scsi", + "scsi-2", + "scsi-3", + "ide", + "fcal", + "keyboard", + "mouse", + "dma" +}; + +#define NIODEVICE sizeof (io_device_table) / sizeof (io_device_table[0]) + +static char *bus_table[] = { + "ebus", + "isa", + "pmu" +}; + +#define NBUS sizeof (bus_table) / sizeof (bus_table[0]) + +/* + * check if it is an IO deice + */ +static int +is_io_device(char *device_class) +{ + int i; + + for (i = 0; i < NIODEVICE; i++) { + if (strcmp(device_class, io_device_table[i]) == 0) + return (1); + } + + return (0); +} + +/* + * check if it is a bus + */ +static int +is_bus(char *device_class) +{ + int i; + + for (i = 0; i < NBUS; i++) { + if (strcmp(device_class, bus_table[i]) == 0) + return (1); + } + + return (0); +} + +/* + * search children to get the node by the nodename + */ +static int +picldiag_get_node_by_name(picl_nodehdl_t rooth, char *name, + picl_nodehdl_t *nodeh) +{ + picl_nodehdl_t childh; + int err; + char *nodename; + + nodename = alloca(strlen(name) + 1); + if (nodename == NULL) + return (PICL_FAILURE); + + err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_NAME, + nodename, (strlen(name) + 1)); + if (err != PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + continue; + } + + if (strcmp(nodename, name) == 0) { + *nodeh = childh; + return (PICL_SUCCESS); + } + + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + } + + return (err); +} + +/* + * get the value by the property name of the string prop + * Caller must free the outbuf + */ +static int +picldiag_get_string_propval(picl_nodehdl_t modh, char *prop_name, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + char *prop_value; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + /* + * If it is not a string prop, return NULL + */ + if (pinfo.type != PICL_PTYPE_CHARSTRING) + return (PICL_FAILURE); + + prop_value = malloc(pinfo.size); + if (prop_value == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(proph, prop_value, pinfo.size); + if (err != PICL_SUCCESS) { + free(prop_value); + return (err); + } + + *outbuf = prop_value; + return (PICL_SUCCESS); +} + + +/* + * return the value as a signed integer + */ + +static int64_t +picldiag_get_int_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + int8_t int8v; + int16_t int16v; + int32_t int32v; + int64_t int64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int or uint prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) { + *ret = PICL_FAILURE; + return (0); + } + + switch (pinfo.size) { + case sizeof (int8_t): + err = picl_get_propval(proph, &int8v, sizeof (int8v)); + *ret = err; + return (int8v); + case sizeof (int16_t): + err = picl_get_propval(proph, &int16v, sizeof (int16v)); + *ret = err; + return (int16v); + case sizeof (int32_t): + err = picl_get_propval(proph, &int32v, sizeof (int32v)); + *ret = err; + return (int32v); + case sizeof (int64_t): + err = picl_get_propval(proph, &int64v, sizeof (int64v)); + *ret = err; + return (int64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} + +/* + * return the value of the uint prop + */ +static uint64_t +picldiag_get_uint_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + uint8_t uint8v; + uint16_t uint16v; + uint32_t uint32v; + uint64_t uint64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int or uint prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) { + *ret = PICL_FAILURE; + return (0); + } + + /* uint prop */ + + switch (pinfo.size) { + case sizeof (uint8_t): + err = picl_get_propval(proph, &uint8v, sizeof (uint8v)); + *ret = err; + return (uint8v); + case sizeof (uint16_t): + err = picl_get_propval(proph, &uint16v, sizeof (uint16v)); + *ret = err; + return (uint16v); + case sizeof (uint32_t): + err = picl_get_propval(proph, &uint32v, sizeof (uint32v)); + *ret = err; + return (uint32v); + case sizeof (uint64_t): + err = picl_get_propval(proph, &uint64v, sizeof (uint64v)); + *ret = err; + return (uint64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} + +/* + * return the value of the float prop + */ +static float +picldiag_get_float_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + float floatv; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return ((float)0); + } + + /* + * If it is not a float prop, return failure + */ + if (pinfo.type != PICL_PTYPE_FLOAT) { + *ret = PICL_FAILURE; + return ((float)0); + } + + *ret = picl_get_propval(proph, &floatv, sizeof (floatv)); + return (floatv); +} + +/* + * get the clock frequency + */ +static int +picldiag_get_clock_freq(picl_nodehdl_t modh, uint32_t *freq) +{ +#define ROUND_TO_MHZ(x) (((x) + 500000)/ 1000000) + int err; + uint64_t clk_freq; + + clk_freq = picldiag_get_uint_propval(modh, OBP_PROP_CLOCK_FREQ, &err); + if (err != PICL_SUCCESS) + return (err); + + *freq = ROUND_TO_MHZ(clk_freq); + + return (PICL_SUCCESS); +} + +/* + * get the clock frequency from parent + */ +static int +picldiag_get_clock_from_parent(picl_nodehdl_t nodeh, uint32_t *clk) +{ + picl_nodehdl_t parenth; + int err; + + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &parenth, sizeof (parenth)); + + while (err == PICL_SUCCESS) { + err = picldiag_get_clock_freq(parenth, clk); + if (err != PICL_PROPNOTFOUND) + return (err); + + err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT, + &parenth, sizeof (parenth)); + } + + return (err); +} + +/* + * get _fru_parent prop + * If not found, then travese superiors (parent nodes) until + * a _fru_parent property is found. + * If not found, no fru parent + */ +static int +picldiag_get_fru_parent(picl_nodehdl_t nodeh, picl_nodehdl_t *fruparenth) +{ + picl_nodehdl_t fruh; + int err; + + /* find fru parent */ + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_FRU_PARENT, + &fruh, sizeof (fruh)); + + if (err != PICL_SUCCESS) + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_LOC_PARENT, + &fruh, sizeof (fruh)); + + while (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &nodeh, sizeof (nodeh)); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_FRU_PARENT, + &fruh, sizeof (fruh)); + if (err != PICL_SUCCESS) + err = picl_get_propval_by_name(nodeh, + PICL_REFPROP_LOC_PARENT, &fruh, sizeof (fruh)); + } + + if (err == PICL_SUCCESS) + *fruparenth = fruh; + + return (err); +} + +/* + * get label + * + * To get the label, use the following algorithm: + * Lookup "Label" property in the fru node itself. If no + * Label found, then traverse superiors (parent nodes) until + * a Label property is found. + * if not found, then no label + */ +static int +picldiag_get_label(picl_nodehdl_t nodeh, char **label) +{ + int err; + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, label); + + while (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &nodeh, sizeof (nodeh)); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, + label); + } + + return (err); +} + +/* + * get combined label + * + * like picldiag_get_label, except concatenates the labels of parent locations + * eg SB0/P3 for processor P3 on system board SB0 + * + * if caller specifies non-zero label length, label will be cut to specified + * length. + * negative length is left justified, non-negative length is right justified + */ +static int +picldiag_get_combined_label(picl_nodehdl_t nodeh, char **label, int lablen) +{ + int err; + char *ptr; + char *ptr1 = NULL; + char *ptr2; + int len; + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, &ptr1); + if (err != PICL_PROPNOTFOUND && err != PICL_SUCCESS) + return (err); + + for (;;) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &nodeh, sizeof (nodeh)); + if (err == PICL_PROPNOTFOUND) + break; + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, &ptr); + if (err == PICL_SUCCESS) { + if (ptr1 == NULL) { + ptr1 = ptr; + } else { + ptr2 = malloc(strlen(ptr1) + strlen(ptr) + 2); + if (ptr2 == NULL) + return (PICL_FAILURE); + (void) strcpy(ptr2, ptr); + (void) strcat(ptr2, "/"); + (void) strcat(ptr2, ptr1); + (void) free(ptr); + (void) free(ptr1); + ptr1 = ptr2; + } + } else if (err != PICL_PROPNOTFOUND) { + return (err); + } + } + + if (ptr1 == NULL) + return (PICL_PROPNOTFOUND); + + len = strlen(ptr1); + /* if no string truncation is desired or required */ + if ((lablen == 0) || (len <= abs(lablen))) { + *label = ptr1; + return (PICL_SUCCESS); + } + + /* string truncation is required; alloc space for (lablen + \0) */ + ptr = malloc(abs(lablen) + 1); + if (ptr == 0) + return (PICL_FAILURE); + if (lablen > 0) { + /* right justification; label = "+<string>\0" */ + strcpy(ptr, "+"); + strncat(ptr, ptr1 + len - lablen + 1, lablen + 1); + } else { + /* left justification; label = "<string>+\0" */ + strncpy(ptr, ptr1, abs(lablen) - 1); + strcat(ptr, "+"); + } + + *label = ptr; + return (PICL_SUCCESS); +} + +/* + * return the first compatible value + */ +static int +picldiag_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + char *pval; + + err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE, + &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + *outbuf = pval; + return (PICL_SUCCESS); + } + + if (pinfo.type != PICL_PTYPE_TABLE) + return (PICL_FAILURE); + + /* get first string from table */ + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + + *outbuf = pval; + return (PICL_SUCCESS); +} + +/* + * print the header in the center + */ +static void +logprintf_header(char *header, size_t line_width) +{ + size_t start_pos; + size_t i; + + log_printf("\n"); + start_pos = (line_width - strlen(header) - 2) / 2; + + for (i = 0; i < start_pos; i++) + log_printf("%s", HEADING_SYMBOL); + + log_printf(" %s ", header); + + for (i = 0; i < start_pos; i++) + log_printf("%s", HEADING_SYMBOL); + + log_printf("\n"); +} + +/* + * print the size + */ +static void +logprintf_size(uint64_t size) +{ +#define SIZE_FIELD 11 + + uint64_t kbyte = 1024; + uint64_t mbyte = 1024 * 1024; + uint64_t gbyte = 1024 * 1024 * 1024; + uint64_t residue; + char buf[SIZE_FIELD]; + + if (size >= gbyte) { + residue = size % gbyte; + if (residue == 0) + snprintf(buf, sizeof (buf), "%dGB", + (int)(size / gbyte)); + else + snprintf(buf, sizeof (buf), "%.2fGB", + (float)size / gbyte); + } else if (size >= mbyte) { + residue = size % mbyte; + if (residue == 0) + snprintf(buf, sizeof (buf), "%dMB", + (int)(size / mbyte)); + else + snprintf(buf, sizeof (buf), "%.2fMB", + (float)size / mbyte); + } else { + residue = size % kbyte; + if (residue == 0) + snprintf(buf, sizeof (buf), "%dKB", + (int)(size / kbyte)); + else + snprintf(buf, sizeof (buf), "%.2fKB", + (float)size / kbyte); + } + + log_printf("%-10s ", buf); +} + +/* + * display platform banner + */ +static int +display_platform_banner(picl_nodehdl_t plafh) +{ + char *platform; + char *banner_name; + int err; + + /* + * get PICL_PROP_MACHINE and PICL_PROP_BANNER_NAME + */ + log_printf(SIGN_ON_MSG); + err = picldiag_get_string_propval(plafh, PICL_PROP_MACHINE, + &platform); + if (err != PICL_SUCCESS) + return (err); + log_printf(" %s", platform); + free(platform); + + err = picldiag_get_string_propval(plafh, OBP_PROP_BANNER_NAME, + &banner_name); + if (err != PICL_SUCCESS) + return (err); + log_printf(" %s", banner_name); + free(banner_name); + + log_printf("\n"); + return (PICL_SUCCESS); +} + +/* + * display the clock frequency + */ +static int +display_system_clock(picl_nodehdl_t plafh) +{ + uint32_t system_clk; + int err; + + err = picldiag_get_clock_freq(plafh, &system_clk); + if (err != PICL_SUCCESS) + return (err); + + log_printf(SYSCLK_FREQ_MSG, system_clk); + + return (PICL_SUCCESS); +} + +/* + * callback function to display the memory size + */ +/*ARGSUSED*/ +static int +memory_callback(picl_nodehdl_t memh, void *args) +{ + uint64_t mem_size; + int err; + + log_printf(MEM_SIZE_MSG); + mem_size = picldiag_get_uint_propval(memh, PICL_PROP_SIZE, &err); + if (err == PICL_SUCCESS) + logprintf_size(mem_size); + log_printf("\n"); + no_xfer_size = 0; + mem_xfersize = picldiag_get_uint_propval(memh, PICL_PROP_TRANSFER_SIZE, + &err); + if (err == PICL_PROPNOTFOUND) + no_xfer_size = 1; + return (PICL_WALK_TERMINATE); +} + +/* + * callback function to print cpu information + */ +/*ARGSUSED*/ +static int +cpu_callback(picl_nodehdl_t nodeh, void *args) +{ + int err; + int id0, id1; + uint64_t uintval; + uint64_t decoded_mask; + uint32_t freq; + char *impl_name; + char *status; + picl_prophdl_t parenth; + picl_prophdl_t peerh; + char *label; + int is_cmp = 0; + int is_cheetah = 0; + + /* + * first check the implementation. If it's a CMP + * we need to combine info from both cores. + */ + + impl_name = NULL; + uintval = picldiag_get_uint_propval(nodeh, + OBP_PROP_IMPLEMENTATION, &err); + if (err == PICL_SUCCESS) { + if (CPU_IMPL_IS_CMP(uintval)) { + is_cmp = 1; + err = picldiag_get_first_compatible_value(nodeh, + &impl_name); + if (err != PICL_SUCCESS) + impl_name = NULL; + } else if (IS_CHEETAH(uintval) || IS_CHEETAH_PLUS(uintval)) { + is_cheetah = 1; + err = picldiag_get_string_propval(nodeh, PICL_PROP_NAME, + &impl_name); + if (err != PICL_SUCCESS) + impl_name = NULL; + } + } else { + err = picldiag_get_string_propval(nodeh, + PICL_PROP_NAME, &impl_name); + if (err != PICL_SUCCESS) + impl_name = NULL; + } + + /* + * In the CMP case, combine info from both cores. If we + * are being called with the first core handle, we display + * info on the sibling core handle too. If we are being + * called with the second core hanlde as an argument, simply + * return. + */ + + if (is_cmp) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &peerh, sizeof (picl_nodehdl_t)); + if (err != PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + id1 = picldiag_get_uint_propval(peerh, PICL_PROP_ID, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + } + + /* + * If no ID is found, return + */ + id0 = picldiag_get_uint_propval(nodeh, PICL_PROP_ID, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + if (is_cmp) { + log_printf("%3d,%3d ", id0, id1); + } else { + log_printf("%7d ", id0); + } + + /* + * If no freq is found, return + */ + err = picldiag_get_clock_freq(nodeh, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + log_printf("%4d MHz ", freq); + + /* Ecache size */ + + if (is_cmp) { + uintval = picldiag_get_uint_propval(nodeh, + OBP_PROP_L3_CACHE_SIZE, &err); + if (err == PICL_SUCCESS) { + /* + * Panther L3 is logically shared, so the total E$ + * size is equal to the core size. + */ + logprintf_size(uintval); + } else { + uintval = picldiag_get_uint_propval(nodeh, + OBP_PROP_L2_CACHE_SIZE, &err); + if (err == PICL_SUCCESS) { + /* + * Jaguar has a split E$, so the total size + * is the sum of both cores. + */ + logprintf_size(uintval * 2); + } else + log_printf(" - "); + } + } else { + uintval = picldiag_get_uint_propval(nodeh, + OBP_PROP_ECACHE_SIZE, &err); + if (err == PICL_SUCCESS) + logprintf_size(uintval); + else + log_printf(" - "); + } + + /* Implementation */ + if (impl_name != NULL) + log_printf(" %-20s ", impl_name); + else + log_printf(" <unknown> "); + + /* CPU Mask */ + uintval = picldiag_get_uint_propval(nodeh, OBP_PROP_MASK, &err); + if (err == PICL_PROPNOTFOUND) + log_printf(" - "); + else if (err == PICL_SUCCESS) { + if (is_cheetah) { + decoded_mask = REMAP_CHEETAH_MASK(uintval); + } else { + decoded_mask = uintval; + } + log_printf("%2lld.%-2lld ", (decoded_mask >> 4) & 0xf, + decoded_mask & 0xf); + } else + return (err); + + /* + * Status - if the node has a status property then display that + * otherwise display the State property + */ + err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS, &status); + if (err == PICL_SUCCESS) { + log_printf("%-12s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != + PICL_PROPVALUNAVAILABLE && err != PICL_ENDOFLIST) { + return (err); + } else { + err = picldiag_get_string_propval(nodeh, + PICL_PROP_STATE, &status); + if (err == PICL_SUCCESS) { + log_printf("%-12s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != + PICL_PROPVALUNAVAILABLE && err != + PICL_ENDOFLIST) { + return (err); + } else { + log_printf("unknown "); + } + } + + /* + * Location: use label of fru parent + */ + err = picldiag_get_fru_parent(nodeh, &parenth); + if (err == PICL_PROPNOTFOUND) { + log_printf(" - "); + } else if (err == PICL_SUCCESS) { + err = picldiag_get_combined_label(parenth, &label, 12); + if (err == PICL_PROPNOTFOUND) + log_printf(" - "); + else if (err == PICL_SUCCESS) { + log_printf("%s", label); + free(label); + } else + return (err); + } else + return (err); + + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +/* + * display cpu information + */ +static int +display_cpu_info(picl_nodehdl_t plafh) +{ + int err; + + /* + * Display the table header for CPUs . Then display the CPU + * frequency, cache size, and processor revision on all the boards. + */ + logprintf_header(dgettext(TEXT_DOMAIN, "CPUs"), DEFAULT_LINE_WIDTH); + log_printf(" E$ CPU " + "CPU\n"); + log_printf("CPU Freq Size Implementation " + "Mask Status Location\n"); + log_printf("------- -------- ---------- ------------------- " + "----- ------ --------\n"); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_CPU, PICL_CLASS_CPU, + cpu_callback); + return (err); +} + +/* + * Inserts an io_card structure into the list. + */ +static void +add_io_card(uint32_t board, uint32_t bus_id, uint32_t slot, char *label, + uint32_t freq, char *name, char *model, char *status, char *devfs_path) +{ + struct io_card card; + + card.display = 1; + card.board = board; + switch (bus_id) { + case SBUS_TYPE: + strlcpy(card.bus_type, SBUS_NAME, MAXSTRLEN); + break; + case PCI_TYPE: + strlcpy(card.bus_type, PCI_NAME, MAXSTRLEN); + break; + case UPA_TYPE: + strlcpy(card.bus_type, UPA_NAME, MAXSTRLEN); + break; + default: /* won't reach here */ + strlcpy(card.bus_type, "", MAXSTRLEN); + break; + } + if (label == NULL) + card.slot = slot; + else { + card.slot = PCI_SLOT_IS_STRING; + (void) strlcpy(card.slot_str, label, MAXSTRLEN); + } + card.freq = freq; + card.status[0] = '\0'; + card.name[0] = '\0'; + card.model[0] = '\0'; + card.notes[0] = '\0'; + if (status != NULL) + strlcpy(card.status, status, MAXSTRLEN); + if (name != NULL) + strlcpy(card.name, name, MAXSTRLEN); + if (model != NULL) + strlcpy(card.model, model, MAXSTRLEN); + if (status != NULL) + strlcpy(card.status, status, MAXSTRLEN); + if (devfs_path != NULL) + strlcpy(card.notes, devfs_path, MAXSTRLEN); + + io_card_list = insert_io_card(io_card_list, &card); +} + +static void +append_to_bank_list(bank_list_t *newptr) +{ + bank_list_t *ptr; + + if (mem_banks == NULL) { + mem_banks = newptr; + return; + } + ptr = mem_banks; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = newptr; +} + +static void +free_bank_list(void) +{ + bank_list_t *ptr; + bank_list_t *tmp; + + for (ptr = mem_banks; ptr != NULL; ptr = tmp) { + tmp = ptr->next; + free(ptr); + } + mem_banks = NULL; +} + + +/* + * print label for memory module + */ +static int +logprintf_memory_module_label(picl_nodehdl_t moduleh) +{ + picl_nodehdl_t fruparenth; + int err; + char *label; + + err = picldiag_get_fru_parent(moduleh, &fruparenth); + if (err == PICL_PROPNOTFOUND) { + log_printf("-"); + return (PICL_SUCCESS); + } else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruparenth, &label, 30); + if (err == PICL_PROPNOTFOUND) + log_printf("-"); + else if (err == PICL_SUCCESS) { + log_printf("%-15s", label); + free(label); + } else + return (err); + + return (PICL_SUCCESS); +} + +/* + * print the bank id and add the bank handle in the bank list + * return the head of the bank list + */ +static int +membank_callback(picl_nodehdl_t bankh, void *args) +{ + int err; + int64_t id; + uint64_t match; + uint64_t mask; + int i; + bank_list_t *newptr; + seg_info_t *segp = args; + + /* + * print the bank id in the segment table contains column + */ + id = picldiag_get_uint_propval(bankh, PICL_PROP_ID, &err); + if (segp->bank_count > 0) + log_printf(","); + if (err == PICL_PROPNOTFOUND) + log_printf("-"); + else if (err == PICL_SUCCESS) + log_printf("%-lld", id); + else + return (err); + segp->bank_count++; + + /* + * Save the bank information for later (print_bank_table) + */ + newptr = malloc(sizeof (*newptr)); + if (newptr == NULL) + return (PICL_FAILURE); + + newptr->nodeh = bankh; + newptr->iway_count = 0; + newptr->next = NULL; + append_to_bank_list(newptr); + + /* + * Compute the way numbers for the bank + */ + if (no_xfer_size) + return (PICL_WALK_CONTINUE); + + match = picldiag_get_uint_propval(bankh, PICL_PROP_ADDRESSMATCH, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + mask = picldiag_get_uint_propval(bankh, PICL_PROP_ADDRESSMASK, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + i = 0; + while ((i < segp->ifactor) && (newptr->iway_count < MAX_IWAYS)) { + if (((segp->base + i * mem_xfersize) & mask) == match) + newptr->iway[newptr->iway_count++] = i; + ++i; + } + return (PICL_WALK_CONTINUE); +} + + +/* + * find the memory bank and add the bank handle in the bank list + * return the head of the bank list + */ +static int +logprintf_bankinfo(picl_nodehdl_t segh, seg_info_t *segp) +{ + int err; + + log_printf("BankIDs "); + /* + * find memory-bank + */ + segp->bank_count = 0; + err = picl_walk_tree_by_class(segh, PICL_CLASS_MEMORY_BANK, segp, + membank_callback); + log_printf("\n"); + return (err); +} + +/* + * print the label of memory module or the memory module bank ids + */ +static int +logprintf_seg_contains_col(picl_nodehdl_t nodeh, seg_info_t *segp) +{ + picl_nodehdl_t moduleh; + int err; + + /* + * find memory-module if referenced directly from the memory-segment + * (ie no memory banks) + */ + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_MEMORY_MODULE, + &moduleh, sizeof (moduleh)); + if ((err != PICL_SUCCESS) && (err != PICL_PROPNOTFOUND)) + return (err); + if (err == PICL_SUCCESS) { + err = logprintf_memory_module_label(moduleh); + log_printf("\n"); + return (err); + } + + /* + * memory-module not referenced directly from the memory segment + * so list memory banks instead + */ + err = logprintf_bankinfo(nodeh, segp); + return (err); +} + +/* + * find all memory modules under the given memory module group + * and print its label + */ +static int +logprintf_memory_module_group_info(picl_nodehdl_t memgrph, uint64_t mcid) +{ + int err; + int64_t id; + picl_nodehdl_t moduleh; + char piclclass[PICL_CLASSNAMELEN_MAX]; + picl_nodehdl_t fruparenth; + char *status; + + id = picldiag_get_uint_propval(memgrph, PICL_PROP_ID, &err); + if (err == PICL_PROPNOTFOUND) + id = -1; + else if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(memgrph, PICL_PROP_CHILD, &moduleh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + /* controller id */ + log_printf("%-8lld ", mcid); + + /* group id */ + if (id == -1) { + log_printf("- "); + } else { + log_printf("%-8lld ", id); + } + + err = picl_get_propval_by_name(moduleh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, PICL_CLASS_MEMORY_MODULE) == 0) { + err = logprintf_memory_module_label(moduleh); + if (err != PICL_SUCCESS) + return (err); + } + + err = picldiag_get_fru_parent(moduleh, &fruparenth); + if (err == PICL_SUCCESS) { + err = picldiag_get_string_propval(fruparenth, + PICL_PROP_OPERATIONAL_STATUS, &status); + if (err == PICL_SUCCESS) { + log_printf("%s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND) + return (err); + } else if (err != PICL_PROPNOTFOUND) + return (err); + + err = picl_get_propval_by_name(moduleh, PICL_PROP_PEER, + &moduleh, sizeof (picl_nodehdl_t)); + + log_printf("\n"); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + return (err); +} + +/* + * search children to find memory module group under memory-controller + */ +static int +find_memory_module_group(picl_nodehdl_t mch, int *print_header) +{ + picl_nodehdl_t memgrph; + uint64_t mcid; + int err; + char piclclass[PICL_CLASSNAMELEN_MAX]; + + mcid = picldiag_get_uint_propval(mch, OBP_PROP_PORTID, &err); + if (err == PICL_PROPNOTFOUND) + mcid = DEFAULT_PORTID; + else if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(mch, PICL_PROP_CHILD, + &memgrph, sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(memgrph, + PICL_PROP_CLASSNAME, piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, PICL_CLASS_MEMORY_MODULE_GROUP) == 0) { + if (*print_header == 1) { + log_printf( + dgettext(TEXT_DOMAIN, + "\nMemory Module Groups:\n")); + log_printf("--------------------------"); + log_printf("------------------------\n"); + log_printf("ControllerID GroupID Labels"); + log_printf(" Status\n"); + log_printf("--------------------------"); + log_printf("------------------------\n"); + *print_header = 0; + } + err = logprintf_memory_module_group_info(memgrph, mcid); + if (err != PICL_SUCCESS) + return (err); + } + + err = picl_get_propval_by_name(memgrph, PICL_PROP_PEER, + &memgrph, sizeof (picl_nodehdl_t)); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + return (err); +} + +/* + * print memory module group table per memory-controller + */ +static int +print_memory_module_group_table(picl_nodehdl_t plafh) +{ + picl_nodehdl_t mch; + int err; + char piclclass[PICL_CLASSNAMELEN_MAX]; + int print_header; + + print_header = 1; + + /* + * find memory-controller + */ + err = picl_get_propval_by_name(plafh, PICL_PROP_CHILD, &mch, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(mch, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, PICL_CLASS_MEMORY_CONTROLLER) != 0) { + err = print_memory_module_group_table(mch); + if (err != PICL_SUCCESS) + return (err); + err = picl_get_propval_by_name(mch, PICL_PROP_PEER, + &mch, sizeof (picl_nodehdl_t)); + continue; + } + + err = find_memory_module_group(mch, &print_header); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(mch, PICL_PROP_PEER, + &mch, sizeof (picl_nodehdl_t)); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + + return (err); +} + +/* + * print bank table + */ +static int +print_bank_table(void) +{ + bank_list_t *ptr; + picl_nodehdl_t bankh; + picl_nodehdl_t memgrph; + picl_nodehdl_t mch; + int err; + int32_t i; + uint64_t size; + int id; + + log_printf(dgettext(TEXT_DOMAIN, "\nBank Table:\n")); + log_printf("---------------------------------------"); + log_printf("--------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, " Physical Location\n")); + log_printf(dgettext(TEXT_DOMAIN, "ID ControllerID GroupID ")); + log_printf(dgettext(TEXT_DOMAIN, "Size Interleave Way\n")); + log_printf("---------------------------------------"); + log_printf("--------------------\n"); + + for (ptr = mem_banks; ptr != NULL; ptr = ptr->next) { + bankh = ptr->nodeh; + id = picldiag_get_uint_propval(bankh, PICL_PROP_ID, &err); + if (err != PICL_SUCCESS) + log_printf("%-8s ", "-"); + else + log_printf("%-8d ", id); + + /* find memory-module-group */ + err = picl_get_propval_by_name(bankh, + PICL_REFPROP_MEMORY_MODULE_GROUP, &memgrph, + sizeof (memgrph)); + if (err == PICL_PROPNOTFOUND) { + log_printf("%-8s ", "-"); + log_printf("%-8s ", "-"); + } else if (err != PICL_SUCCESS) + return (err); + else { + /* + * get controller id + */ + err = picl_get_propval_by_name(memgrph, + PICL_PROP_PARENT, &mch, sizeof (picl_nodehdl_t)); + if (err != PICL_SUCCESS) + return (err); + + id = picldiag_get_uint_propval(mch, OBP_PROP_PORTID, + &err); + if (err == PICL_PROPNOTFOUND) + id = DEFAULT_PORTID; /* use default */ + else if (err != PICL_SUCCESS) + return (err); + + log_printf("%-8d ", id); + + /* get group id */ + id = picldiag_get_uint_propval(memgrph, PICL_PROP_ID, + &err); + if (err == PICL_PROPNOTFOUND) + log_printf("- "); + else if (err == PICL_SUCCESS) + log_printf("%-8d ", id); + else + return (err); + } + + size = picldiag_get_uint_propval(bankh, PICL_PROP_SIZE, &err); + if (err == PICL_PROPNOTFOUND) + log_printf("- "); + else if (err == PICL_SUCCESS) + logprintf_size(size); + else + return (err); + + log_printf(" "); + for (i = 0; i < ptr->iway_count; i++) { + if (i != 0) + log_printf(","); + log_printf("%d", ptr->iway[i]); + } + + log_printf("\n"); + } + return (PICL_SUCCESS); +} + +/* + * callback function to print segment, add the bank in the list and + * return the bank list + */ +/* ARGSUSED */ +static int +memseg_callback(picl_nodehdl_t segh, void *args) +{ + seg_info_t seginfo; + int err; + + /* get base address */ + seginfo.base = picldiag_get_uint_propval(segh, PICL_PROP_BASEADDRESS, + &err); + if (err == PICL_PROPNOTFOUND) { + log_printf("-\n"); + return (PICL_WALK_CONTINUE); + } else if (err == PICL_SUCCESS) + log_printf("0x%-16llx ", seginfo.base); + else + return (err); + + /* get size */ + seginfo.size = picldiag_get_uint_propval(segh, PICL_PROP_SIZE, &err); + if (err == PICL_PROPNOTFOUND) { + log_printf("-\n"); + return (PICL_WALK_CONTINUE); + } else if (err == PICL_SUCCESS) + logprintf_size(seginfo.size); + else + return (err); + + /* get interleave factor */ + seginfo.ifactor = picldiag_get_uint_propval(segh, + PICL_PROP_INTERLEAVE_FACTOR, &err); + + if (err == PICL_PROPNOTFOUND) { + log_printf(" -\n"); + return (PICL_WALK_CONTINUE); + } else if (err == PICL_SUCCESS) + log_printf(" %-2d ", seginfo.ifactor); + else + return (err); + + seginfo.bank_count = 0; + err = logprintf_seg_contains_col(segh, &seginfo); + if (err != PICL_SUCCESS) + return (err); + return (PICL_WALK_CONTINUE); +} + +/* + * search children to find memory-segment and set up the bank list + */ +static int +find_segments(picl_nodehdl_t plafh) +{ + int err; + + log_printf(dgettext(TEXT_DOMAIN, "Segment Table:\n")); + log_printf("------------------------------"); + log_printf("-----------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Base Address Size ")); + log_printf(dgettext(TEXT_DOMAIN, "Interleave Factor Contains\n")); + log_printf("------------------------------"); + log_printf("-----------------------------------------\n"); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT, + NULL, memseg_callback); + return (err); +} + +/* + * display memory configuration + */ +static int +display_memory_config(picl_nodehdl_t plafh) +{ + int err; + + logprintf_header(dgettext(TEXT_DOMAIN, "Memory Configuration"), + DEFAULT_LINE_WIDTH); + + mem_banks = NULL; + err = find_segments(plafh); + + if ((err == PICL_SUCCESS) && (mem_banks != NULL)) + print_bank_table(); + + free_bank_list(); + + return (print_memory_module_group_table(plafh)); +} + +/* + * print the hub device + */ +static int +logprintf_hub_devices(picl_nodehdl_t hubh) +{ + char *name; + int portnum; + int err; + + err = picldiag_get_string_propval(hubh, PICL_PROP_NAME, &name); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-12.12s ", name); + free(name); + + err = picl_get_propval_by_name(hubh, OBP_PROP_REG, + &portnum, sizeof (portnum)); + if (err == PICL_PROPNOTFOUND) + log_printf("-\n"); + else if (err == PICL_SUCCESS) + log_printf("%3d\n", portnum); + else + return (err); + + return (PICL_SUCCESS); +} + +/* + * callback functions to display hub devices + */ +/* ARGSUSED */ +static int +print_usb_devices(picl_nodehdl_t hubh, void *arg) +{ + picl_nodehdl_t chdh; + char *rootname; + int type = *(int *)arg; + int hubnum; + int err; + + err = picl_get_propval_by_name(hubh, PICL_PROP_CHILD, &chdh, + sizeof (picl_nodehdl_t)); + + /* print header */ + if (err == PICL_SUCCESS) { + err = picldiag_get_string_propval(hubh, PICL_PROP_NAME, + &rootname); + if (err != PICL_SUCCESS) + return (err); + + if (type == USB) { + log_printf("\n==============================="); + log_printf(dgettext(TEXT_DOMAIN, + " %s Devices "), rootname); + } else { + /* Get its hub number */ + err = picl_get_propval_by_name(hubh, + OBP_PROP_REG, &hubnum, sizeof (hubnum)); + if ((err != PICL_SUCCESS) && + (err != PICL_PROPNOTFOUND)) { + free(rootname); + return (err); + } + + log_printf("\n==============================="); + if (err == PICL_SUCCESS) + log_printf(dgettext(TEXT_DOMAIN, + " %s#%d Devices "), + rootname, hubnum); + else + log_printf(dgettext(TEXT_DOMAIN, + " %s Devices "), rootname); + } + + log_printf("===============================\n\n"); + log_printf("Name Port#\n"); + log_printf("------------ -----\n"); + free(rootname); + + do { + logprintf_hub_devices(chdh); + + err = picl_get_propval_by_name(chdh, PICL_PROP_PEER, + &chdh, sizeof (picl_nodehdl_t)); + } while (err == PICL_SUCCESS); + } + + + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback functions to display usb devices + */ +/* ARGSUSED */ +static int +usb_callback(picl_nodehdl_t usbh, void *args) +{ + int err; + int type; + + type = USB; + err = print_usb_devices(usbh, &type); + if (err != PICL_WALK_CONTINUE) + return (err); + type = HUB; + err = picl_walk_tree_by_class(usbh, NULL, &type, print_usb_devices); + if (err == PICL_SUCCESS) + err = PICL_WALK_CONTINUE; + return (err); +} + + +/* + * find usb devices and print its information + */ +static int +display_usb_devices(picl_nodehdl_t plafh) +{ + int err; + + /* + * get the usb node + */ + err = picl_walk_tree_by_class(plafh, PICL_CLASS_USB, NULL, + usb_callback); + return (err); +} + + + +/* + * If nodeh is the io device, add it into the io list and return + * If it is not an io device and it has the subtree, traverse the subtree + * and add all leaf io devices + */ +static int +add_io_leaves(picl_nodehdl_t nodeh, char *parentname, uint32_t board, + uint32_t bus_id, uint64_t slot, uint32_t freq, char *model, char *status) +{ + picl_nodehdl_t childh; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + int err; + char *nameval; + char piclclass[PICL_CLASSNAMELEN_MAX]; + char nodename[MAXSTRLEN]; + char name[MAXSTRLEN]; + char *devfs_path; + char *compatible; + picl_nodehdl_t fruparenth; + char *label; + char binding_name[MAXSTRLEN]; + + err = picl_get_propinfo_by_name(nodeh, PICL_PROP_NAME, &pinfo, + &proph); + if (err != PICL_SUCCESS) + return (err); + + nameval = alloca(pinfo.size); + if (nameval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(proph, nameval, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + (void) strlcpy(nodename, nameval, MAXSTRLEN); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + /* if binding_name is found, name will be <nodename>-<binding_name> */ + err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME, + binding_name, sizeof (binding_name)); + if (err == PICL_PROPNOTFOUND) { + /* + * if compatible prop is found, name will be + * <nodename>-<compatible> + */ + err = picldiag_get_first_compatible_value(nodeh, &compatible); + if (err == PICL_SUCCESS) { + strlcat(nodename, "-", MAXSTRLEN); + strlcat(nodename, compatible, MAXSTRLEN); + free(compatible); + } else if (err != PICL_PROPNOTFOUND) { + return (err); + } + } else if (err != PICL_SUCCESS) { + return (err); + } else if (strcmp(nodename, binding_name) != 0) { + if (strcmp(nodename, piclclass) == 0) { + /* + * nodename same as binding name - + * no need to display twice + */ + strlcpy(nodename, binding_name, MAXSTRLEN); + } else { + strlcat(nodename, "-", MAXSTRLEN); + strlcat(nodename, binding_name, MAXSTRLEN); + } + } + + /* + * If it is an immediate child under pci/sbus/upa and not + * a bus node, add it to the io list. + * If it is a child under sub-bus and it is in an io + * device, add it to the io list. + */ + if (((parentname == NULL) && (!is_bus(piclclass))) || + ((parentname != NULL) && (is_io_device(piclclass)))) { + if (parentname == NULL) + (void) snprintf(name, MAXSTRLEN, "%s", nodename); + else + (void) snprintf(name, MAXSTRLEN, "%s/%s", parentname, + nodename); + + /* + * append the class if its class is not a generic + * obp-device class + */ + if (strcmp(piclclass, PICL_CLASS_OBP_DEVICE)) + (void) snprintf(name, MAXSTRLEN, "%s (%s)", name, + piclclass); + + err = picldiag_get_fru_parent(nodeh, &fruparenth); + if (err == PICL_PROPNOTFOUND) { + label = NULL; + } else if (err != PICL_SUCCESS) { + return (err); + } else { + err = picldiag_get_combined_label(fruparenth, &label, + 15); + if (err == PICL_PROPNOTFOUND) + label = NULL; + else if (err != PICL_SUCCESS) + return (err); + } + /* devfs-path */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH, + &devfs_path); + if (err == PICL_PROPNOTFOUND) + devfs_path = NULL; + else if (err != PICL_SUCCESS) + return (err); + + add_io_card(board, bus_id, slot, label, freq, name, + model, status, devfs_path); + if (label != NULL) + free(label); + if (devfs_path != NULL) + free(devfs_path); + return (PICL_SUCCESS); + } + + /* + * If there is any child, Go through each child. + */ + + err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD, + &childh, sizeof (picl_nodehdl_t)); + + /* there is a child */ + while (err == PICL_SUCCESS) { + if (parentname == NULL) + (void) strlcpy(name, nodename, MAXSTRLEN); + else + (void) snprintf(name, MAXSTRLEN, "%s/%s", parentname, + nodename); + + err = add_io_leaves(childh, name, board, bus_id, slot, freq, + model, status); + if (err != PICL_SUCCESS) + return (err); + /* + * get next child + */ + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + } + + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + return (err); +} + +/* + * callback function to add all io devices under sbus in io list + */ +/*ARGSUSED*/ +static int +sbus_callback(picl_nodehdl_t sbush, void *args) +{ + picl_nodehdl_t nodeh; + int err; + uint32_t boardnum; + uint32_t bus_id; + uint32_t slot; + uint32_t freq; + char *model; + char *status; + + /* Fill in common infomation */ + bus_id = SBUS_TYPE; + + err = picldiag_get_clock_freq(sbush, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + /* + * If no board# is found, set boardnum to 0 + */ + boardnum = picldiag_get_uint_propval(sbush, OBP_PROP_BOARD_NUM, &err); + if (err == PICL_PROPNOTFOUND) + boardnum = DEFAULT_BOARD_NUM; + else if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(sbush, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + slot = picldiag_get_uint_propval(nodeh, + PICL_PROP_SLOT, &err); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, OBP_PROP_MODEL, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS, + &status); + if (err == PICL_PROPNOTFOUND) { + status = malloc(5); + strncpy(status, "okay", 5); + } else if (err != PICL_SUCCESS) + return (err); + + err = add_io_leaves(nodeh, NULL, boardnum, bus_id, slot, freq, + model, status); + if (model != NULL) + free(model); + if (status != NULL) + free(status); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * add all io devices under pci in io list + */ +/* ARGSUSED */ +static int +pci_callback(picl_nodehdl_t pcih, void *args) +{ + picl_nodehdl_t nodeh; + int err; + char piclclass[PICL_CLASSNAMELEN_MAX]; + uint32_t boardnum; + uint32_t bus_id; + uint32_t slot; + uint32_t freq; + char *model; + char *status; + + /* Fill in common infomation */ + bus_id = PCI_TYPE; + + /* + * Check if it has the freq, if not, + * If not, use its parent's freq + * if its parent's freq is not found, return + */ + err = picldiag_get_clock_freq(pcih, &freq); + if (err == PICL_PROPNOTFOUND) { + err = picldiag_get_clock_from_parent(pcih, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + } else if (err != PICL_SUCCESS) + return (err); + + /* + * If no board# is found, set boardnum to 0 + */ + boardnum = picldiag_get_uint_propval(pcih, OBP_PROP_BOARD_NUM, &err); + if (err == PICL_PROPNOTFOUND) + boardnum = DEFAULT_BOARD_NUM; + else if (err != PICL_SUCCESS) + return (err); + + /* Walk through the children */ + + err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + /* + * Skip PCI bridge and USB devices because they will be + * processed later + */ + if ((strcmp(piclclass, PICL_CLASS_PCI) == 0) || + (strcmp(piclclass, PICL_CLASS_USB) == 0)) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } + + /* Get the device id for pci card */ + slot = picldiag_get_uint_propval(nodeh, + PICL_PROP_DEVICE_ID, &err); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } else if (err != PICL_SUCCESS) + return (err); + + /* Get the model of this card */ + err = picldiag_get_string_propval(nodeh, OBP_PROP_MODEL, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS, + &status); + if (err == PICL_PROPNOTFOUND) { + status = malloc(5); + strncpy(status, "okay", 5); + } else if (err != PICL_SUCCESS) + return (err); + + err = add_io_leaves(nodeh, NULL, boardnum, bus_id, slot, + freq, model, status); + + if (model != NULL) + free(model); + + if (status != NULL) + free(status); + + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + + } + + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + + return (err); +} + +/* + * add io devices in io list + * Its slot number is drived from upa-portid + */ +static int +add_io_devices(picl_nodehdl_t nodeh) +{ + int err; + uint64_t board_type; + char piclclass[PICL_CLASSNAMELEN_MAX]; + char name[MAXSTRLEN]; + char *devfs_path; + char *nameval; + uint32_t boardnum; + uint32_t bus_id; + uint32_t slot; + uint32_t freq; + char *model; + char *status; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_nodehdl_t fruparenth; + char *label; + + + bus_id = UPA_TYPE; + + /* + * If clock frequency can't be found from its parent, don't add + */ + err = picldiag_get_clock_from_parent(nodeh, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + else if (err != PICL_SUCCESS) + return (err); + + /* + * If no board# is found, set boardnum to 0 + */ + boardnum = picldiag_get_uint_propval(nodeh, OBP_PROP_BOARD_NUM, &err); + if (err == PICL_PROPNOTFOUND) + boardnum = DEFAULT_BOARD_NUM; + else if (err != PICL_SUCCESS) + return (err); + + /* + * get upa portid as slot number + * If upa portid is not found, don't add the card. + */ + slot = picldiag_get_uint_propval(nodeh, OBP_PROP_UPA_PORTID, + &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + else if (err != PICL_SUCCESS) + return (err); + + /* Get the model of this card */ + err = picldiag_get_string_propval(nodeh, OBP_PROP_MODEL, &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* + * check if it is a ffb device + * If it's a ffb device, append its board type to name + * otherwise, use its nodename + */ + err = picl_get_prop_by_name(nodeh, PICL_PROP_FFB_BOARD_REV, &proph); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propinfo_by_name(nodeh, PICL_PROP_NAME, + &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + nameval = alloca(pinfo.size); + if (nameval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(proph, nameval, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + (void) strlcpy(name, nameval, MAXSTRLEN); + } else if (err == PICL_SUCCESS) { + /* Find out if it's single or double buffered */ + board_type = picldiag_get_uint_propval(nodeh, + OBP_PROP_BOARD_TYPE, &err); + if (err == PICL_PROPNOTFOUND) + (void) strlcpy(name, FFB_NAME, sizeof (name)); + if (err == PICL_SUCCESS) { + if (board_type & FFB_B_BUFF) + (void) strlcpy(name, FFB_DOUBLE_BUF, + sizeof (name)); + else + (void) strlcpy(name, FFB_SINGLE_BUF, + sizeof (name)); + } else + return (err); + } else + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + (void) snprintf(name, sizeof (name), "%s (%s)", name, piclclass); + + err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS, &status); + if (err == PICL_PROPNOTFOUND) { + status = malloc(5); + strncpy(status, "okay", 5); + } else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_fru_parent(nodeh, &fruparenth); + if (err == PICL_PROPNOTFOUND) { + label = NULL; + } else if (err != PICL_SUCCESS) { + return (err); + } else { + err = picldiag_get_combined_label(fruparenth, &label, 15); + if (err == PICL_PROPNOTFOUND) + label = NULL; + else if (err != PICL_SUCCESS) + return (err); + } + /* devfs-path */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH, + &devfs_path); + if (err == PICL_PROPNOTFOUND) + devfs_path = NULL; + else if (err != PICL_SUCCESS) + return (err); + + add_io_card(boardnum, bus_id, slot, label, freq, name, model, status, + devfs_path); + if (label != NULL) + free(label); + if (model != NULL) + free(model); + if (status != NULL) + free(status); + if (devfs_path != NULL) + free(devfs_path); + + return (PICL_SUCCESS); +} + +/* + * loop through all children and add io devices in io list + */ +static int +process_io_leaves(picl_nodehdl_t rooth) +{ + picl_nodehdl_t nodeh; + char classval[PICL_CLASSNAMELEN_MAX]; + int err; + + err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + classval, sizeof (classval)); + if (err != PICL_SUCCESS) + return (err); + + if (is_io_device(classval)) + err = add_io_devices(nodeh); + + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + } + + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + + return (err); +} + +/* + * callback function to add all io devices under upa in io list + */ +/*ARGSUSED*/ +static int +upa_callback(picl_nodehdl_t upah, void *args) +{ + int err; + + err = process_io_leaves(upah); + + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * display ffb hardware configuration + */ +/* ARGSUSED */ +static int +ffbconfig_callback(picl_nodehdl_t ffbh, void *arg) +{ + int err; + uint64_t board_rev; + uint64_t fbc_ver; + char *dac_ver; + char *fbram_ver; + + /* + * If it has PICL_PROP_FFB_BOARD_REV, it is a ffb device + * Otherwise, return. + */ + board_rev = picldiag_get_uint_propval(ffbh, PICL_PROP_FFB_BOARD_REV, + &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + log_printf("FFB Hardware Configuration:\n"); + log_printf("-----------------------------------\n"); + log_printf("Board rev: %lld\n", board_rev); + + fbc_ver = picldiag_get_uint_propval(ffbh, OBP_PROP_FBC_REG_ID, + &err); + if (err == PICL_SUCCESS) + log_printf("FBC version: 0x%llx\n", fbc_ver); + else if (err != PICL_PROPNOTFOUND) + return (err); + + err = picldiag_get_string_propval(ffbh, PICL_PROP_FFB_DAC_VER, + &dac_ver); + if (err == PICL_SUCCESS) { + log_printf("DAC: %s\n", dac_ver); + free(dac_ver); + } else if (err != PICL_PROPNOTFOUND) + return (err); + + err = picldiag_get_string_propval(ffbh, PICL_PROP_FFB_FBRAM_VER, + &fbram_ver); + if (err == PICL_SUCCESS) { + log_printf("3DRAM: %s\n", fbram_ver); + free(fbram_ver); + } else if (err != PICL_PROPNOTFOUND) + return (err); + + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +/* + * find all io devices and add them in the io list + */ +static int +gather_io_cards(picl_nodehdl_t plafh) +{ + int err; + + /* + * look for io devices under the immediate children of platform + */ + err = process_io_leaves(plafh); + + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_SBUS, + PICL_CLASS_SBUS, sbus_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_PCI, + PICL_CLASS_PCI, pci_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_UPA, + PICL_CLASS_UPA, upa_callback); + return (err); +} + +static void +picldiag_display_io_cards(struct io_card *list) +{ + static int banner = 0; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + if (banner == 0) { + log_printf("Bus Freq Slot + Name +\n", 0); + log_printf("Type MHz Status " + "Path " + "Model", 0); + log_printf("\n", 0); + log_printf("---- ---- ---------- " + "---------------------------- " + "--------------------", 0); + log_printf("\n", 0); + banner = 1; + } + + for (p = list; p != NULL; p = p -> next) { + log_printf("%-4s ", p->bus_type, 0); + log_printf("%3d ", p->freq, 0); + /* + * We check to see if it's an int or + * a char string to display for slot. + */ + if (p->slot == PCI_SLOT_IS_STRING) + log_printf("%10s ", p->slot_str, 0); + else + log_printf("%10d ", p->slot, 0); + + log_printf("%-28.28s", p->name, 0); + if (strlen(p->name) > 28) + log_printf("+ ", 0); + else + log_printf(" ", 0); + log_printf("%-19.19s", p->model, 0); + if (strlen(p->model) > 19) + log_printf("+", 0); + log_printf("\n", 0); + log_printf(" %10s ", p->status, 0); + if (strlen(p->notes) > 0) + log_printf("%s", p->notes, 0); + log_printf("\n\n", 0); + } +} + +/* + * display all io devices + */ +static int +display_io_device_info(picl_nodehdl_t plafh) +{ + int err; + + err = gather_io_cards(plafh); + if (err != PICL_SUCCESS) + return (err); + + logprintf_header(dgettext(TEXT_DOMAIN, "IO Devices"), + DEFAULT_LINE_WIDTH); + + picldiag_display_io_cards(io_card_list); + + free_io_cards(io_card_list); + + return (PICL_SUCCESS); +} + +/* + * print fan device information + */ +static int +logprintf_fan_info(picl_nodehdl_t fanh) +{ + int err; + char *label; + char *unit; + int64_t speed; + int64_t min_speed; + picl_nodehdl_t fruph; + + err = picldiag_get_fru_parent(fanh, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 14); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-14s ", label); + free(label); + + err = picldiag_get_label(fanh, &label); + if (err == PICL_SUCCESS) { + log_printf("%-14s ", label); + free(label); + } else if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf(" - "); + } else + return (err); + + speed = picldiag_get_uint_propval(fanh, PICL_PROP_FAN_SPEED, &err); + if (err == PICL_SUCCESS) { + min_speed = picldiag_get_uint_propval(fanh, + PICL_PROP_LOW_WARNING_THRESHOLD, &err); + if (err != PICL_SUCCESS) + min_speed = 1; + if (speed < min_speed) { + log_printf("failed (%lld", speed); + err = picldiag_get_string_propval(fanh, + PICL_PROP_FAN_SPEED_UNIT, &unit); + if (err == PICL_SUCCESS) { + log_printf("%s", unit); + free(unit); + } + log_printf(")"); + } else { + log_printf("okay"); + } + } else { + err = picldiag_get_string_propval(fanh, + PICL_PROP_FAN_SPEED_UNIT, &unit); + if (err == PICL_SUCCESS) { + log_printf("%-12s ", unit); + free(unit); + } + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +fan_callback(picl_nodehdl_t fanh, void *arg) +{ + int *countp = arg; + int err; + + if (*countp == 0) { + log_printf(dgettext(TEXT_DOMAIN, "Fan Status:\n")); + log_printf("---------------------------------------\n"); + log_printf("Location Sensor Status \n"); + log_printf("---------------------------------------\n"); + } + *countp += 1; + err = logprintf_fan_info(fanh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find fan device and print its speed + */ +static int +display_fan_speed(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_FAN, + &print_header, fan_callback); + return (err); +} + +/* + * print temperature sensor information + */ +static int +logprintf_temp_info(picl_nodehdl_t temph) +{ + int err; + char *label; + int64_t temperature; + int64_t threshold; + picl_nodehdl_t fruph; + char *status = "unknown"; + int got_temp = 0; + + err = picldiag_get_fru_parent(temph, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 14); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-14s ", label); + free(label); + + err = picldiag_get_label(temph, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-14s ", label); + free(label); + + temperature = picldiag_get_int_propval(temph, PICL_PROP_TEMPERATURE, + &err); + if (err == PICL_SUCCESS) { + got_temp = 1; + status = "okay"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_LOW_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature < threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_LOW_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature < threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_HIGH_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature > threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_HIGH_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature > threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + err = picldiag_get_string_propval(temph, PICL_PROP_CONDITION, &status); + if (err == PICL_SUCCESS) { + log_printf("%s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } else { + log_printf("%s ", status); + if (strcmp(status, "failed") == 0 || + strcmp(status, "warning") == 0) + log_printf("(%.2lldC)", temperature); + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +temp_callback(picl_nodehdl_t temph, void *arg) +{ + int err; + int *countp = arg; + + if (*countp == 0) { + log_printf("\n"); + log_printf("---------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Temperature sensors:\n")); + log_printf("------------------------------------\n"); + log_printf("Location Sensor Status\n"); + log_printf("------------------------------------\n"); + } + *countp += 1; + err = logprintf_temp_info(temph); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find temp sensors and print the temp + */ +/* ARGSUSED */ +static int +display_temp(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_TEMPERATURE_SENSOR, + &print_header, temp_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_TEMPERATURE_INDICATOR, + &print_header, temp_callback); + return (err); +} + +/* + * print current sensor information + */ +static int +logprintf_current_info(picl_nodehdl_t currenth) +{ + int err; + char *label; + float current; + float threshold; + picl_nodehdl_t fruph; + char *status = "unknown"; + int got_current = 0; + + err = picldiag_get_fru_parent(currenth, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 10); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-10s ", label); + free(label); + + err = picldiag_get_label(currenth, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-10s ", label); + free(label); + + current = picldiag_get_float_propval(currenth, PICL_PROP_CURRENT, &err); + if (err == PICL_SUCCESS) { + status = "okay"; + got_current = 1; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, PICL_PROP_LOW_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_current && current < threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, PICL_PROP_LOW_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_current && current < threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, PICL_PROP_HIGH_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_current && current > threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, + PICL_PROP_HIGH_SHUTDOWN, &err); + if (err == PICL_SUCCESS) { + if (got_current && current > threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + err = picldiag_get_string_propval(currenth, + PICL_PROP_CONDITION, &status); + if (err == PICL_SUCCESS) { + log_printf(" %s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } else { + log_printf("%s ", status); + if (strcmp(status, "failed") == 0 || + strcmp(status, "warning") == 0) + log_printf("(%.2fA)", current); + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +current_callback(picl_nodehdl_t currh, void *arg) +{ + int err; + int *countp = arg; + + if (*countp == 0) { + log_printf("------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Current sensors:\n")); + log_printf("------------------------------\n"); + log_printf("Location Sensor Status\n"); + log_printf("------------------------------\n"); + } + *countp += 1; + err = logprintf_current_info(currh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find curr sensors and print the curr + */ +/* ARGSUSED */ +static int +display_current(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_CURRENT_SENSOR, + &print_header, current_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_CURRENT_INDICATOR, + &print_header, current_callback); + return (err); +} + +/* + * print voltage sensor information + */ +static int +logprintf_voltage_info(picl_nodehdl_t voltageh) +{ + int err; + char *label; + float voltage; + float threshold; + picl_nodehdl_t fruph; + char *status = "unknown"; + int got_voltage = 0; + + err = picldiag_get_fru_parent(voltageh, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 10); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-10s ", label); + free(label); + + err = picldiag_get_label(voltageh, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-12s ", label); + free(label); + + voltage = picldiag_get_float_propval(voltageh, PICL_PROP_VOLTAGE, &err); + if (err == PICL_SUCCESS) { + status = "okay"; + got_voltage = 1; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, PICL_PROP_LOW_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage < threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, PICL_PROP_LOW_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage < threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, PICL_PROP_HIGH_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage > threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, + PICL_PROP_HIGH_SHUTDOWN, &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage > threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + err = picldiag_get_string_propval(voltageh, + PICL_PROP_CONDITION, &status); + if (err == PICL_SUCCESS) { + log_printf("%s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } else { + log_printf("%s ", status); + if (strcmp(status, "warning") == 0 || + strcmp(status, "failed") == 0) + log_printf("(%.2fV)", voltage); + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +voltage_callback(picl_nodehdl_t voltageh, void *arg) +{ + int *countp = arg; + int err; + + if (*countp == 0) { + log_printf("--------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Voltage sensors:\n")); + log_printf("-------------------------------\n"); + log_printf("Location Sensor Status\n"); + log_printf("-------------------------------\n"); + } + *countp += 1; + err = logprintf_voltage_info(voltageh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find voltage sensors and print voltage + */ +/* ARGSUSED */ +static int +display_voltage(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_VOLTAGE_SENSOR, + &print_header, voltage_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_VOLTAGE_INDICATOR, + &print_header, voltage_callback); + return (err); +} + +/* + * print led device information + */ +static int +logprintf_led_info(picl_nodehdl_t ledh) +{ + int err; + char *label; + char *state; + char *color; + picl_nodehdl_t fruph; + + err = picldiag_get_fru_parent(ledh, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 10); + if (err != PICL_SUCCESS) { + log_printf(" - ", label); + } else { + log_printf("%-10s ", label); + free(label); + } + + err = picldiag_get_label(ledh, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-20s ", label); + free(label); + + err = picldiag_get_string_propval(ledh, PICL_PROP_STATE, &state); + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf(" - "); + } else if (err != PICL_SUCCESS) { + return (err); + } else { + log_printf("%-10s ", state); + free(state); + } + + err = picldiag_get_string_propval(ledh, PICL_PROP_COLOR, &color); + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf("\n"); + } else if (err != PICL_SUCCESS) { + return (err); + } else { + log_printf("%-16s\n", color); + free(color); + } + + return (PICL_SUCCESS); +} + +static int +led_callback(picl_nodehdl_t ledh, void *arg) +{ + int *countp = arg; + int err; + + if (*countp == 0) { + + log_printf("--------------------------------------" + "------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Led State:\n")); + log_printf("--------------------------------------" + "------------\n"); + log_printf("Location Led State" + " Color\n"); + log_printf("--------------------------------------" + "------------\n"); + } + *countp += 1; + err = logprintf_led_info(ledh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find led devices and print status + */ +/* ARGSUSED */ +static int +display_led_status(picl_nodehdl_t plafh) +{ + int print_header; + + print_header = 0; + picl_walk_tree_by_class(plafh, PICL_CLASS_LED, + &print_header, led_callback); + return (PICL_SUCCESS); +} + +/* + * print keyswitch device information + */ +static int +logprintf_keyswitch_info(picl_nodehdl_t keyswitchh, picl_nodehdl_t fruph) +{ + int err; + char *label; + char *state; + + err = picldiag_get_combined_label(fruph, &label, 10); + if (err != PICL_SUCCESS) { + log_printf("%-14s", " -"); + } else { + log_printf("%-14s ", label); + free(label); + } + + err = picldiag_get_label(keyswitchh, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-11s ", label); + free(label); + + err = picldiag_get_string_propval(keyswitchh, PICL_PROP_STATE, &state); + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf(" -\n"); + } else if (err != PICL_SUCCESS) { + return (err); + } else { + log_printf("%s\n", state); + free(state); + } + + return (PICL_SUCCESS); +} + +static int +keyswitch_callback(picl_nodehdl_t keyswitchh, void *arg) +{ + int *countp = arg; + int err; + picl_nodehdl_t fruph; + + /* + * Tamale simulates a key-switch on ENxS. So the presence of a + * node of class keyswitch is not sufficient. If it has a fru parent + * or location parent, then believe it. + */ + err = picl_get_propval_by_name(keyswitchh, PICL_REFPROP_FRU_PARENT, + &fruph, sizeof (fruph)); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(keyswitchh, + PICL_REFPROP_LOC_PARENT, &fruph, sizeof (fruph)); + } + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) + return (PICL_WALK_CONTINUE); + if (err != PICL_SUCCESS) + return (err); + + if (*countp == 0) { + log_printf("-----------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Keyswitch:\n")); + log_printf("-----------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Location Keyswitch State\n")); + log_printf("-----------------------------------------\n"); + } + *countp += 1; + err = logprintf_keyswitch_info(keyswitchh, fruph); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * search children to find keyswitch device(s) and print status + */ +/* ARGSUSED */ +static int +display_keyswitch(picl_nodehdl_t plafh) +{ + int print_header = 0; + + picl_walk_tree_by_class(plafh, PICL_CLASS_KEYSWITCH, + &print_header, keyswitch_callback); + return (PICL_SUCCESS); +} + +/* + * display environment status + */ +static int +display_envctrl_status(picl_nodehdl_t plafh) +{ + logprintf_header(dgettext(TEXT_DOMAIN, "Environmental Status"), + DEFAULT_LINE_WIDTH); + + display_fan_speed(plafh); + display_temp(plafh); + display_current(plafh); + display_voltage(plafh); + display_keyswitch(plafh); + display_led_status(plafh); + + return (PICL_SUCCESS); +} + +/* + * print fru operational status + */ +static int +logprintf_fru_oper_status(picl_nodehdl_t fruh, int *countp) +{ + int err; + char *label; + char *status; + + err = picldiag_get_combined_label(fruh, &label, 15); + if (err != PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + + err = picldiag_get_string_propval(fruh, + PICL_PROP_OPERATIONAL_STATUS, &status); + if (err == PICL_SUCCESS) { + if (*countp == 0) { + logprintf_header(dgettext(TEXT_DOMAIN, + "FRU Operational Status"), + DEFAULT_LINE_WIDTH); + log_printf("-------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Fru Operational Status:\n")); + log_printf("-------------------------\n"); + log_printf("Location Status \n"); + log_printf("-------------------------\n"); + } + *countp += 1; + log_printf("%-15s ", label); + free(label); + log_printf("%s\n", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + free(label); + return (err); + } else { + free(label); + } + return (PICL_WALK_CONTINUE); +} + +static int +fru_oper_status_callback(picl_nodehdl_t fruh, void *arg) +{ + int err; + + err = logprintf_fru_oper_status(fruh, (int *)arg); + return (err); +} + +/* + * display fru operational status + */ +static int +display_fru_oper_status(picl_nodehdl_t frutreeh) +{ + int print_header; + + print_header = 0; + picl_walk_tree_by_class(frutreeh, PICL_CLASS_FRU, + &print_header, fru_oper_status_callback); + return (PICL_SUCCESS); +} + +/* + * check if the node having the version prop + * If yes, print its nodename and version + */ +/* ARGSUSED */ +static int +asicrev_callback(picl_nodehdl_t nodeh, void *arg) +{ + uint32_t version; + char *name; + char *model; + char *status; + int err; + + version = picldiag_get_uint_propval(nodeh, OBP_PROP_VERSION_NUM, + &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + /* devfs-path */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH, &name); + if (err == PICL_PROPNOTFOUND) + name = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* model */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_BINDING_NAME, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* status */ + err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS, &status); + if (err == PICL_PROPNOTFOUND) + status = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* + * Display the data + */ + + /* name */ + if (name != NULL) { + log_printf("%-22s ", name); + free(name); + } else + log_printf("%-22s ", "unknown"); + /* model */ + if (model != NULL) { + log_printf("%-15s ", model); + free(model); + } else + log_printf("%-15s ", "unknown"); + /* status */ + if (status == NULL) + log_printf("%-15s ", "okay"); + else { + log_printf("%-15s ", status); + free(status); + } + /* revision */ + log_printf(" %-4d\n", version); + + return (PICL_WALK_CONTINUE); +} + +/* + * traverse the tree to display asic revision id for ebus + */ +/* ARGSUSED */ +static int +ebus_callback(picl_nodehdl_t ebush, void *arg) +{ + uint32_t id; + char *name; + int err; + char *model; + char *status; + + id = picldiag_get_uint_propval(ebush, OBP_PROP_REVISION_ID, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + /* devfs-path */ + err = picldiag_get_string_propval(ebush, PICL_PROP_DEVFS_PATH, &name); + if (err == PICL_PROPNOTFOUND) + name = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* model */ + err = picldiag_get_string_propval(ebush, PICL_PROP_BINDING_NAME, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* status */ + err = picldiag_get_string_propval(ebush, OBP_PROP_STATUS, &status); + if (err == PICL_PROPNOTFOUND) + status = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* + * Display the data + */ + + /* name */ + if (name != NULL) { + log_printf("%-22s ", name); + free(name); + } else + log_printf("%-22s ", "unknown"); + /* model */ + if (model != NULL) { + log_printf("%-15s ", model); + free(model); + } else + log_printf("%-15s ", "unknown"); + /* status */ + if (status == NULL) + log_printf("%-15s ", "okay"); + else { + log_printf("%-15s ", status); + free(status); + } + /* revision */ + log_printf(" %-4d\n", id); + + return (PICL_WALK_CONTINUE); +} + +/* + * display asic revision id + */ +static int +display_hw_revisions(picl_nodehdl_t plafh) +{ + int err; + + /* Print the header */ + logprintf_header(dgettext(TEXT_DOMAIN, "HW Revisions"), + DEFAULT_LINE_WIDTH); + + log_printf("ASIC Revisions:\n"); + log_printf("-----------------------------"); + log_printf("--------------------------------------\n"); + log_printf("Path Device"); + log_printf(" Status Revision\n"); + log_printf("-----------------------------"); + log_printf("--------------------------------------\n"); + + err = picl_walk_tree_by_class(plafh, NULL, NULL, asicrev_callback); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_EBUS, + NULL, ebus_callback); + if (err != PICL_SUCCESS) + return (err); + + log_printf("\n"); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_DISPLAY, + NULL, ffbconfig_callback); + return (err); +} + +/* + * find the options node and its powerfail_time prop + * If found, display the list of latest powerfail. + */ +/* ARGSUSED */ +static int +options_callback(picl_nodehdl_t nodeh, void *arg) +{ + time_t value; + char *failtime; + int err; + + err = picldiag_get_string_propval(nodeh, PROP_POWERFAIL_TIME, + &failtime); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_TERMINATE); + else if (err != PICL_SUCCESS) + return (err); + + value = (time_t)atoi(failtime); + free(failtime); + if (value == 0) + return (PICL_WALK_TERMINATE); + + log_printf(dgettext(TEXT_DOMAIN, "Most recent AC Power Failure:\n")); + log_printf("=============================\n"); + log_printf("%s", ctime(&value)); + log_printf("\n"); + return (PICL_WALK_TERMINATE); +} + +/* + * display the OBP and POST prom revisions + */ +/* ARGSUSED */ +static int +flashprom_callback(picl_nodehdl_t flashpromh, void *arg) +{ + picl_prophdl_t proph; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + picl_propinfo_t pinfo; + char *prom_version = NULL; + char *obp_version = NULL; + int err; + + err = picl_get_propinfo_by_name(flashpromh, OBP_PROP_VERSION, + &pinfo, &proph); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_TERMINATE); + else if (err != PICL_SUCCESS) + return (err); + + log_printf(dgettext(TEXT_DOMAIN, "System PROM revisions:\n")); + log_printf("----------------------\n"); + + /* + * If it's a table prop, the first element is OBP revision + * The second one is POST revision. + * If it's a charstring prop, the value will be only OBP revision + */ + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + prom_version = alloca(pinfo.size); + if (prom_version == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, prom_version, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + log_printf("%s\n", prom_version); + } + + if (pinfo.type != PICL_PTYPE_TABLE) /* not supported type */ + return (PICL_WALK_TERMINATE); + + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err == PICL_SUCCESS) { + /* get first row */ + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + prom_version = alloca(pinfo.size); + if (prom_version == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, prom_version, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + log_printf("%s\n", prom_version); + + /* get second row */ + err = picl_get_next_by_col(rowproph, &rowproph); + if (err == PICL_SUCCESS) { + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + obp_version = alloca(pinfo.size); + if (obp_version == NULL) + return (PICL_FAILURE); + err = picl_get_propval(rowproph, obp_version, + pinfo.size); + if (err != PICL_SUCCESS) + return (err); + log_printf("%s\n", obp_version); + } + } + + return (PICL_WALK_TERMINATE); +} + +static int +display_system_info(int serrlog, int log_flag, picl_nodehdl_t rooth) +{ + int err; + picl_nodehdl_t plafh; + picl_nodehdl_t frutreeh; + + err = picldiag_get_node_by_name(rooth, PICL_NODE_PLATFORM, &plafh); + if (err != PICL_SUCCESS) + return (err); + + if (!log_flag) { + err = display_platform_banner(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_system_clock(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY, + PICL_CLASS_MEMORY, memory_callback); + if (err != PICL_SUCCESS) + return (err); + + err = display_cpu_info(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_io_device_info(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_memory_config(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_usb_devices(plafh); + if (err != PICL_SUCCESS) + return (err); + } + + if (serrlog) { + err = picl_walk_tree_by_class(rooth, PICL_CLASS_OPTIONS, + NULL, options_callback); + if (err != PICL_SUCCESS) + return (err); + + err = display_envctrl_status(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_node_by_name(rooth, PICL_NODE_FRUTREE, + &frutreeh); + if (err != PICL_SUCCESS) + return (err); + + err = display_fru_oper_status(frutreeh); + if (err != PICL_SUCCESS) + return (err); + + err = display_hw_revisions(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_FLASHPROM, + NULL, flashprom_callback); + if (err != PICL_SUCCESS) + return (err); + } + + return (PICL_SUCCESS); +} + +/* ARGSUSED */ +int +do_prominfo(int serrlog, char *pgname, int log_flag, int prt_flag) +{ + int err; + char *errstr; + int done; + picl_nodehdl_t rooth; + + err = picl_initialize(); + if (err != PICL_SUCCESS) { + fprintf(stderr, EM_INIT_FAIL, picl_strerror(err)); + exit(1); + } + + do { + done = 1; + err = picl_get_root(&rooth); + if (err != PICL_SUCCESS) { + fprintf(stderr, EM_GET_ROOT_FAIL, picl_strerror(err)); + exit(1); + } + + err = display_system_info(serrlog, log_flag, rooth); + + if ((err == PICL_STALEHANDLE) || (err == PICL_INVALIDHANDLE)) + done = 0; + } while (!done); + + if (err != PICL_SUCCESS) { + errstr = picl_strerror(err); + fprintf(stderr, EM_PRTDIAG_FAIL); + fprintf(stderr, "%s\n", errstr? errstr : " "); + } + + (void) picl_shutdown(); + + return (0); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/lw8/picl/Makefile b/usr/src/lib/libprtdiag_psr/sparc/lw8/picl/Makefile new file mode 100644 index 0000000000..4ea8b5565c --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/lw8/picl/Makefile @@ -0,0 +1,87 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/desktop/picl/Makefile + +UTSBASE = $(SRC)/uts + +PLATFORM_OBJECTS= lw8.o + +objs/%.o pics/%.o: ../common/%.c + $(COMPILE.c) $(IFLAGS) -o $@ $< + $(POST_PROCESS_O) + +include $(SRC)/lib/libprtdiag_psr/sparc/Makefile.com + +SRCS= $(OBJECTS:%.o=../common/%.c) + +LDLIBS += -lpicl + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../../libprtdiag/inc +IFLAGS += -I$(SRC)/cmd/picl/plugins/inc +LINTFLAGS += $(IFLAGS) + +PLATFORM= SUNW,Netra-T12 + +.KEEP_STATE: + +PLATLIBS= $(PLATFORM:%=$(USR_PLAT_DIR)/%/lib/) + +install: all $(PLATLIBS) $(USR_PSM_LIBS) + +# +# install rules for SUNW,Sun-Blade-100/lib/libprtdiag_psr.so.1 +# +$(PLATLIBS): + $(INS.dir) + +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +$(USR_PSM_LIB_DIR): + $(INS.dir) + +# +# Rules for making message files +# +POFILE= libprtdiag_psr_lw8_picl.po +POFILES= lw8.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../common/lw8.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po + diff --git a/usr/src/lib/libprtdiag_psr/sparc/montecarlo/Makefile b/usr/src/lib/libprtdiag_psr/sparc/montecarlo/Makefile new file mode 100644 index 0000000000..b5e733ffb2 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/montecarlo/Makefile @@ -0,0 +1,104 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/montecarlo/Makefile + +LIBBASE = ../../../../../src/lib +UTSBASE = ../../../../uts +PLATFORM_OBJECTS= montecarlo.o + +include ../Makefile.com + +IFLAGS = -I$(USR_PLAT_DIR)/sun4u/include -I../../../libprtdiag/inc -I$(LIBBASE)/libdevinfo +IFLAGS += -I$(UTSBASE)/sun4u/sys +IFLAGS += -I$(UTSBASE)/sun4u/montecarlo/sys +LINTFLAGS += $(IFLAGS) +LDLIBS += -L$(LIBBASE)/libdevinfo -ldevinfo -L$(LIBBASE)/libcfgadm -lcfgadm \ + -lkstat +# +# links in /usr/platform +# +LINKED_PLATFORMS = SUNW,UltraSPARC-IIe-NetraCT-40 + +LINKED_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%) +LINKED_LIB_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib) +LINKED_PRTDIAG_DIRS = \ + $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib/libprtdiag_psr.so.1) + +# +# SUNW,UltraSPARC-IIi-Netract and SUNW,UltraSPARC-IIe-NetraCT-40 platform +# should install into SUNW,UltraSPARC-IIi-Netract. +# SUNW,UltraSPARC-IIe-NetraCT-40 platform can link to +# /usr/platform/SUNW,UltraSPARC-IIi-Netract/lib/libprtdiag_psr.so +# +PLATFORM=SUNW,UltraSPARC-IIi-Netract + +.KEEP_STATE: + +PLATLIBS= $(PLATFORM:%=$(USR_PLAT_DIR)/%/lib/) + +install: all $(PLATLIBS) $(USR_PSM_LIBS) \ + $(LINKED_PRTDIAG_DIRS) + +# +# install rules for SUNW,UltraSPARC-IIi-Netract/lib/libprtdiag_psr.so +# +$(PLATLIBS): + $(INS.dir) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/montecarlo; $(MAKE) $(USR_PSM_LIB_DIR) + +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +$(LINKED_DIRS): $(USR_PLAT_DIR) + -$(INS.dir.root.sys) + +$(LINKED_LIB_DIRS): $(LINKED_DIRS) + -$(INS.dir.root.sys) + +$(LINKED_PRTDIAG_DIRS): $(LINKED_LIB_DIRS) + -$(INS.slink6) + + +# New additions to generate msg file +POFILE = libprtdiag_psr_montecarlo.po +POFILES = montecarlo.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/montecarlo.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/sparc/montecarlo/common/montecarlo.c b/usr/src/lib/libprtdiag_psr/sparc/montecarlo/common/montecarlo.c new file mode 100644 index 0000000000..317235c2c6 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/montecarlo/common/montecarlo.c @@ -0,0 +1,2454 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2000-2001 by Sun Microsystems, Inc. + * All rights reserved. + * + * Netract Platform specific functions. + * + * called when : + * machine_type == MTYPE_MONTECARLO + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* includes */ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <stropts.h> +#include <fcntl.h> +#include <kvm.h> +#include <kstat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <sys/sunddi.h> +#include <sys/ddi_impldefs.h> +#include <sys/devinfo_impl.h> +#include <sys/ioccom.h> +#include <sys/systeminfo.h> +#include <libintl.h> +#include <config_admin.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" +#include "libdevinfo.h" + +/* MC specific header, might just include from MC space */ +#include "mct_topology.h" +#include "envctrl_gen.h" +#include "pcf8574_nct.h" +#include "netract_gen.h" +#include "hscimpl.h" +#include "scsbioctl.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +/* globals */ +#define MAXNAMESZ 128 +#define MAX_NODE_NAME_SZ 32 + +/* this values equates to Max Tree depth for now */ +#define MAXIMUM_DEVS 64 + +typedef char device_info_t[MAX_NODE_NAME_SZ]; + +typedef struct { + cfga_list_data_t *ldatap; + int req; /* If set, this list_data was requested by user */ +} ap_out_t; + + +typedef struct { + uint_t slot_addr; + uint_t slot_stat; + uint_t slot_cond; + device_info_t devs_info[MAXIMUM_DEVS]; + uint_t number_devs; +} mc_slot_info_t; + +typedef struct { + mc_slot_info_t mc_slot_info[MC_MAX_SLOTS]; +} slot_data_t; + + +extern char *progname; +extern int print_flag; + +/* These are used to store all force loads of the drivers */ +static int ps_fd[MC_MAX_PS]; +static int oprom_fd; +static int slot_index = 0; +static int idx_minuscpu = 0; +static int num_devs = 0; +static int sd_instances[MC_MAX_SLOTS*15]; +static int gpio_instances[MC_MAX_PS+MC_MAX_FAN]; +static int sd_count = 0; +static int st_instance; +static int gpio_count = 0; +static int slot_table_not_found = 0; + +/* default not present */ +static int alarm_card_present = 0; +static int cpu_ftm_present = 0; + +/* + * We will store all kstat in globals so that + * we can browse thru them later + */ +static int fail_syssoft_prop = 0; +static int fail_drv_prop = 0; +di_node_t rootnode; /* root nexus */ +slot_data_t mc_slots_data; + +/* scsb driver kstats */ +scsb_ks_leddata_t scsb_ks_leddata; +scsb_ks_state_t scsb_ks_state; +mct_topology_t scsb_ks_topo; + +/* pcf8574(gpio) driver kstats */ +envctrl_cpuvoltage_t pcf8574_ks_cpuv; +envctrl_pwrsupp_t pcf8574_ks_ps1; +envctrl_fantray_t pcf8574_ks_fant1; +envctrl_pwrsupp_t pcf8574_ks_ps2; +envctrl_fantray_t pcf8574_ks_fant2; + +/* pcf8591(adc-dac) driver kstats */ +envctrl_temp_t pcf8591_ks_temp; + +hsc_slot_table_t hotswap_slot_table[MC_MAX_SLOTS]; +hsc_prom_slot_table_t prom_slot_table[MC_MAX_SLOTS]; + +static char *hotswap_mode = NULL; +static char *slot_auto_config[MC_MAX_SLOTS]; +static int slot_table_size; + +/* + * use this to ascertain what's the system, + * default is tonga, we can add more for future variations + * 0=tonga, 1=montecarlo + * we need also to figure out what the system version is + * 0 = 1.5, 1 = 1.0, 0.6 etc. + */ +int montecarlo = 0; +int version_p15_and_p20 = 0; + +#define MAX_PRTDIAG_INFO_LENGTH 1024 +#define MAX_PRTDIAG_FRUS 22 +#define BIT_TEST(X, N) ((X) & (1 << (N))) +#define SLOT1_OK_BIT 0 +#define SLOT2_OK_BIT 1 +#define SLOT3_OK_BIT 2 +#define SLOT4_OK_BIT 3 +#define SLOT5_OK_BIT 4 +#define SLOT6_OK_BIT 5 +#define SLOT7_OK_BIT 6 +#define SLOT8_OK_BIT 7 +#define PDU1_OK_BIT SLOT2_OK_BIT +#define PDU2_OK_BIT SLOT4_OK_BIT +#define FTM_OK_BIT SLOT5_OK_BIT +#define SCB_OK_BIT SLOT6_OK_BIT +#define FAN1_OK_BIT SLOT1_OK_BIT +#define FAN2_OK_BIT SLOT2_OK_BIT +#define DISK1_OK_BIT SLOT4_OK_BIT +#define DISK2_OK_BIT SLOT5_OK_BIT +#define DISK3_OK_BIT SLOT6_OK_BIT +#define PS1_OK_BIT SLOT7_OK_BIT +#define PS2_OK_BIT SLOT8_OK_BIT +#define S_FREE(x) (((x) != NULL) ? (free(x), (x) = NULL) : (void *)0) +#define ENVC_DEBUG_MODE 0x03 +#define OPENPROMDEV "/dev/openprom" +#define I2C_PCF8591_NAME "adc-dac" +#define I2C_KSTAT_CPUTEMP "adc_temp" +#define SCSB_DEV "scsb" +#define SDERR "sderr" +#define STERR "sterr" +#define OK "ok" +#define NOK "Not ok" +#define ON "on" +#define OFF "off" +#define BLINK "blink" +#define NA "Not Available" +#define UK "Unknown " +#define YES "Yes" +#define NO "No " +#define LO "low" +#define HI "high" +#define BLANK " " +#define SYSSOFT_PROP "System software" +#define DRV_PROP "Driver" +#define HSC_PROP_NAME "hsc-slot-map" +#define HSC_MODE "hotswap-mode" +#define PCI_ROOT_AP "pci" +#define PROPS "Properties:" +#define BOARDTYPE "Board Type:" +#define DEVS "Devices:" +#define CPCI_IO "CompactPCI IO Slot" +#define AC_CARD "Alarm Card" +#define CPU_FTM "Front Transition Module" +#define SCTRL_PROM_P06 0x00 +#define SCTRL_PROM_P10 0x01 +#define SCTRL_PROM_P15 0x02 +#define SCTRL_PROM_P20 0x03 + +#define RMM_NUMBER 3 + +#define MONTECARLO_PLATFORM "SUNW,UltraSPARC-IIi-Netract" +#define MAKAHA_PLATFORM "SUNW,UltraSPARC-IIe-NetraCT-40" + +/* + * The follow table is indexed with the enum's defined by mct_slot_occupant_t + * OC_UNKN OC_CPU OC_AC OC_BHS OC_FHS OC_HAHS + * OC_QFE OC_FRCH OC_COMBO OC_PMC OC_ATM + * + * But "scsb" can currently identify only CPU and Alarm Cards by known + * slot numbers. + */ +char *slot_occupants[] = { + CPCI_IO, + "CPU board ", + CPCI_IO, + "Basic HotSwap Board", + "Full HotSwap Board", + "HA Board", + "QFE Board", + "Fresh Choice Board", + "SUN Combo Board", + "PMC Board", + "ATM Board" + }; + +static char *prtdiag_fru_types[] = { + "I/O ", /* 0 */ + "CPU ", + "PSU ", + "HDD ", + "FAN ", + "Alarm Card ", + "SCB ", + "SSB ", + "CFTM ", + "CRTM ", + "PRTM ", + "Midplane " /* 11 */ + }; + +char prtdiag_fru_info[MAX_PRTDIAG_FRUS][MAX_PRTDIAG_INFO_LENGTH]; + +#define SCB_REG_READ 1 +#define SCB_REG_WRITE 2 + +/* Standard Device nodes - hardwired for now */ +/* will include fan tray later, cpu voltage not impl */ +static char *scsb_node = NULL; +static char **ps_node = NULL; +static char *temp_node = NULL; + +static char *mc_scsb_node = +"/devices/pci@1f,0/pci@1,1/ebus@1/i2c@14,600000/sysctrl@0,80:scsb"; + +static char *ot_scsb_node = +"/devices/pci@1f,0/pci@1,1/ebus@3/sysmgmt@14,600000/sysctrl@0,80:scsb"; + +static char *mc_ps_node[] = { +"/devices/pci@1f,0/pci@1,1/ebus@1/i2c@14,600000/gpio@0,7c:pwrsuppply", +"/devices/pci@1f,0/pci@1,1/ebus@1/i2c@14,600000/gpio@0,7e:pwrsuppply" +}; + +static char *ot_ps_node[] = { +"/devices/pci@1f,0/pci@1,1/ebus@3/sysmgmt@14,600000/gpio@0,7c:pwrsuppply", +"/devices/pci@1f,0/pci@1,1/ebus@3/sysmgmt@14,600000/gpio@0,7e:pwrsuppply" +}; + +static char *mc_temp_node = +"/devices/pci@1f,0/pci@1,1/ebus@1/i2c@14,600000/adc-dac@0,9e:cputemp"; + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (netract systems only) + * display functions + */ +int display(Sys_tree *, Prom_node *, struct system_kstat_data *, int); +/* local functions */ +/* + * prom function + */ +static void gather_diaginfo(int flag); +static int extract_slot_table_from_obp(); +static int mc_next(int id); +static void mc_walk(int id); +static int mc_child(int id); +static void mc_dump_node(int id); +static int mc_getpropval(struct openpromio *opp); + +#ifdef REDUNDANT_INFO +static int mc_get_cpu_freq(Prom_node *node); +static int mc_get_ecache_size(Prom_node *node); +static void mc_display_cpus(Board_node *board); +static void mc_display_cpu_devices(Sys_tree *tree); +#endif /* REDUNDANT_INFO */ + +static void netract_disp_prom_version(); + +/* + * Since we do not have a system wide kstat for MC/Tg + * here we have to do specific kstats to drivers that + * post this information - MC/Tg specific drivers + * that post kstat here are : scsb, pcf8574(gpio) and pcf8591 + */ +static int analyze_nodes(di_node_t, void*); +static void analyze_pcipci_siblings(di_node_t); +static void display_mc_prtdiag_info(); +static int dump_devs(di_node_t, void *); +static void prtdiag_devinfo(void); +static void force_load_drivers(); +static int dump_prop_list(char *name, + di_node_t node, di_prop_t (*nxtprop)()); +static void *config_calloc_check(size_t nelem, size_t elsize); +static void explore_slot_occupants(); +static void do_scsb_kstat(); +static void do_pcf8574_kstat(); +static void do_pcf8591_kstat(); +static void do_promversion(); +static int mc_promopen(int oflag); +static int scsi_disk_status(int disk_number); +static void alarm_card_occupant(); +static int scsb_mode(int fd, scsb_op_t sop, uint8_t *new_mode); +static int scsb_ioc_reg_read(int fd, uchar_t index, + scsb_ioc_rdwr_t *ioc_rd, int num); + +static int check_platform(); + +int +display(Sys_tree *tree, + Prom_node *root, + struct system_kstat_data *kstats, + int syserrlog) +{ + int exit_code = 0; /* init to all OK */ + void *value; /* used for opaque PROM data */ + struct mem_total memory_total; /* Total memory in system */ + struct grp_info grps; /* Info on all groups in system */ +#ifdef lint + syserrlog = syserrlog; +#endif + sys_clk = -1; /* System clock freq. (in MHz) */ + /* + * Now display the machine's configuration. We do this if we + * are not logging or exit_code is set (machine is broke). + */ + if (!logging || exit_code) { + struct utsname uts_buf; + + /* + * Display system banner + */ + (void) uname(&uts_buf); + + log_printf(dgettext(TEXT_DOMAIN, + "System Configuration: Sun Microsystems" + " %s %s\n"), uts_buf.machine, + get_prop_val(find_prop(root, "banner-name")), 0); + + /* display system clock frequency */ + value = get_prop_val(find_prop(root, "clock-frequency")); + if (value != NULL) { + sys_clk = ((*((int *)value)) + 500000) / 1000000; + log_printf(dgettext(TEXT_DOMAIN, + "System clock frequency: " + "%d MHz\n"), sys_clk, 0); + } + + /* Display the Memory Size */ + display_memorysize(tree, kstats, &grps, &memory_total); + /* Lets make sure we have all the needed drivers loaded */ + /* display Montecarlo/Tonga FRU information */ + if (!extract_slot_table_from_obp()) + log_printf(dgettext(TEXT_DOMAIN, + "\r\nslot-table not available\r\n"), 0); + do_scsb_kstat(); + force_load_drivers(); + gather_diaginfo(print_flag && !logging); + /* figure out if ac is present */ + alarm_card_occupant(); + /* platform specific display mod */ + display_mc_prtdiag_info(); + di_fini(rootnode); + netract_disp_prom_version(); + } /* if (!logging || exit_code) */ + + return (exit_code); + +} /* display(....) */ + +static int +check_platform() +{ + char si_platform[SYS_NMLN]; + + /* + * Check for the platform: Montecarlo or Makaha/CP2040 based + */ + if (sysinfo(SI_PLATFORM, si_platform, sizeof (si_platform)) == -1) { + return (-1); + } + + if ((strncmp(si_platform, MONTECARLO_PLATFORM, + strlen(MONTECARLO_PLATFORM))) == 0) { + scsb_node = mc_scsb_node; + ps_node = mc_ps_node; + temp_node = mc_temp_node; + } else if ((strncmp(si_platform, MAKAHA_PLATFORM, + strlen(MAKAHA_PLATFORM))) == 0) { + scsb_node = ot_scsb_node; + ps_node = ot_ps_node; + temp_node = NULL; + } else { + return (-1); + } + + return (0); +} + +void +force_load_drivers() +{ + int i; + + if (NULL == scsb_node || NULL == ps_node) { + if (check_platform() == -1) { + return; + } + } + + /* check scb/ssb presence */ + if (scsb_ks_state.scb_present || scsb_ks_state.ssb_present) { + if (open(scsb_node, O_RDONLY) < 0) + log_printf(dgettext(TEXT_DOMAIN, + "\nscsb open FAILED!"), 0); + } + + /* check the num of PS we have */ + for (i = 0; i < scsb_ks_topo.max_units[PS]; ++i) { + if (scsb_ks_topo.mct_ps[i].fru_status == FRU_PRESENT) { + if ((ps_fd[i] = open(ps_node[i], O_RDONLY)) < 0) + log_printf(dgettext(TEXT_DOMAIN, + "\npowersupply%d open failed"), + i, 0); + } + } /* for */ + + /* open the cpu temp driver */ + if (temp_node) { + if (open(temp_node, O_RDONLY) < 0) + log_printf(dgettext(TEXT_DOMAIN, + "\ncputemp open FAILED!"), 0); + } +} + + +void +explore_slot_occupants() +{ + char *cp = NULL; + int index; + int ret = CFGA_ERROR; + char *estrp = NULL; + cfga_list_data_t *list_array = NULL; + ap_out_t *out_array = NULL; + int nlist = 0; + char *prefilt_optp = NULL; + int dyn_exp = 1; + char *plat_opts = NULL; + + ret = config_list_ext(0, NULL, &list_array, + &nlist, plat_opts, prefilt_optp, &estrp, + dyn_exp ? CFGA_FLAG_LIST_ALL : 0); + if (ret != CFGA_OK) { + log_printf(dgettext(TEXT_DOMAIN, + "\ncannot explore configuration"), 0); + return; + } + assert(nlist != 0); + out_array = config_calloc_check(nlist, sizeof (*out_array)); + if (out_array == NULL) { + ret = CFGA_LIB_ERROR; + goto bail; + } + /* create a list of output stat data */ + for (index = 0; index < nlist; index++) { + out_array[index].ldatap = &list_array[index]; + out_array[index].req = 0; + } + + for (index = 0; index < nlist; index++) { + if ((cp = strstr(out_array[index].ldatap->ap_phys_id, + "cpci_slot")) != NULL) { + mc_slots_data.mc_slot_info[idx_minuscpu].slot_stat + = out_array[index].ldatap->ap_o_state; + mc_slots_data.mc_slot_info[idx_minuscpu].slot_cond + = out_array[index].ldatap->ap_cond; + idx_minuscpu++; + } + } +bail: + S_FREE(list_array); + S_FREE(out_array); +} + + +/* + * config_calloc_check - perform allocation, check result and + * set error indicator + */ +void * +config_calloc_check( + size_t nelem, + size_t elsize) +{ + void *p; + static char alloc_fail[] = + "%s: memory allocation failed (%d*%d bytes)\n"; + + p = calloc(nelem, elsize); + if (p == NULL) { + log_printf(dgettext(TEXT_DOMAIN, alloc_fail), nelem, elsize, 0); + } + return (p); +} + + +void +do_scsb_kstat() +{ + kstat_ctl_t *kc; + kstat_t *ksp_leddata; + kstat_t *ksp_state; + kstat_t *ksp_topo; + scsb_ks_leddata_t *pks_leddata; + scsb_ks_state_t *pks_state; + mct_topology_t *pks_topo; + int i; + +#ifdef DEBUG_TEMP1 + int index; +#endif + if (!(kc = kstat_open())) { +#ifdef DEBUG_TEMP + log_printf("\nkstat_open failed", 0); +#endif + return; + } +#ifdef lint + kc = kc; +#endif + /* get kstat on scsb led data */ + if ((ksp_leddata = kstat_lookup(kc, SCSB_DEV, 0, SCSB_KS_LEDDATA)) + == NULL) { +#ifdef DEBUG_TEMP + log_printf("\nkstat_lookup for scsb_leddata failed", 0); +#endif + return; + } + if (kstat_read(kc, ksp_leddata, NULL) == -1) { +#ifdef DEBUG_TEMP + log_printf("\nkstat_read for scsb_leddata failed", 0); +#endif + return; + } + pks_leddata = (scsb_ks_leddata_t *)ksp_leddata->ks_data; + scsb_ks_leddata = *pks_leddata; /* set the globals for future */ +#ifdef DEBUG_LEDS + /* dump the kstat leddata */ + printf("\nDumping LED regs: "); + for (i = 0; i < SCSB_LEDDATA_REGISTERS; ++i) { + log_printf("0x%x ", pks_leddata->scb_led_regs[i] & 0xff, 0); + } + log_printf("\n", 0); +#endif + /* get kstat on scsb states */ + if ((ksp_state = kstat_lookup(kc, SCSB_DEV, 0, SCSB_KS_STATE)) + == NULL) { +#ifdef DEBUG_TEMP + log_printf("\nkstat_lookup for scsb_state failed", 0); +#endif + return; + } + if (kstat_read(kc, ksp_state, NULL) == -1) { +#ifdef DEBUG_TEMP + log_printf("\nkstat_read for scsb_state failed", 0); +#endif + return; + } + pks_state = (scsb_ks_state_t *)ksp_state->ks_data; + scsb_ks_state = *pks_state; /* set the global for future */ +#ifdef DEBUG_TEMP1 + /* dump the kstat state */ + log_printf("\tSCB is%spresent\n", + pks_state->scb_present ? " " : " not ", 0); + log_printf("\tSSB is%spresent\n", + pks_state->ssb_present ? " " : " not ", 0); + log_printf("\tscsb is%sfrozen\n", + pks_state->scsb_frozen ? " " : " not ", 0); + log_printf("\tscsb mode: ", 0); + switch (pks_state->scsb_mode) { + case ENVC_DEBUG_MODE: + log_printf("DEBUG MODE\n", 0); + break; + case ENVCTRL_DIAG_MODE: + log_printf("DIAGNOSTIC MODE\n", 0); + break; + case ENVCTRL_NORMAL_MODE: + log_printf("NORMAL MODE\n", 0); + break; + } + log_printf("\tscsb event code: 0x%x\n", pks_state->event_code, 0); +#endif /* DEBUG_TEMP1 */ + + if ((ksp_topo = kstat_lookup(kc, SCSB_DEV, 0, SCSB_KS_TOPOLOGY)) + == NULL) { +#ifdef DEBUG_TEMP + log_printf("\nkstat_lookup for scsb_topo failed", 0); +#endif + return; + } + if (kstat_read(kc, ksp_topo, NULL) == -1) { +#ifdef DEBUG_TEMP + log_printf("\nkstat_read for scsb_topo failed", 0); +#endif + return; + } + pks_topo = (mct_topology_t *)ksp_topo->ks_data; + scsb_ks_topo = *pks_topo; /* set the global for future */ + /* + * we need to set this so that we can get status info + * for the 2 powersupplies in MC as we need to get + * kstat from both driver instances for environment + */ + if (pks_topo->mid_plane.fru_id == SCTRL_MPID_HALF) + montecarlo = 1; /* Monte Carlo */ + /* + * HW version 0.6 and 1.0 had different led maps + * its assumed that HW 2.0 would not change this + * need to modify if it does + */ + if ((pks_topo->mct_scb[0].fru_version == SCTRL_PROM_P15) || + (pks_topo->mct_scb[0].fru_version == SCTRL_PROM_P20)) { + version_p15_and_p20 = 1; + } + + /* set flag to note that CFTM is present */ + for (i = 0; i < pks_topo->max_units[CFTM]; ++i) { + if (pks_topo->mct_cftm[i].fru_status == FRU_PRESENT) + cpu_ftm_present = 1; + } + +#ifdef DEBUG_TEMP1 + /* + * Midplane + */ + log_printf("Midplane type: ", 0); + if (pks_topo->mid_plane.fru_id == SCTRL_MPID_HALF) + log_printf("Netra ct800 server\n", 0); + else + log_printf("Netra ct400 server%s\n", + pks_topo->mid_plane.fru_id == + SCTRL_MPID_QUARTER_NODSK ? ", no disk" : " with disk", 0); + log_printf("Midplane version: %d\n", + pks_topo->mid_plane.fru_version, 0); + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_scb[0].fru_type, + pks_topo->mct_scb[0].fru_unit, + pks_topo->mct_scb[0].fru_id, + pks_topo->mct_scb[0].fru_version, 0); + /* + * Slots + */ + log_printf("Slots present out of maximum %d\n", + pks_topo->max_units[SLOT], 0); + for (i = 0; i < pks_topo->max_units[SLOT]; ++i) { + if (pks_topo->mct_slots[i].fru_status != FRU_PRESENT) + continue; + index = (int)pks_topo->mct_slots[i].fru_type; + log_printf("\tSlot %d occupant: %s;", + pks_topo->mct_slots[i].fru_unit, slot_occupants[index], 0); + log_printf(" ID 0x%x; VER 0x%x ; ", + pks_topo->mct_slots[i].fru_id, + pks_topo->mct_slots[i].fru_version, 0); + log_printf(" Slot health %d\n", + pks_topo->mct_slots[i].fru_health, 0); + /* pks_topo->mct_slots[i].fru_health */ + } + + /* + * PDU + */ + log_printf("PDUs present out of maximum %d\n", + pks_topo->max_units[PDU], 0); + for (i = 0; i < pks_topo->max_units[PDU]; ++i) { + if (pks_topo->mct_pdu[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_pdu[i].fru_type, + pks_topo->mct_pdu[i].fru_unit, + pks_topo->mct_pdu[i].fru_id, + pks_topo->mct_pdu[i].fru_version, 0); + /* pks_topo->mct_pdu[i].fru_health */ + } + + /* + * Power Supplies + */ + log_printf("Power Supplies present out of maximum %d\n", + pks_topo->max_units[PS], 0); + for (i = 0; i < pks_topo->max_units[PS]; ++i) { + if (pks_topo->mct_ps[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_ps[i].fru_type, + pks_topo->mct_ps[i].fru_unit, + pks_topo->mct_ps[i].fru_id, + pks_topo->mct_ps[i].fru_version, 0); + } + + /* + * Disks + */ + log_printf("Disks present out of maximum %d\n", + pks_topo->max_units[DISK], 0); + for (i = 0; i < pks_topo->max_units[DISK]; ++i) { + if (pks_topo->mct_disk[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_disk[i].fru_type, + pks_topo->mct_disk[i].fru_unit, + pks_topo->mct_disk[i].fru_id, + pks_topo->mct_disk[i].fru_version, 0); + } + + /* + * Fans + */ + log_printf("Fans present out of maximum %d\n", + pks_topo->max_units[FAN], 0); + for (i = 0; i < pks_topo->max_units[FAN]; ++i) { + if (pks_topo->mct_fan[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_fan[i].fru_type, + pks_topo->mct_fan[i].fru_unit, + pks_topo->mct_fan[i].fru_id, + pks_topo->mct_fan[i].fru_version, 0); + } + + /* + * SCBs + */ + log_printf("SCBs present out of maximum %d\n", + pks_topo->max_units[SCB], 0); + for (i = 0; i < pks_topo->max_units[SCB]; ++i) { + if (pks_topo->mct_scb[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_scb[i].fru_type, + pks_topo->mct_scb[i].fru_unit, + pks_topo->mct_scb[i].fru_id, + pks_topo->mct_scb[i].fru_version, 0); + } + + /* + * SSBs + */ + log_printf("SSBs present out of maximum %d\n", + pks_topo->max_units[SSB], 0); + for (i = 0; i < pks_topo->max_units[SSB]; ++i) { + if (pks_topo->mct_ssb[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_ssb[i].fru_type, + pks_topo->mct_ssb[i].fru_unit, + pks_topo->mct_ssb[i].fru_id, + pks_topo->mct_ssb[i].fru_version, 0); + } + + /* + * Alarms Cards + */ + log_printf("Alarm Cards present out of maximum %d\n", + pks_topo->max_units[ALARM], 0); + for (i = 0; i < pks_topo->max_units[ALARM]; ++i) { + if (pks_topo->mct_alarm[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d; unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_alarm[i].fru_type, + pks_topo->mct_alarm[i].fru_unit, + pks_topo->mct_alarm[i].fru_id, + pks_topo->mct_alarm[i].fru_version, 0); + } + + /* + * CFTMs + */ + log_printf("CFTMs present out of maximum %d\n", + pks_topo->max_units[CFTM], 0); + for (i = 0; i < pks_topo->max_units[CFTM]; ++i) { + if (pks_topo->mct_cftm[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_cftm[i].fru_type, + pks_topo->mct_cftm[i].fru_unit, + pks_topo->mct_cftm[i].fru_id, + pks_topo->mct_cftm[i].fru_version, 0); + } + + /* + * CRTMs + */ + log_printf("CRTMs present out of maximum %d\n", + pks_topo->max_units[CRTM], 0); + for (i = 0; i < pks_topo->max_units[CRTM]; ++i) { + if (pks_topo->mct_crtm[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_crtm[i].fru_type, + pks_topo->mct_crtm[i].fru_unit, + pks_topo->mct_crtm[i].fru_id, + pks_topo->mct_crtm[i].fru_version, 0); + } + + /* + * PRTMs + */ + log_printf("PRTMs present out of maximum %d\n", + pks_topo->max_units[PRTM], 0); + for (i = 0; i < pks_topo->max_units[PRTM]; ++i) { + if (pks_topo->mct_prtm[i].fru_status != FRU_PRESENT) + continue; + log_printf("\ttype %d unit %d; id 0x%x; VER 0x%x\n", + pks_topo->mct_prtm[i].fru_type, + pks_topo->mct_prtm[i].fru_unit, + pks_topo->mct_prtm[i].fru_id, + pks_topo->mct_prtm[i].fru_version, 0); + } +#endif /* DEBUG_TEMP1 */ + +} /* do_scsb_kstat(...) */ + + +void +do_pcf8574_kstat() +{ + kstat_ctl_t *kc; + kstat_t *ksp_ps; + kstat_t *ksp_fan; + envctrl_pwrsupp_t *pks_ps; + envctrl_fantray_t *pks_fan; + int i; + char *kstat_name = NULL; + + if (!(kc = kstat_open())) { +#ifdef DEBUG_TEMP + log_printf("\nkstat_open for pcf8574 failed", 0); +#endif + return; + } + +#ifdef lint + kc = kc; +#endif + /* get kstat on gpio powersupply and fan states */ + for (i = 0; i < scsb_ks_topo.max_units[PS]; ++i) { + if (i == 1) { + kstat_name = I2C_KSTAT_PWRSUPPLY; + strncat(kstat_name, "1", 1); + } else { + kstat_name = I2C_KSTAT_PWRSUPPLY; + strncat(kstat_name, "2", 1); + } + if ((ksp_ps = kstat_lookup(kc, I2C_PCF8574_NAME, 0, kstat_name)) + == NULL) { +#ifdef DEBUG_TEMP + log_printf("\nks lookup for pwrsupply%d failed", + i+1, 0); +#endif + return; + } + if (kstat_read(kc, ksp_ps, NULL) == -1) { +#ifdef DEBUG_TEMP + log_printf("\nks read for pwrsupply%d failed", i+1, 0); +#endif + return; + } + pks_ps = (envctrl_pwrsupp_t *)ksp_ps->ks_data; + if (i == 1) + pcf8574_ks_ps1 = *pks_ps; /* ps 1 */ + else + pcf8574_ks_ps2 = *pks_ps; /* ps 2 */ + } /* for */ + for (i = 0; i < scsb_ks_topo.max_units[FAN]; ++i) { + if (i == 1) { + kstat_name = I2C_KSTAT_FANTRAY; + strncat(kstat_name, "1", 1); + } else { + kstat_name = I2C_KSTAT_FANTRAY; + strncat(kstat_name, "2", 1); + } + if ((ksp_fan = kstat_lookup(kc, I2C_PCF8574_NAME, + 0, kstat_name)) == NULL) { +#ifdef DEBUG_TEMP + log_printf("\nks lookup for fantray%d failed", + i+1, 0); +#endif + return; + } + if (kstat_read(kc, ksp_fan, NULL) == -1) { +#ifdef DEBUG_TEMP + log_printf("\nks read for fantray%d failed", i+1, 0); +#endif + return; + } + pks_fan = (envctrl_fantray_t *)ksp_fan->ks_data; + if (i == 1) + pcf8574_ks_fant1 = *pks_fan; /* fan 1 */ + else + pcf8574_ks_fant2 = *pks_fan; /* fan 2 */ + } /* for */ + kstat_close(kc); + +} /* do_pcf8574_kstat(...) */ + +void +do_pcf8591_kstat() +{ + kstat_ctl_t *kc; + kstat_t *ksp_temp; + + envctrl_temp_t *pks_temp; + + if (!(kc = kstat_open())) { +#ifdef DEBUG_TEMP + log_printf("ks open for pcf8591 failed", 0); +#endif + return; + } +#ifdef lint + kc = kc; +#endif + /* get kstat on adc driver's CPU temperature data */ + if ((ksp_temp = kstat_lookup(kc, I2C_PCF8591_NAME, + -1, I2C_KSTAT_CPUTEMP)) + == NULL) { +#ifdef DEBUG_TEMP + log_printf("ks lookup for adc_temp failed", 0); +#endif + return; + } + if (kstat_read(kc, ksp_temp, NULL) == -1) { +#ifdef DEBUG_TEMP + log_printf("ks read for adc_temp failed", 0); +#endif + return; + } + pks_temp = (envctrl_temp_t *)ksp_temp->ks_data; + pcf8591_ks_temp = *pks_temp; + kstat_close(kc); +} /* do_pcf8591_kstat(.) */ + + +void +gather_diaginfo(int flag) +{ + if (flag) { + /* gather system environmental conditions. */ + /* obtain kstat info from gpio & temp. driver */ + do_pcf8574_kstat(); + do_pcf8591_kstat(); + explore_slot_occupants(); /* fill in some occupant info */ + prtdiag_devinfo(); + analyze_pcipci_siblings(rootnode); + } + +} /* display_diaginfo(...) */ + +void +netract_disp_prom_version() +{ + /* Display Prom revision header */ + log_printf(dgettext(TEXT_DOMAIN, "System Board PROM revision:\n"), 0); + log_printf("---------------------------\n", 0); + do_promversion(); + +} /* netract_disp_prom_version(.) */ + + +/* + * Get and print the PROM version. + */ +void +do_promversion(void) +{ + Oppbuf oppbuf; + struct openpromio *opp = &(oppbuf.opp); + + if (mc_promopen(O_RDONLY)) { + log_printf(dgettext(TEXT_DOMAIN, + "\nCannot open openprom device"), 0); + return; + } + + opp->oprom_size = MAXVALSIZE; + if (ioctl(oprom_fd, OPROMGETVERSION, opp) < 0) { + perror("\nOPROMGETVERSION ioctl failed"); + return; + } + log_printf("%s\n", opp->oprom_array, 0); + + if (close(oprom_fd) < 0) { + log_printf(dgettext(TEXT_DOMAIN, + "\nclose error on %s"), OPENPROMDEV, 0); + return; + } +} /* do_promversion() */ + +int +mc_promopen(int oflag) +{ + for (;;) { + if ((oprom_fd = open(OPENPROMDEV, oflag)) < 0) { + if (errno == EAGAIN) { + (void) sleep(5); + continue; + } + if (errno == ENXIO) + return (-1); + log_printf(dgettext(TEXT_DOMAIN, + "\ncannot open %s"), OPENPROMDEV, 0); + return (1); + } else + return (0); + } +} + + +/* + * This will return -1 for status unknown, 0 for OK, and 1 for failed (scsi + * hard errors) + * swiped from envmon policies + */ +int +scsi_disk_status(int disk_number) +{ + kstat_ctl_t *kc; + kstat_t *ksp_disk; + kstat_named_t *disk_data; + + int i; + int nlist = 0; + cfga_list_data_t *list_array = NULL; + char *ap_ids[] = {"c0"}; + + if ((kc = kstat_open()) == NULL) { + log_printf(dgettext(TEXT_DOMAIN, "\nks open failed"), 0); + return (-1); + } + + if (disk_number == RMM_NUMBER) { /* RMM */ + if (config_list_ext(1, ap_ids, &list_array, &nlist, + NULL, NULL, NULL, CFGA_FLAG_LIST_ALL) != CFGA_OK) { + kstat_close(kc); + return (-1); + } + for (i = 0; i < nlist; i++) { + if (strstr(list_array[i].ap_phys_id, "rmt/0") != NULL) { + /* Tape drive */ + if (list_array[i].ap_o_state == + CFGA_STAT_UNCONFIGURED) { + kstat_close(kc); + return (-1); + } + if ((ksp_disk = kstat_lookup(kc, STERR, + st_instance, NULL)) == NULL) { + kstat_close(kc); + return (-1); + } + break; + } else if (strstr(list_array[i].ap_phys_id, + "dsk/c0t6d0") != NULL) { + /* CD_ROM */ + if (list_array[i].ap_o_state == + CFGA_STAT_UNCONFIGURED) { + kstat_close(kc); + return (-1); + } + if ((ksp_disk = kstat_lookup(kc, SDERR, + sd_instances[disk_number-1], NULL)) == + NULL) { + kstat_close(kc); + return (-1); + } + break; + } + } + } else { /* Hard disk */ + if ((ksp_disk = kstat_lookup(kc, SDERR, + sd_instances[disk_number-1], NULL)) == NULL) { + kstat_close(kc); + return (-1); + } + } + + if (kstat_read(kc, ksp_disk, NULL) == -1) { + log_printf(dgettext(TEXT_DOMAIN, + "\nks read error for disk%d, drv inst%d"), + disk_number, sd_instances[disk_number-1], 0); + kstat_close(kc); + return (-1); + } + disk_data = KSTAT_NAMED_PTR(ksp_disk); + /* + * if disk_data[].value is >0, we have a problem + */ + if (disk_data[1].value.ui32 == 0) { + kstat_close(kc); + return (0); + } else { + kstat_close(kc); + return (1); + } +} + + +void +prtdiag_devinfo(void) +{ + uint_t flag; + /* lets get everything we can from kernel */ + flag = DINFOSUBTREE|DINFOPROP; + rootnode = di_init("/", flag); + if (rootnode == DI_NODE_NIL) { + log_printf(dgettext(TEXT_DOMAIN, + "\nprtdiag_devinfo: di_init() failed"), 0); + return; + } + (void) di_walk_node(rootnode, DI_WALK_CLDFIRST, NULL, + dump_devs); +} + + +/* + * gather information about this node, returns appropriate code. + * specific information we seek are driver names, instances + * we will initialize some globals depending on what we find + * from the kernel device tree info and may be private data + * if required + */ +/*ARGSUSED1*/ +int +dump_devs(di_node_t node, void *arg) +{ + char *driver_name; + + driver_name = di_driver_name(node); + /* we will initialize our globals here */ + if ((di_instance(node) >= 0) && + (driver_name != NULL) && + (!(di_state(node) & DI_DRIVER_DETACHED))) { + if (strcmp(driver_name, "pcf8574") == 0) { + gpio_instances[gpio_count] = di_instance(node); + gpio_count++; + } else if (strcmp(driver_name, "sd") == 0) { + sd_instances[sd_count] = di_instance(node); + sd_count++; + } else if (strcmp(driver_name, "st") == 0) { + st_instance = di_instance(node); + } + } + + if (strcmp(di_node_name(node), "pseudo") == 0) + return (DI_WALK_PRUNECHILD); + else + return (DI_WALK_CONTINUE); +} + + + +/* + * Returns 0 if error , 1 otherwise + */ +int +dump_prop_list(char *name, di_node_t node, di_prop_t (*nxtprop)()) +{ + int prop_len, i, k, max_slots_minus_cpu, n; + uchar_t *prop_data; + char *p; + char *temp_s; + di_prop_t prop, next; + int ret_value = 0; + + max_slots_minus_cpu = scsb_ks_topo.max_units[SLOT]-1; + + if ((next = nxtprop(node, DI_PROP_NIL)) == DI_PROP_NIL) + return (0); + while (next != DI_PROP_NIL) { + int maybe_str = 1, npossible_strs = 0; + prop = next; + next = nxtprop(node, prop); + /* + * get prop length and value: + * private interface--always success + */ + prop_len = di_prop_rawdata(prop, &prop_data); + if (di_prop_type(prop) == DDI_PROP_UNDEF_IT) { + continue; + } + + if (prop_len == 0) { + continue; + } + if (prop_data[prop_len - 1] != '\0') { + maybe_str = 0; + } else { + /* + * Every character must be a string character or a \0, + * and there must not be two \0's in a row. + */ + for (i = 0; i < prop_len; i++) { + if (prop_data[i] == '\0') { + npossible_strs++; + } else if (!isascii(prop_data[i]) || + iscntrl(prop_data[i])) { + maybe_str = 0; + break; + } + + if ((i > 0) && (prop_data[i] == '\0') && + (prop_data[i - 1] == '\0')) { + maybe_str = 0; + break; + } + } + } + + if (maybe_str) { + p = (char *)prop_data; + for (i = 0; i < npossible_strs - 1; i++) { + if ((strcmp(name, SYSSOFT_PROP) == 0) && + (strcmp(di_prop_name(prop), + HSC_PROP_NAME) == 0)) { + temp_s = p; + temp_s += strlen(temp_s) + 1; + } + p += strlen(p) + 1; + } + + if ((strcmp(name, SYSSOFT_PROP) == 0) && + (strcmp(di_prop_name(prop), HSC_PROP_NAME) == 0)) { + temp_s = temp_s - prop_len+2; + for (k = 0, n = 0; k < prop_len; k++) { + if (temp_s[k] == 0) { + n++; + } + } + if (n % 4) { + log_printf(dgettext(TEXT_DOMAIN, + "\nbad slot-table(%d)\n"), n); + slot_table_not_found = 0; + return (ret_value); + } + slot_table_size = n / 4; + /* + * NOTE : We save slot table info in order + */ + for (k = 0; k < slot_table_size; k++) { + char *nexus, *pcidev, *phys_slotname; + char *ga; + /* + * Pick off pointer to nexus + * path or PROM handle + */ + nexus = temp_s; + while (*temp_s != NULL) + temp_s++; + temp_s++; + + /* + * Pick off pointer to the + * pci device number + */ + pcidev = temp_s; + while (*temp_s != NULL) + temp_s++; + temp_s++; + + /* Pick off physical slot no */ + phys_slotname = temp_s; + while (*temp_s != NULL) + temp_s++; + temp_s++; + + /* + * Pick off GA bits which + * we dont use for now. + */ + ga = temp_s; + while (*temp_s != NULL) + temp_s++; + temp_s++; + + hotswap_slot_table[k].pslotnum + = atoi(phys_slotname); + hotswap_slot_table[k].ga = atoi(ga); + hotswap_slot_table[k].pci_devno + = atoi(pcidev); + strcpy(hotswap_slot_table[k].nexus, + nexus); + } /* for (k = 0; k < slot_table_size; k++) */ + + ret_value = 1; + } else /* (strcmp(name, SYSSOFT_PROP) */ + slot_table_not_found = 1; + + /* + * now we want to save off the info + * we would use later + */ + if ((strcmp(name, DRV_PROP) == 0) && + (strcmp(di_prop_name(prop), HSC_MODE) == 0)) { + hotswap_mode = p; + ret_value = 1; + } else if ((strcmp(name, DRV_PROP) == 0) && + (strcmp(di_prop_name(prop), HSC_MODE) != 0)) { + /* save it in order in the right index */ + slot_auto_config[max_slots_minus_cpu] = p; + max_slots_minus_cpu--; + ret_value = 1; + } + + } else { + for (i = 0; i < prop_len; ++i) { +#if 0 + unsigned char byte; + byte = (unsigned char)prop_data[i]; + log_printf("%2.2x", byte, 0); +#endif + } + } + } + return (ret_value); +} + + +void +display_mc_prtdiag_info() +{ + int i, index; + int s_index, i1; + int tg_cpu_index = 0; + char *mcfru_type, *status, *mc_ok_led, *mc_nok_led; + char *misc_info, *health, *board_type; + + log_printf("===============================", 0); + log_printf(dgettext(TEXT_DOMAIN, + " FRU Information ================================\n"), 0); + log_printf(dgettext(TEXT_DOMAIN, + "FRU FRU FRU Green Amber"), 0); + log_printf(dgettext(TEXT_DOMAIN, " Miscellaneous\n"), 0); + log_printf(dgettext(TEXT_DOMAIN, + "Type Unit# Present LED LED"), 0); + log_printf(dgettext(TEXT_DOMAIN, " Information\n"), 0); + + log_printf("---------- ----- ------- ----- -----", 0); + log_printf(" ----------------------------------\n", 0); + + if (scsb_ks_topo.mid_plane.fru_id == SCTRL_MPID_HALF) + misc_info = "Netra ct800"; + else { + misc_info = "Netra ct400"; + } + mcfru_type = prtdiag_fru_types[MIDPLANE]; + switch (scsb_ks_topo.mid_plane.fru_status) { + case FRU_PRESENT: + status = YES; + break; + case FRU_NOT_PRESENT: + status = NO; + break; + case FRU_NOT_AVAILABLE: + status = NA; break; + default: + status = NA; break; + } + mc_ok_led = " "; + mc_nok_led = " "; + + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s %-5s %s\n"), + mcfru_type, scsb_ks_topo.mid_plane.fru_unit, + status, mc_ok_led, mc_nok_led, + misc_info, 0); + log_printf(dgettext(TEXT_DOMAIN, "%46s%s\n"), BLANK, PROPS, 0); + log_printf(dgettext(TEXT_DOMAIN, "%49sVersion=%d\n"), BLANK, + scsb_ks_topo.mid_plane.fru_version, 0); + log_printf(dgettext(TEXT_DOMAIN, "%49sMaximum Slots=%d\n"), BLANK, + scsb_ks_topo.max_units[SLOT], 0); + + /* SCB & SSB */ + mcfru_type = prtdiag_fru_types[SCB]; + for (i = 0; i < scsb_ks_topo.max_units[SCB]; ++i) { + misc_info = "System Controller Board"; + if (version_p15_and_p20) { + mc_ok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.blink_leds[1] + & 0xff), SCB_OK_BIT) ? BLINK : + (BIT_TEST((scsb_ks_leddata.leds.p15.ok_leds[1] + & 0xff), SCB_OK_BIT) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.nok_leds[1] + & 0xff), SCB_OK_BIT) ? ON:OFF; + } else { + /* + * support for 1.0 systems - + * Hack! - should use tables ? + */ + mc_ok_led = + (BIT_TEST((scsb_ks_leddata.leds.p10.ok_leds[2] + & 0xff), 0) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p10.nok_leds[2] + & 0xff), 0) ? ON:OFF; + } + switch (scsb_ks_topo.mct_scb[i].fru_status) { + case FRU_PRESENT: + status = YES; + break; + case FRU_NOT_PRESENT: + status = NO; + break; + case FRU_NOT_AVAILABLE: + status = NA; + break; + default: + status = NA; + break; + } + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s %-5s %s\n"), + mcfru_type, scsb_ks_topo.mct_scb[i].fru_unit, + status, mc_ok_led, mc_nok_led, misc_info, 0); + log_printf(dgettext(TEXT_DOMAIN, "%46s%s\n"), BLANK, PROPS, 0); + log_printf(dgettext(TEXT_DOMAIN, "%49sVersion=%d\n"), BLANK, + scsb_ks_topo.mct_scb[0].fru_version, 0); + if (fail_drv_prop == 1) + log_printf(dgettext(TEXT_DOMAIN, + "%49s%s=%s\n"), BLANK, HSC_MODE, + hotswap_mode, 0); + } /* for */ + + mcfru_type = prtdiag_fru_types[SSB]; + for (i = 0; i < scsb_ks_topo.max_units[SSB]; ++i) { + misc_info = "System Status Panel"; + switch (scsb_ks_topo.mct_ssb[i].fru_status) { + case FRU_PRESENT: + status = YES; + break; + case FRU_NOT_PRESENT: + status = NO; + break; + case FRU_NOT_AVAILABLE: + status = NA; + break; + default: + status = NA; + break; + } + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s %-5s %s\n"), + mcfru_type, scsb_ks_topo.mct_ssb[i].fru_unit, + status, BLANK, BLANK, misc_info, 0); + } /* for */ + + /* Slots */ + for (i = 0; i < scsb_ks_topo.max_units[SLOT]; ++i) { + if (montecarlo) { + if (scsb_ks_topo.mct_slots[i].fru_unit == 1) + mcfru_type = prtdiag_fru_types[1]; + else + mcfru_type = prtdiag_fru_types[SLOT]; + /* + * Another way this could have been done is, + * to read the sub system id + * it is 0x6722 for Alarm Card + * but this id is only valid for the new ACs + * older ACs still have the same susbsystem + * id as most other Sun PCI cards + * We cannot completely rely on this. + * Also,it turns out that Sun OpenBoot does not + * always follow IEEE 1275 std, hence in a few + * systems, the "subsystem-id" published by the + * PROM could not be found + * We know the AC slot# if present on both MC&Tg + * Hence we check on both - now we are sure + * that we have found an AC + */ + if ((scsb_ks_topo.mct_slots[i].fru_unit == 8) && + (alarm_card_present == 1)) + board_type = AC_CARD; + else + board_type = UK; + } else { + if (scsb_ks_topo.mct_slots[i].fru_unit == 3) + mcfru_type = prtdiag_fru_types[1]; + else + mcfru_type = prtdiag_fru_types[SLOT]; + /* + * Another way this could have been done is, + * to read the sub system id + * it is 0x6722 for Alarm Card + * but this id is only valid for the new ACs + * older ACs still have the same susbsystem + * id as most other Sun PCI cards + * We cannot completely rely on this. + * Also,it turns out that Sun OpenBoot does not + * always follow IEEE 1275 std, hence in a few + * systems, the "subsystem-id" published by the + * PROM could not be found + * We know the AC slot# if present on both MC&Tg + * Hence we check on both - now we are sure + * that we have found an AC + */ + if ((scsb_ks_topo.mct_slots[i].fru_unit == 1) && + (alarm_card_present == 1)) + board_type = AC_CARD; + else + board_type = UK; + } + if (version_p15_and_p20) { + mc_ok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.blink_leds[0] + & 0xff), i) ? BLINK : + (BIT_TEST((scsb_ks_leddata.leds.p15.ok_leds[0] + & 0xff), i) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.nok_leds[0] + & 0xff), i) ? ON:OFF; + } else { + /* + * support for 1.0 systems - + * Hack! - should use tables ? + */ + if (scsb_ks_topo.mct_slots[i].fru_unit == 7) { + mc_ok_led = + BIT_TEST( + (scsb_ks_leddata.leds.p10.blink_leds[1] + & 0xff), 0) ? BLINK : + (BIT_TEST( + (scsb_ks_leddata.leds.p10.ok_leds[1] + & 0xff), 0) ? ON:OFF); + mc_nok_led = + BIT_TEST( + (scsb_ks_leddata.leds.p10.nok_leds[1] + & 0xff), 0) ? ON:OFF; + } else if (scsb_ks_topo.mct_slots[i].fru_unit == 8) { + mc_ok_led = + BIT_TEST( + (scsb_ks_leddata.leds.p10.blink_leds[1] + & 0xff), 1) ? BLINK : + (BIT_TEST( + (scsb_ks_leddata.leds.p10.ok_leds[1] + & 0xff), 1) ? ON:OFF); + mc_nok_led = + BIT_TEST( + (scsb_ks_leddata.leds.p10.nok_leds[1] + & 0xff), 1) ? ON:OFF; + } else { + /* + * for all other slots offset, + * index are the same + */ + mc_ok_led = + BIT_TEST( + (scsb_ks_leddata.leds.p10.blink_leds[0] + & 0xff), i) ? BLINK : + (BIT_TEST( + (scsb_ks_leddata.leds.p10.ok_leds[0] + & 0xff), i) ? ON:OFF); + mc_nok_led = + BIT_TEST( + (scsb_ks_leddata.leds.p10.nok_leds[0] + & 0xff), i) ? ON:OFF; + } + + } /* else if (!version_p15_and_p20) */ + + switch (scsb_ks_topo.mct_slots[i].fru_status) { + case FRU_PRESENT: + status = YES; + break; + case FRU_NOT_PRESENT: + status = NO; + break; + case FRU_NOT_AVAILABLE: + status = NA; + break; + default: + status = NA; + break; + } + + index = (int)scsb_ks_topo.mct_slots[i].fru_type; + if (montecarlo) { + if (scsb_ks_topo.mct_slots[i].fru_unit == 1) { + /* cpu slot */ + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s "), + mcfru_type, + scsb_ks_topo.mct_slots[i].fru_unit, + status, mc_ok_led, mc_nok_led, 0); + log_printf(dgettext(TEXT_DOMAIN, "%-5s %s\n"), + mc_nok_led, + slot_occupants[index], 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49stemperature(celsius):%d\n"), + BLANK, + pcf8591_ks_temp.value, 0); +#ifdef NEVER + log_printf(dgettext(TEXT_DOMAIN, + "%49sminimum temperature:%d\n"), + BLANK, + pcf8591_ks_temp.min, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49swarning temp. threshold:%d\n"), + BLANK, + pcf8591_ks_temp.warning_threshold, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49sshutdown temp.threshold:%d\n"), + BLANK, + pcf8591_ks_temp.shutdown_threshold, 0); +#endif /* NEVER */ + } else if ((scsb_ks_topo.mct_slots[i].fru_unit == 2) && + (cpu_ftm_present == 1)) { + /* CFTM slot */ + /* + * The CFTM can only be present in Slot 2 + * for Netract-800, for Netract-400 the FTM + * is not sitted in a Slot. Hence, this is + * another special case and we need to handle + * this differently than other slots + */ + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s "), + mcfru_type, + scsb_ks_topo.mct_slots[i].fru_unit, + status, mc_ok_led, mc_nok_led, 0); + log_printf(dgettext(TEXT_DOMAIN, "%-5s %s\n"), + mc_nok_led, + CPU_FTM, 0); + } else { + if (fail_drv_prop == 1) { + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s "), + mcfru_type, + scsb_ks_topo.mct_slots[i].fru_unit, + status, mc_ok_led, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%-5s %s\n"), + mc_nok_led, + slot_occupants[index], 0); + log_printf(dgettext(TEXT_DOMAIN, + "%46s%s\n"), BLANK, + PROPS, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49sauto-config=%s\n"), + BLANK, + slot_auto_config[i], 0); + } else { + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s "), + mcfru_type, + scsb_ks_topo.mct_slots[i].fru_unit, + status, mc_ok_led, 0); + log_printf(dgettext(TEXT_DOMAIN, "%-5s %s\n"), + mc_nok_led, + slot_occupants[index], 0); + } + } + } else { /* tonga */ + if (scsb_ks_topo.mct_slots[i].fru_unit == 3) { + /* cpu slot */ + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s "), + mcfru_type, + scsb_ks_topo.mct_slots[i].fru_unit, + status, mc_ok_led, 0); + log_printf(dgettext(TEXT_DOMAIN, "%-5s %s\n"), + mc_nok_led, + slot_occupants[index], 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49stemperature(celsius):%d\n"), + BLANK, + pcf8591_ks_temp.value, 0); +#ifdef NEVER + + log_printf(dgettext(TEXT_DOMAIN, + "%49sminimum temperature:%d\n"), + BLANK, + pcf8591_ks_temp.min, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49swarning temp. threshold:%d\n"), + BLANK, + pcf8591_ks_temp.warning_threshold, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49sshutdown temp. threshold:%d\n"), + BLANK, + pcf8591_ks_temp.shutdown_threshold, 0); +#endif /* NEVER */ + } else { + if (fail_drv_prop == 1) { + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s "), + mcfru_type, + scsb_ks_topo.mct_slots[i].fru_unit, + status, mc_ok_led, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%-5s %s\n"), + mc_nok_led, + slot_occupants[index], 0); + + log_printf(dgettext(TEXT_DOMAIN, + "%46s%s\n"), BLANK, PROPS, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49sauto-config=%s\n"), + BLANK, + slot_auto_config[tg_cpu_index+1], + 0); + if (scsb_ks_topo.mct_slots[i].fru_unit + != 3) + tg_cpu_index++; + } else { + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s "), + mcfru_type, + scsb_ks_topo.mct_slots[i].fru_unit, + status, mc_ok_led, 0); + log_printf(dgettext(TEXT_DOMAIN, "%-5s %s\n"), + mc_nok_led, + slot_occupants[index], 0); + } + } + } + /* we first match the correct slot numbers */ + for (s_index = 0; s_index < slot_table_size; s_index++) { + if (slot_table_not_found == 1) { + /* use prom table */ + if (scsb_ks_topo.mct_slots[i].fru_unit == + prom_slot_table[s_index].pslotnum) { + /* + * search for the addr/pci num + * in all slot info structs + */ + for (i1 = 0; i1 < slot_index; + i1++) { + if (prom_slot_table[s_index].pci_devno == + mc_slots_data.mc_slot_info[i1].slot_addr) { + int nd; + log_printf(dgettext(TEXT_DOMAIN, + "%46s%s%s\n"), BLANK, + BOARDTYPE, board_type, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%46s%s\n"), BLANK, DEVS, 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49s%s\n"), BLANK, + PCI_ROOT_AP, 0); + for (nd = 0; + nd < mc_slots_data.mc_slot_info[i1].number_devs; + nd++) { + log_printf(dgettext(TEXT_DOMAIN, "%52s%s\n"), BLANK, + mc_slots_data.mc_slot_info[i1].devs_info[nd], + 0); + } /* for */ + + } /* if */ + + } /* for(i1) */ + + } /* if */ + + } else { + /* use solaris lot table */ + if (fail_syssoft_prop == 1) { + if (scsb_ks_topo.mct_slots[i].fru_unit == + hotswap_slot_table[s_index].pslotnum) { + /* + * search for the addr/pci + * num in all slot info structs + */ + for (i1 = 0; i1 < slot_index; i1++) { + if (hotswap_slot_table[s_index].pci_devno == + mc_slots_data.mc_slot_info[i1].slot_addr) { + int nd; + for (nd = 0; + nd < mc_slots_data.mc_slot_info[i1].number_devs; + nd++) { + log_printf(dgettext(TEXT_DOMAIN, "%49s%s\n"), BLANK, + mc_slots_data.mc_slot_info[i1].devs_info[nd], + 0); + } + } /* if */ + + } /* for(i1) */ + + } /* if */ + + } /* (fail_syssoft_prop == 1) */ + + } /* (slot_table_not_found == 1) */ + + } /* for(s_index) */ + + } /* for */ + mcfru_type = "PDU"; + misc_info = "Power Distribution Unit"; + for (i = 0; i < scsb_ks_topo.max_units[PDU]; ++i) { + if (version_p15_and_p20) { + mc_ok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.blink_leds[1] + & 0xff), PDU1_OK_BIT+i*2) ? BLINK : + (BIT_TEST((scsb_ks_leddata.leds.p15.ok_leds[1] + & 0xff), PDU1_OK_BIT+i*2) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.nok_leds[1] + & 0xff), PDU1_OK_BIT+i*2) ? ON:OFF; + } + switch (scsb_ks_topo.mct_pdu[i].fru_status) { + case FRU_PRESENT: + status = YES; + break; + case FRU_NOT_PRESENT: + status = NO; + break; + case FRU_NOT_AVAILABLE: + status = NA; + break; + default: + status = NA; + break; + } + if (version_p15_and_p20) { + log_printf(dgettext(TEXT_DOMAIN, + "%-10s %-5d %-7s %-5s %-5s %s\n"), + mcfru_type, scsb_ks_topo.mct_pdu[i].fru_unit, + status, mc_ok_led, mc_nok_led, misc_info, 0); + } else { + log_printf(dgettext(TEXT_DOMAIN, + "%-10s %-5d %-7s%18s%s\n"), + mcfru_type, scsb_ks_topo.mct_pdu[i].fru_unit, + status, BLANK, misc_info, 0); + } + } /* for */ + + /* PS */ + mcfru_type = prtdiag_fru_types[PS]; + misc_info = "Power Supply Unit"; + for (i = 0; i < scsb_ks_topo.max_units[PS]; ++i) { + if (version_p15_and_p20) { + mc_ok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.blink_leds[2] + & 0xff), PS1_OK_BIT+i) ? BLINK : + (BIT_TEST((scsb_ks_leddata.leds.p15.ok_leds[2] + & 0xff), PS1_OK_BIT+i) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.nok_leds[2] + & 0xff), PS1_OK_BIT+i) ? ON:OFF; + } else { + /* + * support for 1.0 systems - + * Hack! - should use tables ? + */ + mc_ok_led = + (BIT_TEST((scsb_ks_leddata.leds.p10.ok_leds[2] + & 0xff), 1+i) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p10.nok_leds[2] + & 0xff), 1+i) ? ON:OFF; + } + switch (scsb_ks_topo.mct_ps[i].fru_status) { + case FRU_PRESENT: + status = YES; + break; + case FRU_NOT_PRESENT: + status = NO; + break; + case FRU_NOT_AVAILABLE: + status = NA; + break; + default: + status = NA; + break; + } + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s %-5s %s\n"), + mcfru_type, scsb_ks_topo.mct_ps[i].fru_unit, + status, mc_ok_led, mc_nok_led, + misc_info, 0); + if (scsb_ks_topo.mct_ps[i].fru_status == FRU_PRESENT) { + if (scsb_ks_topo.mct_ps[i].fru_unit == 1) { + log_printf(dgettext(TEXT_DOMAIN, + "%49scondition:%s\n"), BLANK, + ((pcf8574_ks_ps1.ps_ok)? NOK:OK), 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49stemperature:%s\n"), BLANK, + ((pcf8574_ks_ps1.temp_ok)? NOK:OK), 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49sps fan:%s\n"), BLANK, + ((pcf8574_ks_ps1.psfan_ok)? NOK:OK), 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49ssupply:%s\n"), BLANK, + ((pcf8574_ks_ps1.on_state)? OFF:ON), 0); + } else { + log_printf(dgettext(TEXT_DOMAIN, + "%49scondition:%s\n"), BLANK, + ((pcf8574_ks_ps2.ps_ok)? NOK:OK), 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49stemperature:%s\n"), BLANK, + ((pcf8574_ks_ps2.temp_ok)? NOK:OK), 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49sps fan:%s\n"), BLANK, + ((pcf8574_ks_ps2.psfan_ok)? NOK:OK), 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49ssupply:%s\n"), BLANK, + ((pcf8574_ks_ps2.on_state)? OFF:ON), 0); + } /* if */ + } + + } /* for */ + + /* Fan tray */ + mcfru_type = prtdiag_fru_types[FAN]; + misc_info = "Fan Tray"; + for (i = 0; i < scsb_ks_topo.max_units[FAN]; ++i) { + if (version_p15_and_p20) { + mc_ok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.blink_leds[2] + & 0xff), FAN1_OK_BIT+i) ? BLINK : + (BIT_TEST((scsb_ks_leddata.leds.p15.ok_leds[2] + & 0xff), FAN1_OK_BIT+i) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.nok_leds[2] + & 0xff), FAN1_OK_BIT+i) ? ON:OFF; + } else { + /* + * support for 1.0 systems - + * Hack! - should use tables ? + */ + mc_ok_led = + (BIT_TEST((scsb_ks_leddata.leds.p10.ok_leds[3] + & 0xff), 3+i) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p10.nok_leds[3] + & 0xff), 3+i) ? ON:OFF; + } + switch (scsb_ks_topo.mct_fan[i].fru_status) { + case FRU_PRESENT: + status = YES; + break; + case FRU_NOT_PRESENT: + status = NO; + break; + case FRU_NOT_AVAILABLE: + status = NA; + break; + default: + status = NA; + break; + } + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s %-5s %s\n"), + mcfru_type, scsb_ks_topo.mct_fan[i].fru_unit, + status, mc_ok_led, mc_nok_led, + misc_info, 0); + if (scsb_ks_topo.mct_fan[i].fru_status == FRU_PRESENT) { + if (scsb_ks_topo.mct_fan[i].fru_unit == 1) { + log_printf(dgettext(TEXT_DOMAIN, + "%49scondition:%s\n"), BLANK, + ((pcf8574_ks_fant1.fan_ok)? OK:NOK), 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49sfan speed:%s\n"), BLANK, + ((pcf8574_ks_fant1.fanspeed)? HI:LO), 0); + } else { + log_printf(dgettext(TEXT_DOMAIN, + "%49scondition:%s\n"), BLANK, + ((pcf8574_ks_fant2.fan_ok)? OK:NOK), 0); + log_printf(dgettext(TEXT_DOMAIN, + "%49sfan speed:%s\n"), BLANK, + ((pcf8574_ks_fant2.fanspeed)? HI:LO), 0); + } + } + + } /* for */ + + /* DISKS */ + for (i = 0; i < scsb_ks_topo.max_units[DISK]; ++i) { + if (scsb_ks_topo.mct_disk[i].fru_unit != RMM_NUMBER) + mcfru_type = prtdiag_fru_types[DISK]; + else + mcfru_type = "RMM "; + switch (scsb_ks_topo.mct_disk[i].fru_status) { + case FRU_PRESENT: + status = YES; + break; + case FRU_NOT_PRESENT: + status = NO; + break; + case FRU_NOT_AVAILABLE: + status = NA; + break; + default: + status = NA; + break; + } + if (version_p15_and_p20) { + mc_ok_led = + BIT_TEST((scsb_ks_leddata.scb_led_regs[8] + & 0xff), DISK1_OK_BIT+i) ? BLINK : + (BIT_TEST((scsb_ks_leddata.leds.p15.ok_leds[2] + & 0xff), DISK1_OK_BIT+i) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p15.nok_leds[2] + & 0xff), DISK1_OK_BIT+i) ? ON:OFF; + } else { + /* + * support for 1.0 systems - + * Hack! - should use tables ? + */ + mc_ok_led = + (BIT_TEST((scsb_ks_leddata.leds.p10.ok_leds[2] + & 0xff), DISK1_OK_BIT+i) ? ON:OFF); + mc_nok_led = + BIT_TEST((scsb_ks_leddata.leds.p10.nok_leds[2] + & 0xff), DISK1_OK_BIT+i) ? ON:OFF; + } + /* print everything except condition */ + if (scsb_ks_topo.mct_disk[i].fru_unit != RMM_NUMBER) { + misc_info = "Hard Disk Drive"; + log_printf(dgettext(TEXT_DOMAIN, + "%10s %-5d %-7s %-5s %-5s %s\n"), + mcfru_type, scsb_ks_topo.mct_disk[i].fru_unit-1, + status, mc_ok_led, mc_nok_led, misc_info, 0); + } else { + misc_info = "Removable Media Module"; + log_printf(dgettext(TEXT_DOMAIN, + "%10s %5s %-7s %-5s %-5s %s\n"), + mcfru_type, BLANK, + status, mc_ok_led, mc_nok_led, misc_info, 0); + } + + /* find out fru health from the SCSI drivers */ + if (scsb_ks_topo.mct_disk[i].fru_status == FRU_PRESENT) { + switch ( + scsi_disk_status( + scsb_ks_topo.mct_disk[i].fru_unit)) { + case 0: + health = OK; + break; + case 1: + health = NOK; + break; + case -1: + health = UK; + break; + default: + health = NA; + break; + } + log_printf(dgettext(TEXT_DOMAIN, + "%49scondition:%s\n"), BLANK, health, 0); + } + + } /* for */ + + log_printf(dgettext(TEXT_DOMAIN, "\n"), 0); + +} /* display_mc_prtdiag_info() */ + + +void +analyze_pcipci_siblings(di_node_t node) +{ + di_node_t lc_node; + /* we will find all the dev info for slots first */ + lc_node = di_drv_first_node("pci_pci", node); + lc_node = di_child_node(lc_node); + /* we are at "pci" node now */ + do { + if (di_walk_node(lc_node, DI_WALK_CLDFIRST, + NULL, analyze_nodes) != 0) { + return; + } + } while ((lc_node = di_sibling_node(lc_node)) != DI_NODE_NIL); + + /* now we wll gather info on sysctrl */ + lc_node = di_drv_first_node(SCSB_DEV, node); + if (lc_node != DI_NODE_NIL) + analyze_nodes(lc_node, "sysctrl"); +} /* analyze_pcipci_siblings(.) */ + + +int +analyze_nodes(di_node_t l_node, void *arg) +{ + char *temp; + char *name, *pname; + di_node_t parent; + /* + * we will figure out whether the parent node is "pci" type + * we will save info only in this case as we only want to + * print out the nodes under AP and not others + */ + parent = di_parent_node(l_node); + pname = di_node_name(parent); + name = di_node_name(l_node); + /* + * if this is PCI bridge, we know that this is the AP for slots + * hence, we will save off the address(to convert to slot mapping) + * later, and also we will start saving off slot info struct for + * reporting later + * we will save the immediate childs of this bridge only + */ + if (strcmp(name, "pci") == 0) { + num_devs = 0; + if ((temp = di_bus_addr(l_node)) != NULL) { + mc_slots_data.mc_slot_info[slot_index].slot_addr + = (int)strtol(temp, (char **)NULL, 16); + } + slot_index++; + } else { + if (strcmp(pname, "pci") == 0) { + if ((mc_slots_data.mc_slot_info[slot_index-1].devs_info[num_devs]) + != NULL) { + (void) strcat( + mc_slots_data.mc_slot_info[slot_index-1].devs_info[num_devs], + name); + } else { + (void) strcpy( + mc_slots_data.mc_slot_info[slot_index-1].devs_info[num_devs], + name); + } /* if ((mc_slots_data.mc_slot_inf */ + + num_devs++; + mc_slots_data.mc_slot_info[slot_index-1].number_devs + = num_devs; + } /* if parent is pci */ + + } /* if node is pci */ + if (arg != NULL) { + if (strcmp((char *)arg, "sysctrl") == 0) { + if (dump_prop_list("System", l_node, + di_prop_sys_next)) { + (void) dump_prop_list(NULL, l_node, + di_prop_global_next); + } else { + fail_syssoft_prop = + dump_prop_list(SYSSOFT_PROP, + l_node, di_prop_global_next); + } + + fail_drv_prop = + dump_prop_list(DRV_PROP, l_node, + di_prop_drv_next); + /* + * (void) dump_prop_list("Hardware", + * l_node, di_prop_hw_next); + */ + /* dump_priv_data(l_node); */ + } + } + + return (0); + +} /* analyze_nodes(..) */ + + + +/* + * To get the slot information, + * The OBP defines the 'slot-table' property. But the OS + * can override it with 'hsc-slot-map' property + * through the .conf file. + * Since the formats are different, 2 different property names + * are chosen. + * The OBP property format is + * <phandle>,<pci-devno>,<phys-slotno>,<ga-bits> + * The OS property format is (ga-bits is not used however) + * <busnexus-path>,<pci-devno>,<phys-slotno>,<ga-bits> + * returns 0 on error, 1 otherwise + */ +int +extract_slot_table_from_obp() +{ + if (mc_promopen(O_RDONLY)) { + log_printf(dgettext(TEXT_DOMAIN, + "\ncannot open openprom device"), 0); + return (0); + } + + if (mc_next(0) == 0) + return (0); + mc_walk(mc_next(0)); + + if (close(oprom_fd) < 0) { + log_printf(dgettext(TEXT_DOMAIN, + "\nclose error on %s"), OPENPROMDEV, 0); + return (0); + } + + return (1); + +} /* extract_slot_table_from_obp() */ + + +int +mc_next(int id) +{ + Oppbuf oppbuf; + struct openpromio *opp = &(oppbuf.opp); + + bzero(oppbuf.buf, BUFSIZE); + opp->oprom_size = MAXVALSIZE; + opp->oprom_node = id; + if (ioctl(oprom_fd, OPROMNEXT, opp) < 0) { + log_printf(dgettext(TEXT_DOMAIN, "\nError OPROMNEXT"), 0); + return (0); + } + return (opp->oprom_node); + +} /* mc_next(.) */ + + +void +mc_walk(int id) +{ + int curnode; + mc_dump_node(id); + if (curnode = mc_child(id)) + mc_walk(curnode); + if (curnode = mc_next(id)) + mc_walk(curnode); +} /* mc_walk(.) */ + +int +mc_child(int id) +{ + Oppbuf oppbuf; + struct openpromio *opp = &(oppbuf.opp); + + bzero(oppbuf.buf, BUFSIZE); + opp->oprom_size = MAXVALSIZE; + opp->oprom_node = id; + if (ioctl(oprom_fd, OPROMCHILD, opp) < 0) { + perror("\nOPROMCHILD"); + exit(0); + } + return (opp->oprom_node); + +} /* mc_child(.) */ + + +/* + * Print all properties and values + */ +void +mc_dump_node(int id) +{ + int k; + Oppbuf oppbuf; + hsc_prom_slot_table_t *hpstp; + struct openpromio *opp = &(oppbuf.opp); + + /* get first prop by asking for null string */ + bzero(oppbuf.buf, BUFSIZE); + for (;;) { + /* + * get next property name + */ + opp->oprom_size = MAXNAMESZ; + + if (ioctl(oprom_fd, OPROMNXTPROP, opp) < 0) { + perror("\nOPROMNXTPROP"); + return; + } + if (opp->oprom_size == 0) + break; + if (strcmp(opp->oprom_array, "slot-table") == 0) { + if (mc_getpropval(opp) || opp->oprom_size + == (uint_t)-1) { + log_printf(dgettext(TEXT_DOMAIN, + "\ndata not available"), 0); + return; + } else { + slot_table_size = + opp->oprom_size / + sizeof (hsc_prom_slot_table_t); + hpstp = + (hsc_prom_slot_table_t *)opp->oprom_array; + for (k = 0; k < slot_table_size; k++, hpstp++) { + prom_slot_table[k].pslotnum = + hpstp->pslotnum; + prom_slot_table[k].ga = + hpstp->ga; + prom_slot_table[k].pci_devno = + hpstp->pci_devno; + prom_slot_table[k].phandle = + hpstp->phandle; + } /* for (k = 0; k < slot_table_size; k++) */ + + } + } + } + +} /* mc_dump_node(.) */ + + +int +mc_getpropval(struct openpromio *opp) +{ + opp->oprom_size = MAXVALSIZE; + if (ioctl(oprom_fd, OPROMGETPROP, opp) < 0) { + log_printf(dgettext(TEXT_DOMAIN, "\nError OPROMGETPROP"), 0); + return (1); + } + return (0); + +} /* mc_getpropval(.) */ + + + +/* + * This function returns nothing. + */ +void +alarm_card_occupant() +{ + int scsb_fd; + scsb_ioc_rdwr_t ioc_read; + uint8_t new_mode = 0; + uint8_t old_mode = 0; + uchar_t reg_index; + + if (NULL == scsb_node) { + if (check_platform() == -1) { + return; + } + } + + if (version_p15_and_p20 == 1) + reg_index = 0xe9; /* config status reg offset on SCB */ + else + reg_index = 0xd7; /* config status reg offset on SCB */ + + if ((scsb_fd = open(scsb_node, O_RDONLY)) < 0) { + log_printf(dgettext(TEXT_DOMAIN, + "\n%s open failed"), scsb_node, 0); + return; + } + + /* save off the old mode */ + if (scsb_mode(scsb_fd, GET, &old_mode) == 0) + return; + /* we put scsb in diag mode to read this specific ioctl */ + new_mode = ENVCTRL_DIAG_MODE; + if (scsb_mode(scsb_fd, SET, &new_mode) == 0) + return; + /* now lets read the config register */ + if (scsb_ioc_reg_read(scsb_fd, reg_index, &ioc_read, 1) == 0) + return; + /* restore the original mode */ + if (scsb_mode(scsb_fd, SET, &old_mode) == 0) + return; + alarm_card_present = (BIT_TEST(ioc_read.ioc_rbuf[0]&0xff, 0) ? 1:0); + +} /* alarm_card_occupant() */ + + +/* + * This function changes the SCSB mode to the desired one + * 1 on sucess, 0 otherwise + */ +int +scsb_mode(int fd, scsb_op_t sop, uint8_t *new_mode) +{ + struct strioctl sioc; + + if (sop == GET) + sioc.ic_cmd = ENVC_IOC_GETMODE; + else + sioc.ic_cmd = ENVC_IOC_SETMODE; + + sioc.ic_timout = 0; + sioc.ic_len = sizeof (uint8_t); + sioc.ic_dp = (char *)new_mode; + + + if (ioctl(fd, I_STR, &sioc) == -1) { + log_printf(dgettext(TEXT_DOMAIN, + "\nscsb_mode():scsb ioctl() failed"), 0); + return (0); + } + return (1); + +} /* scsb_mode(...) */ + + +/* + * 1 on success, 0 otherwise + */ +int +scsb_ioc_reg_read(int fd, uchar_t index, scsb_ioc_rdwr_t *ioc_rd, int num) +{ + struct strioctl sioc; + scsb_ioc_rdwr_t *rdwrp; + + rdwrp = ioc_rd; + sioc.ic_timout = 0; + sioc.ic_len = sizeof (scsb_ioc_rdwr_t); + sioc.ic_dp = (char *)rdwrp; + /* setup read command before ioctl */ + sioc.ic_cmd = SCSBIOC_REG_READ; + rdwrp->ioc_wlen = 0; + rdwrp->ioc_rlen = num; + rdwrp->ioc_regindex = index; + if (ioctl(fd, I_STR, &sioc) == -1) { + log_printf(dgettext(TEXT_DOMAIN, + "scsb_ioc_reg_read(): scsb ioctl() failed\n"), 0); + return (0); + } + return (1); + +} /* scsb_ioc_reg_read(....) */ diff --git a/usr/src/lib/libprtdiag_psr/sparc/ontario/Makefile b/usr/src/lib/libprtdiag_psr/sparc/ontario/Makefile new file mode 100644 index 0000000000..1b6dc46d24 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/ontario/Makefile @@ -0,0 +1,92 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/ontario/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= ontario.o erie.o pelton.o + +include ../Makefile.com + +# +# Override the PSR_MACH variable to use sun4v libraries +# +PSR_MACH=sun4v + +IFLAGS += -I ../../../libprtdiag/inc +IFLAGS += -I$(SRC)/cmd/picl/plugins/inc +LDLIBS += -lpicl + +LINTFLAGS += $(IFLAGS) + +PLATFORM=SUNW,Sun-Fire-T200 + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib + +install: all $(USR_PSM_LIBS) $(LINKED_PRTDIAG_DIRS) + +# +# install rules +# + +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +$(USR_PSM_LIB_DIR): + $(INS.dir.root.sys) + +$(LINKED_DIRS): $(USR_PLAT_DIR) + -$(INS.dir.root.sys) + +$(LINKED_LIB_DIRS): $(LINKED_DIRS) + -$(INS.dir.root.sys) + +$(LINKED_PRTDIAG_DIRS): $(USR_PLAT_DIR) + -$(INS.slink6) + +# +# used for message files +# +POFILE= libprtdiag_psr_ontari.po +POFILES= ontari.po + + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/ontario.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/sparc/ontario/common/erie.c b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/erie.c new file mode 100644 index 0000000000..0c48b15736 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/erie.c @@ -0,0 +1,546 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.open2solaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Sun4v Platform specific functions. + * + * called when : + * machine_type == erie + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <kstat.h> +#include <fcntl.h> +#include <string.h> +#include <assert.h> +#include <libintl.h> +#include <note.h> +#include <sys/systeminfo.h> +#include <sys/openpromio.h> +#include <sys/sysmacros.h> +#include <picl.h> +#include "picldefs.h" +#include <pdevinfo.h> +#include <display.h> +#include <display_sun4v.h> +#include <libprtdiag.h> +#include "erie.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + + + +/* + * Add all io picl nodes under pci in io list + */ +/* ARGSUSED */ +int +erie_pci_callback(picl_nodehdl_t pcih, void *args) +{ + int err = PICL_SUCCESS; + picl_nodehdl_t nodeh; + char path[MAXSTRLEN]; + char parent_path[MAXSTRLEN]; + char piclclass[PICL_CLASSNAMELEN_MAX]; + char name[MAXSTRLEN]; + char model[MAXSTRLEN]; + char nac[MAXSTRLEN]; + char bus_type[MAXSTRLEN]; + int slot = NO_SLOT; + + /* Get the parent node's path - used to determine bus type of child */ + err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, parent_path, + sizeof (parent_path)); + if (err != PICL_SUCCESS) { + return (err); + } + + /* Walk through this node's children */ + err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + + /* Get child's class */ + if ((err = erie_get_class(nodeh, piclclass, + sizeof (piclclass))) != PICL_SUCCESS) + return (err); + + /* If this node is a pci bus or bridge, get node's sibling */ + if ((strcmp(piclclass, "pci") == 0 || + (strcmp(piclclass, "pciex") == 0))) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } + + /* + * In order to get certain values, it's necessary + * to search the picl tree. If there's a problem + * with these searches, we'll return the err + */ + if ((err = erie_get_path(nodeh, path, sizeof (path))) + != PICL_SUCCESS) + return (err); + if ((err = erie_get_name(nodeh, name, sizeof (name))) + != PICL_SUCCESS) + return (err); + if ((err = erie_get_model(nodeh, model, sizeof (model))) + != PICL_SUCCESS) + return (err); + erie_get_bus_type(parent_path, bus_type); + slot = erie_get_slot_number(path); + erie_get_nac(bus_type, path, slot, name, nac, sizeof (nac)); + + + /* Print out the data */ + + /* Print NAC */ + log_printf("%-11s", nac); + + /* Print IO Type */ + log_printf("%-6s", bus_type); + + /* Print Slot # */ + if (slot != NO_SLOT) { + log_printf("%5d", slot); + log_printf("%46s", path); + } else { + log_printf("%5s", MOTHERBOARD); + log_printf("%46s", path); + } + + /* Printf Node Name */ + log_printf("%26s", name); + if (strlen(name) > 30) + log_printf(dgettext(TEXT_DOMAIN, "+ ")); + else + log_printf(dgettext(TEXT_DOMAIN, " ")); + + /* Print Card Model */ + log_printf("%-8s", model); + if (strlen(model) > 8) + log_printf(dgettext(TEXT_DOMAIN, "+")); + log_printf("\n"); + + /* Grab the next child under parent node and do it again */ + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + } + return (PICL_WALK_CONTINUE); +} + + +/* + * ---------------------------------------------------------------------------- + */ + +/* + * Add all IO ASIC revisions to list + */ +/* ARGSUSED */ +int +erie_hw_rev_callback(picl_nodehdl_t pcih, void *args) +{ + int err = PICL_SUCCESS; + char path[MAXSTRLEN] = ""; + char nac[MAXSTRLEN]; + char *compatible; + int32_t revision; + + /* Get path of this device */ + err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, path, + sizeof (path)); + if (err != PICL_SUCCESS) { + return (err); + } + /* + * If it's a network dev, then print network info. + * Else if it's not a network dev, check for FIRE ASIC + * Else return PICL_WALK_CONTINUE + */ + if ((strcmp(path, ERIE_NETWORK_0) == 0) || + (strcmp(path, ERIE_NETWORK_1) == 0)) { + (void) snprintf(nac, sizeof (nac), "%s/%s%d", MOTHERBOARD, + OPHIR, 0); + revision = erie_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } else if ((strcmp(path, ERIE_NETWORK_2) == 0) || + (strcmp(path, ERIE_NETWORK_3) == 0)) { + (void) snprintf(nac, sizeof (nac), "%s/%s%d", MOTHERBOARD, + OPHIR, 1); + revision = erie_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } else if ((strcmp(path, ERIE_LSI_PATH) == 0)) { + (void) snprintf(nac, sizeof (nac), "%s/%s", MOTHERBOARD, + SAS_SATA_HBA); + revision = erie_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } else if ((strcmp(path, FIRE0) == 0) || (strcmp(path, FIRE1) == 0)) { + (void) snprintf(nac, sizeof (nac), "%s/%s", MOTHERBOARD, + IOBRIDGE); + revision = erie_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } else if ((strcmp(path, PCIE_PCIX) == 0) || + (strcmp(path, PCIE_PCIE) == 0)) { + (void) snprintf(nac, sizeof (nac), "%s/%s", MOTHERBOARD, + PCI_BRIDGE); + revision = erie_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } else { + return (PICL_WALK_CONTINUE); + } + + /* Get first compatible value from picl compatible list */ + err = erie_get_first_compatible_value(pcih, &compatible); + if (err != PICL_SUCCESS) { + return (err); + } + + /* Print nacation */ + log_printf("%-20s", nac); + + /* Print Device Path */ + log_printf("%41s", path); + + /* Print Compatible # */ + log_printf("%31s", compatible); + free(compatible); + + /* Print Revision */ + log_printf("%6d", revision); + log_printf("\n"); + + return (PICL_WALK_CONTINUE); +} + +/* + * ---------------------------------------------------------------------------- + */ + +/* + * Local functions + */ + + +/* + * This function returns the first picl compatible value + */ +int +erie_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + char *pval; + + err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE, + &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + *outbuf = pval; + return (PICL_SUCCESS); + } + + if (pinfo.type != PICL_PTYPE_TABLE) + return (PICL_FAILURE); + + /* get first string from table */ + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + + *outbuf = pval; + return (PICL_SUCCESS); +} + +/* + * This function returns the revision of + * a device. + */ +int64_t +erie_get_int_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + int8_t int8v; + int16_t int16v; + int32_t int32v; + int64_t int64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int, uint or byte array prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT) && + (pinfo.type != PICL_PTYPE_BYTEARRAY)) { + *ret = PICL_FAILURE; + return (0); + } + + switch (pinfo.size) { + case sizeof (int8_t): + err = picl_get_propval(proph, &int8v, sizeof (int8v)); + *ret = err; + return (int8v); + case sizeof (int16_t): + err = picl_get_propval(proph, &int16v, sizeof (int16v)); + *ret = err; + return (int16v); + case sizeof (int32_t): + err = picl_get_propval(proph, &int32v, sizeof (int32v)); + *ret = err; + return (int32v); + case sizeof (int64_t): + err = picl_get_propval(proph, &int64v, sizeof (int64v)); + *ret = err; + return (int64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} + +/* + * This function fills in the bus type for an IO device. + * If a device hangs off /pci@7c0/pci@0/pci@8, it's on + * the pci-x bus. Otherwise, it's on a pci-e bus. + * + */ +void +erie_get_bus_type(char path[], char bus_type[]) +{ + if (strncmp(path, PCIX_BUS, ERIE_PCIX_COMP) == 0) { + (void) strcpy(bus_type, "PCIX"); + } else { + (void) strcpy(bus_type, "PCIE"); + } +} + +/* + * Thie function indicates whether a device is in a pci-e slot + * or if it's on the motherboard. There's only one pci-e slot + * on erie, everything else is on the motherboard. + * + */ +int +erie_get_slot_number(char path[]) +{ + if (strncmp(path, FIRE0, ERIE_PCIE_COMP) == 0) + return (0); + return (NO_SLOT); +} + +/* + * This function takes a path to one of the on-board + * network devices and returns the instance# of that + * device. + * + */ +int +erie_get_network_instance(char path[]) +{ + + if (strncmp(path, ERIE_NETWORK_1, strlen(ERIE_NETWORK_1)) == 0) { + return (1); + } else if (strncmp(path, ERIE_NETWORK_3, strlen(ERIE_NETWORK_3)) == 0) { + return (3); + } else if (strncmp(path, ERIE_NETWORK_0, strlen(ERIE_NETWORK_0)) == 0) { + return (0); + } else if (strncmp(path, ERIE_NETWORK_2, strlen(ERIE_NETWORK_2)) == 0) { + return (2); + } else { + return (-1); + } +} + +/* + * This function gets the path of a node and + * the error code from the picl API + * + */ +int +erie_get_path(picl_nodehdl_t nodeh, char path[], int size) +{ + int err; + + /* hardware path of this node */ + err = picl_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH, + path, size); + return (err); +} + +/* + * This function returns assings the string passed in + * the value of the picl node's name + * + */ +int +erie_get_name(picl_nodehdl_t nodeh, char name[], int size) +{ + int err; + char *compatible; + char binding_name[MAXSTRLEN]; + char lname[MAXSTRLEN]; + + /* Get this node's name */ + err = picl_get_propval_by_name(nodeh, PICL_PROP_NAME, &lname, size); + if (err == PICL_PROPNOTFOUND) { + (void) strcpy(lname, ""); + err = PICL_SUCCESS; + } + + /* + * If binding_name is found, + * name will be <nodename>-<binding_name> + */ + err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME, + &binding_name, sizeof (binding_name)); + if (err == PICL_SUCCESS) { + if (strcmp(lname, binding_name) != 0) { + (void) strlcat(lname, "-", MAXSTRLEN); + (void) strlcat(lname, binding_name, MAXSTRLEN); + } + /* + * if compatible prop is not found, name will be + * <nodename>-<compatible> + */ + } else if (err == PICL_PROPNOTFOUND) { + err = erie_get_first_compatible_value(nodeh, &compatible); + if (err == PICL_SUCCESS) { + (void) strlcat(lname, "-", MAXSTRLEN); + (void) strlcat(lname, compatible, MAXSTRLEN); + } + err = PICL_SUCCESS; + } else { + return (err); + } + + /* The name was created fine, copy it to name var */ + (void) strcpy(name, lname); + return (err); +} + +/* + * This functions assigns the string passed in the + * the value of the picl node's NAC name. + */ +void +erie_get_nac(char bus_type[], char path[], int slot, char name[], char nac[], + int size) +{ + int instance; + + /* Figure out NAC name and instance, if onboard network node */ + if (strncmp(name, NETWORK, NET_COMP_NUM) == 0) { + instance = erie_get_network_instance(path); + (void) snprintf(nac, size, "%s/%s%d", MOTHERBOARD, + "NET", instance); + } else if (slot != NO_SLOT) { + (void) snprintf(nac, size, "%s/%s%d", MOTHERBOARD, bus_type, + slot); + } else { + (void) snprintf(nac, size, "%s/%s", MOTHERBOARD, bus_type); + } +} + +/* + * This function copies the node's model into model string + * + */ +int +erie_get_model(picl_nodehdl_t nodeh, char model[], int size) +{ + int err; + char tmp_model[MAXSTRLEN]; + + /* Get the model of this node */ + err = picl_get_propval_by_name(nodeh, OBP_PROP_MODEL, + &tmp_model, size); + if (err == PICL_PROPNOTFOUND) { + (void) strcpy(model, ""); + err = PICL_SUCCESS; + } else { + (void) strcpy(model, tmp_model); + } + return (err); +} + +/* + * This function copies the node's class into class string + * + */ +int +erie_get_class(picl_nodehdl_t nodeh, char piclclass[], int size) +{ + int err; + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, size); + return (err); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/ontario/common/erie.h b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/erie.h new file mode 100644 index 0000000000..e507541f89 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/erie.h @@ -0,0 +1,115 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Sun4v Platform header file. + * + * called when : + * machine_type == erie + * + */ + +#ifndef _ERIE_H +#define _ERIE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ERIE_PLATFORM "SUNW,Sun-Fire-T1000" +#define ERIE_PCIE_COMP 8 +#define ERIE_PCIX_COMP 20 +#define NO_SLOT -1 +#define NET_COMP_NUM 3 +#define IOBOARD "IOBD" +#define MOTHERBOARD "MB" +#define OPHIR "GBE" +#define NETWORK "network" +#define ERIE_NETWORK_0 "/pci@7c0/pci@0/network@4" +#define ERIE_NETWORK_1 "/pci@7c0/pci@0/network@4,1" +#define ERIE_NETWORK_2 "/pci@7c0/pci@0/pci@8/network@1" +#define ERIE_NETWORK_3 "/pci@7c0/pci@0/pci@8/network@1,1" +#define PCIX_BUS "/pci@7c0/pci@0/pci@8" +#define PCIE_PCIX "/pci@7c0/pci@0/pci@8" +#define PCIE_PCIE "/pci@7c0/pci@0" +#define ERIE_LSI_PATH "/pci@7c0/pci@0/pci@8/scsi@2" +#define FIRE0 "/pci@780" +#define FIRE1 "/pci@7c0" +#define IOBRIDGE "IO-BRIDGE" +#define PCI_BRIDGE "PCI-BRIDGE" +#define SAS_SATA_HBA "SAS-SATA-HBA" + + + +/* + * Property names + */ +#define OBP_PROP_REG "reg" +#define OBP_PROP_CLOCK_FREQ "clock-frequency" +#define OBP_PROP_BOARD_NUM "board#" +#define OBP_PROP_REVISION_ID "revision-id" +#define OBP_PROP_VERSION_NUM "version#" +#define OBP_PROP_BOARD_TYPE "board_type" +#define OBP_PROP_ECACHE_SIZE "ecache-size" +#define OBP_PROP_IMPLEMENTATION "implementation#" +#define OBP_PROP_MASK "mask#" +#define OBP_PROP_COMPATIBLE "compatible" +#define OBP_PROP_BANNER_NAME "banner-name" +#define OBP_PROP_MODEL "model" +#define OBP_PROP_66MHZ_CAPABLE "66mhz-capable" +#define OBP_PROP_FBC_REG_ID "fbc_reg_id" +#define OBP_PROP_VERSION "version" +#define OBP_PROP_INSTANCE "instance" + +/* + * Function Headers + */ + + +/* local functions */ + +int erie_pci_callback(picl_nodehdl_t pcih, void *args); +int erie_hw_rev_callback(picl_nodehdl_t pcih, void *args); +int erie_get_first_compatible_value(picl_nodehdl_t nodeh, + char **outbuf); +int64_t erie_get_int_propval(picl_nodehdl_t modh, char *prop_name, + int *ret); +void erie_get_bus_type(char path[], char bus_type[]); +void erie_get_nac(char bus_type[], char path[], int s, + char name[], char loc[], int size); +int erie_get_slot_number(char path[]); +int erie_get_network_instance(char path[]); +int erie_get_name(picl_nodehdl_t nodeh, char name[], int size); +int erie_get_model(picl_nodehdl_t nodeh, char model[], int size); +int erie_get_path(picl_nodehdl_t nodeh, char path[], int size); +int erie_get_class(picl_nodehdl_t nodeh, char piclclass[], int size); +#ifdef __cplusplus +} +#endif + +#endif /* _ERIE_H */ diff --git a/usr/src/lib/libprtdiag_psr/sparc/ontario/common/ontario.c b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/ontario.c new file mode 100644 index 0000000000..4cbe33a73d --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/ontario.c @@ -0,0 +1,649 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Sun4v Platform specific functions. + * + * called when : + * machine_type == ontario + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <kstat.h> +#include <fcntl.h> +#include <string.h> +#include <assert.h> +#include <libintl.h> +#include <note.h> +#include <sys/systeminfo.h> +#include <sys/openpromio.h> +#include <sys/sysmacros.h> +#include <picl.h> +#include "picldefs.h" +#include <pdevinfo.h> +#include <display.h> +#include <display_sun4v.h> +#include <libprtdiag.h> +#include "ontario.h" +#include "erie.h" +#include "pelton.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime + */ +void sun4v_display_pci(picl_nodehdl_t plafh); +void sun4v_display_diaginfo(int flag, Prom_node *root, picl_nodehdl_t plafh); + + +/* local functions */ +static void sun4v_display_hw_revisions(Prom_node *root, picl_nodehdl_t plafh); +static int ontario_pci_callback(picl_nodehdl_t pcih, void *args); +static int ontario_get_first_compatible_value(picl_nodehdl_t nodeh, + char **outbuf); +static int64_t ontario_get_int_propval(picl_nodehdl_t modh, char *prop_name, + int *ret); + +static void +get_bus_type(char *path, struct io_card *card) +{ + if (strncmp(path, PCIX_SLOT0, PCIX_COMP_NUM) == 0) { + (void) strcpy(card->bus_type, "PCIX"); + } else { + (void) strcpy(card->bus_type, "PCIE"); + } +} + +static void +get_slot_number(char *path, struct io_card *card) +{ + if (strncmp(path, PCIE_SLOT0, PCIE_COMP_NUM) == 0) { + (void) strcpy(card->slot_str, "0"); + card->slot = 0; + } else if (strncmp(path, PCIE_SLOT1, PCIE_COMP_NUM) == 0) { + (void) strcpy(card->slot_str, "1"); + card->slot = 1; + } else if (strncmp(path, PCIE_SLOT2, PCIE_COMP_NUM) == 0) { + (void) strcpy(card->slot_str, "2"); + card->slot = 2; + } else if (strncmp(path, PCIX_SLOT1, strlen(PCIX_SLOT1)) == 0) { + (void) strcpy(card->slot_str, "PCIX"); + card->slot = -1; + } else if (strncmp(path, PCIX_SLOT0, strlen(PCIX_SLOT0)) == 0) { + (void) strcpy(card->slot_str, "PCIX"); + card->slot = -1; + } else { + (void) strcpy(card->slot_str, IOBOARD); + card->slot = -1; + } +} + +static int +ontario_get_network_instance(char *path) +{ + if (strncmp(path, NETWORK_1_PATH, strlen(NETWORK_1_PATH)) == 0) { + return (1); + } else if (strncmp(path, NETWORK_3_PATH, strlen(NETWORK_3_PATH)) == 0) { + return (3); + } else if (strncmp(path, NETWORK_0_PATH, strlen(NETWORK_0_PATH)) == 0) { + return (0); + } else if (strncmp(path, NETWORK_2_PATH, strlen(NETWORK_2_PATH)) == 0) { + return (2); + } else { + return (-1); + } +} +/* + * add all io devices under pci in io list + */ +/* ARGSUSED */ +static int +ontario_pci_callback(picl_nodehdl_t pcih, void *args) +{ + int err = PICL_SUCCESS; + picl_nodehdl_t nodeh; + char path[MAXSTRLEN]; + char parent_path[MAXSTRLEN]; + char piclclass[PICL_CLASSNAMELEN_MAX]; + char name[MAXSTRLEN]; + char model[MAXSTRLEN]; + char *compatible; + char binding_name[MAXSTRLEN]; + struct io_card pci_card; + int32_t instance; + + err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, parent_path, + sizeof (parent_path)); + if (err != PICL_SUCCESS) { + return (err); + } + + /* Walk through the children */ + + err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, "pciex") == 0) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } + + if (strcmp(piclclass, PICL_CLASS_PCI) == 0) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } + + err = picl_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH, + path, sizeof (path)); + if (err != PICL_SUCCESS) { + return (err); + } + + (void) strlcpy(pci_card.notes, path, sizeof (pci_card.notes)); + + get_bus_type(parent_path, &pci_card); + + get_slot_number(parent_path, &pci_card); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_NAME, &name, + sizeof (name)); + if (err == PICL_PROPNOTFOUND) + (void) strcpy(name, ""); + else if (err != PICL_SUCCESS) + return (err); + + /* Figure NAC name */ + if ((strcmp(name, NETWORK) == 0) && + (strcmp(pci_card.slot_str, IOBOARD) == 0)) { + instance = ontario_get_network_instance(path); + + (void) snprintf(pci_card.status, + sizeof (pci_card.status), "%s/%s%d", IOBOARD, + "NET", instance); + } else { + if (pci_card.slot != -1) { + (void) snprintf(pci_card.status, + sizeof (pci_card.status), "%s/%s%d", + IOBOARD, pci_card.bus_type, pci_card.slot); + } else { + (void) snprintf(pci_card.status, + sizeof (pci_card.status), "%s/%s", IOBOARD, + pci_card.bus_type); + } + } + + /* + * Get the name of this card. If binding_name is found, + * name will be <nodename>-<binding_name> + */ + + err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME, + &binding_name, sizeof (binding_name)); + if (err == PICL_PROPNOTFOUND) { + /* + * if compatible prop is found, name will be + * <nodename>-<compatible> + */ + err = ontario_get_first_compatible_value(nodeh, + &compatible); + if (err == PICL_SUCCESS) { + (void) strlcat(name, "-", MAXSTRLEN); + (void) strlcat(name, compatible, MAXSTRLEN); + free(compatible); + } else if (err != PICL_PROPNOTFOUND) { + return (err); + } + } else if (err != PICL_SUCCESS) { + return (err); + } else if (strcmp(name, binding_name) != 0) { + (void) strlcat(name, "-", MAXSTRLEN); + (void) strlcat(name, binding_name, MAXSTRLEN); + } + + (void) strlcpy(pci_card.name, name, sizeof (pci_card.name)); + + /* Get the model of this card */ + + err = picl_get_propval_by_name(nodeh, OBP_PROP_MODEL, + &model, sizeof (model)); + if (err == PICL_PROPNOTFOUND) + (void) strcpy(model, ""); + else if (err != PICL_SUCCESS) + return (err); + (void) strlcpy(pci_card.model, model, sizeof (pci_card.model)); + + /* Print NAC name */ + log_printf("%-11s", pci_card.status); + /* Print IO Type */ + log_printf("%6s", pci_card.bus_type); + /* Print Slot # */ + log_printf("%5s", pci_card.slot_str); + /* Print Parent Path */ + log_printf("%46.45s", pci_card.notes); + /* Printf Card Name */ + if (strlen(pci_card.name) > 24) + log_printf("%25.24s+", pci_card.name); + else + log_printf("%26s", pci_card.name); + /* Print Card Model */ + if (strlen(pci_card.model) > 10) + log_printf("%10.9s+", pci_card.model); + else + log_printf("%10s", pci_card.model); + log_printf("\n"); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + + } + + return (PICL_WALK_CONTINUE); +} +/* + * display_pci + * Display all the PCI IO cards on this board. + */ +void +sun4v_display_pci(picl_nodehdl_t plafh) +{ + char platbuf[MAXSTRLEN]; + char *fmt = "%-11s %-5s %-4s %-45s %-25s %-8s"; + static int banner = FALSE; /* Have we printed the column headings? */ + + if (banner == FALSE) { + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " IO Configuration "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + log_printf(fmt, "", "IO", "", "", "", "", 0); + log_printf("\n", 0); + log_printf(fmt, "Location", "Type", "Slot", "Path", + "Name", "Model", 0); + log_printf("\n"); + log_printf(fmt, "-----------", "-----", "----", + "---------------------------------------------", + "-------------------------", "---------", 0); + log_printf("\n"); + banner = TRUE; + } + + /* Get platform name, if that fails, use ontario name by default */ + if (sysinfo(SI_PLATFORM, platbuf, sizeof (platbuf)) == -1) { + (void) strcpy(platbuf, ONTARIO_PLATFORM); + } + + /* + * Call functions based on appropriate platform + */ + if ((strncmp(platbuf, ONTARIO_PLATFORM, + strlen(ONTARIO_PLATFORM))) == 0) { + (void) picl_walk_tree_by_class(plafh, "pciex", + "pciex", ontario_pci_callback); + } else if ((strncmp(platbuf, PELTON_PLATFORM, + strlen(PELTON_PLATFORM))) == 0) { + (void) picl_walk_tree_by_class(plafh, "pciex", + "pciex", pelton_pci_callback); + } else { + (void) picl_walk_tree_by_class(plafh, "pciex", "pciex", + erie_pci_callback); + (void) picl_walk_tree_by_class(plafh, "pci", "pci", + erie_pci_callback); + } +} + +/* + * ---------------------------------------------------------------------------- + */ + +/* ARGSUSED */ +void +sun4v_display_diaginfo(int flag, Prom_node *root, picl_nodehdl_t plafh) +{ + /* NOTE(ARGUNUSED(kstats)) */ + /* + * Now display the last powerfail time and the fatal hardware + * reset information. We do this under a couple of conditions. + * First if the user asks for it. The second is if the user + * told us to do logging, and we found a system failure. + */ + if (flag) { + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + /* platform_disp_prom_version(tree); */ + sun4v_display_hw_revisions(root, plafh); + } +} + +/* + * local functions + */ +/* + * add all io devices under pci in io list + */ +/* ARGSUSED */ +static int +ontario_hw_rev_callback(picl_nodehdl_t pcih, void *args) +{ + int err = PICL_SUCCESS; + char path[MAXSTRLEN] = ""; + char device_path[MAXSTRLEN]; + char NAC[MAXSTRLEN]; + char *compatible; + int32_t revision; + int device_found; + + device_found = 0; + + err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, path, + sizeof (path)); + if (err != PICL_SUCCESS) { + return (err); + } + + if ((strcmp(path, NETWORK_0_PATH) == 0) || + (strcmp(path, NETWORK_1_PATH) == 0)) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s%d", IOBOARD, OPHIR, + 0); + revision = ontario_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if ((strcmp(path, NETWORK_2_PATH) == 0) || + (strcmp(path, NETWORK_3_PATH) == 0)) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s%d", IOBOARD, OPHIR, + 1); + revision = ontario_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if ((strcmp(path, FIRE_PATH0) == 0) || + (strcmp(path, FIRE_PATH1) == 0)) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, + "IO-BRIDGE"); + revision = ontario_get_int_propval(pcih, OBP_PROP_VERSION_NUM, + &err); + } + + if ((strcmp(path, PCIX_SLOT0) == 0) || + (strcmp(path, PCIX_SLOT1) == 0)) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, + PCI_BRIDGE); + revision = ontario_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if (strcmp(path, SWITCH_A_PATH) == 0) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, SWITCH_A); + revision = ontario_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if (strcmp(path, SWITCH_B_PATH) == 0) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, SWITCH_B); + revision = ontario_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if (strcmp(path, ONT_LSI_PATH) == 0) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, + SAS_SATA_HBA); + revision = ontario_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + if (device_found == 1) { + (void) strcpy(device_path, path); + err = ontario_get_first_compatible_value(pcih, &compatible); + + /* Print NAC name */ + log_printf("%-20s", NAC); + /* Print Device Path */ + if (strlen(device_path) > 38) + log_printf("%38.37s+", device_path); + else + log_printf("%39s", device_path); + /* Print Compatible # */ + log_printf("%31s", compatible); + free(compatible); + /* Print Revision */ + log_printf("%6d", revision); + log_printf("\n"); + } + + return (PICL_WALK_CONTINUE); +} + +/*ARGSUSED*/ +static void +sun4v_display_hw_revisions(Prom_node *root, picl_nodehdl_t plafh) +{ + Prom_node *pnode; + char *value; + char platbuf[MAXSTRLEN]; + char *fmt = "%-20s %-40s %-30s %-9s"; + + log_printf(dgettext(TEXT_DOMAIN, "\n" + "========================= HW Revisions " + "=======================================\n\n")); + + log_printf(dgettext(TEXT_DOMAIN, + "System PROM revisions:\n" + "----------------------\n")); + + pnode = dev_find_node(root, "openprom"); + if (pnode != NULL) { + value = (char *)get_prop_val(find_prop(pnode, "version")); + log_printf(value); + } + + log_printf(dgettext(TEXT_DOMAIN, "\n\n" + "IO ASIC revisions:\n" + "------------------\n")); + log_printf(fmt, "Location", "Path", "Device", "Revision\n", 0); + log_printf(fmt, "--------------------", + "----------------------------------------", + "------------------------------", + "---------\n", 0); + + /* Get platform name, if that fails, use ontario name by default */ + if (sysinfo(SI_PLATFORM, platbuf, sizeof (platbuf)) == -1) { + (void) strcpy(platbuf, ONTARIO_PLATFORM); + } + + /* + * Walk tree based on platform + */ + if ((strncmp(platbuf, ONTARIO_PLATFORM, + strlen(ONTARIO_PLATFORM))) == 0) { + (void) picl_walk_tree_by_class(plafh, "pciex", + "pciex", ontario_hw_rev_callback); + (void) picl_walk_tree_by_class(plafh, "pci", + "pci", ontario_hw_rev_callback); + (void) picl_walk_tree_by_class(plafh, "network", + "network", ontario_hw_rev_callback); + (void) picl_walk_tree_by_class(plafh, "scsi-2", "scsi-2", + ontario_hw_rev_callback); + } else if ((strncmp(platbuf, PELTON_PLATFORM, + strlen(PELTON_PLATFORM))) == 0) { + (void) picl_walk_tree_by_class(plafh, "pciex", + "pciex", pelton_hw_rev_callback); + (void) picl_walk_tree_by_class(plafh, "pci", + "pci", pelton_hw_rev_callback); + (void) picl_walk_tree_by_class(plafh, "network", + "network", pelton_hw_rev_callback); + (void) picl_walk_tree_by_class(plafh, "scsi-2", "scsi-2", + pelton_hw_rev_callback); + } else { + (void) picl_walk_tree_by_class(plafh, "pciex", "pciex", + erie_hw_rev_callback); + (void) picl_walk_tree_by_class(plafh, "pci", "pci", + erie_hw_rev_callback); + (void) picl_walk_tree_by_class(plafh, "network", "network", + erie_hw_rev_callback); + (void) picl_walk_tree_by_class(plafh, "scsi-2", "scsi-2", + erie_hw_rev_callback); + } +} + +/* + * return the first compatible value + */ +static int +ontario_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + char *pval; + + err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE, + &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + *outbuf = pval; + return (PICL_SUCCESS); + } + + if (pinfo.type != PICL_PTYPE_TABLE) + return (PICL_FAILURE); + + /* get first string from table */ + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + + *outbuf = pval; + return (PICL_SUCCESS); +} + +static int64_t +ontario_get_int_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + int8_t int8v; + int16_t int16v; + int32_t int32v; + int64_t int64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int, uint or byte array prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT) && + (pinfo.type != PICL_PTYPE_BYTEARRAY)) { + *ret = PICL_FAILURE; + return (0); + } + + switch (pinfo.size) { + case sizeof (int8_t): + err = picl_get_propval(proph, &int8v, sizeof (int8v)); + *ret = err; + return (int8v); + case sizeof (int16_t): + err = picl_get_propval(proph, &int16v, sizeof (int16v)); + *ret = err; + return (int16v); + case sizeof (int32_t): + err = picl_get_propval(proph, &int32v, sizeof (int32v)); + *ret = err; + return (int32v); + case sizeof (int64_t): + err = picl_get_propval(proph, &int64v, sizeof (int64v)); + *ret = err; + return (int64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/ontario/common/ontario.h b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/ontario.h new file mode 100644 index 0000000000..cfb5646fec --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/ontario.h @@ -0,0 +1,97 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Sun4v Platform header file. + * + * called when : + * machine_type == Ontario + * + */ + +#ifndef _ONTARIO_H +#define _ONTARIO_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ONTARIO_PLATFORM "SUNW,Sun-Fire-T200" +#define H20_IMPL 0x5678 +#define IS_H20(impl) ((impl) == H20_IMPL) +#define PCIE_COMP_NUM 20 +#define PCIX_COMP_NUM 20 +#define IOBOARD "IOBD" +#define MOTHERBOARD "MB" +#define SWITCH_A "PCI-SWITCH0" +#define SWITCH_B "PCI-SWITCH1" +#define PCI_BRIDGE "PCI-BRIDGE" +#define OPHIR "GBE" +#define NETWORK "network" +#define PCIE "/PCIE" +#define PCIX "/PCIX" +#define FIRE_PATH0 "/pci@780" +#define FIRE_PATH1 "/pci@7c0" +#define SWITCH_A_PATH "/pci@780/pci@0" +#define SWITCH_B_PATH "/pci@7c0/pci@0" +#define NETWORK_0_PATH "/pci@780/pci@0/pci@1/network@0" +#define NETWORK_1_PATH "/pci@780/pci@0/pci@1/network@0,1" +#define NETWORK_2_PATH "/pci@7c0/pci@0/pci@2/network@0" +#define NETWORK_3_PATH "/pci@7c0/pci@0/pci@2/network@0,1" +#define PCIE_SLOT0 "/pci@780/pci@0/pci@8" +#define PCIE_SLOT1 "/pci@7c0/pci@0/pci@8" +#define PCIE_SLOT2 "/pci@7c0/pci@0/pci@9" +#define PCIX_SLOT0 "/pci@7c0/pci@0/pci@1/pci@0,2" +#define PCIX_SLOT1 "/pci@7c0/pci@0/pci@1/pci@0,2" +#define ONT_LSI_PATH "/pci@7c0/pci@0/pci@1/pci@0,2/LSILogic,sas@2" + +/* + * Property names + */ +#define OBP_PROP_REG "reg" +#define OBP_PROP_CLOCK_FREQ "clock-frequency" +#define OBP_PROP_BOARD_NUM "board#" +#define OBP_PROP_REVISION_ID "revision-id" +#define OBP_PROP_VERSION_NUM "version#" +#define OBP_PROP_BOARD_TYPE "board_type" +#define OBP_PROP_ECACHE_SIZE "ecache-size" +#define OBP_PROP_IMPLEMENTATION "implementation#" +#define OBP_PROP_MASK "mask#" +#define OBP_PROP_COMPATIBLE "compatible" +#define OBP_PROP_BANNER_NAME "banner-name" +#define OBP_PROP_MODEL "model" +#define OBP_PROP_66MHZ_CAPABLE "66mhz-capable" +#define OBP_PROP_FBC_REG_ID "fbc_reg_id" +#define OBP_PROP_VERSION "version" +#define OBP_PROP_INSTANCE "instance" + +#ifdef __cplusplus +} +#endif + +#endif /* _ONTARIO_H */ diff --git a/usr/src/lib/libprtdiag_psr/sparc/ontario/common/pelton.c b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/pelton.c new file mode 100644 index 0000000000..f529dbc375 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/pelton.c @@ -0,0 +1,496 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Sun4v Platform specific functions. + * + * called when : + * machine_type == pelton + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <kstat.h> +#include <fcntl.h> +#include <string.h> +#include <assert.h> +#include <libintl.h> +#include <note.h> +#include <sys/systeminfo.h> +#include <sys/openpromio.h> +#include <sys/sysmacros.h> +#include <picl.h> +#include "picldefs.h" +#include <pdevinfo.h> +#include <display.h> +#include <display_sun4v.h> +#include <libprtdiag.h> +#include "pelton.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + + +/* local functions */ +static int pelton_get_first_compatible_value(picl_nodehdl_t nodeh, + char **outbuf); +static int64_t pelton_get_int_propval(picl_nodehdl_t modh, char *prop_name, + int *ret); + +static void +get_bus_type(char *path, struct io_card *card) +{ + if (strncmp(path, PEL_PCIX_SLOT0, PCIX_COMP_NUM) == 0) { + (void) strcpy(card->bus_type, "PCIX"); + } else if (strncmp(path, PEL_PCIX_SLOT1, PCIX_COMP_NUM) == 0) { + (void) strcpy(card->bus_type, "PCIX"); + } else if (strncmp(path, PEL_PCIX_SLOT2, PCIX_COMP_NUM) == 0) { + (void) strcpy(card->bus_type, "PCIX"); + } else if (strncmp(path, PEL_PCIX_SLOT3, PCIX_COMP_NUM) == 0) { + (void) strcpy(card->bus_type, "PCIX"); + } else { + (void) strcpy(card->bus_type, "PCIE"); + } +} + +static void +get_slot_number(char *path, struct io_card *card) +{ + if (strncmp(path, PEL_PCIE_SLOT0, PCIE_COMP_NUM) == 0) { + (void) strcpy(card->slot_str, "0"); + card->slot = 0; + } else if (strncmp(path, PEL_PCIX_SLOT2, strlen(PEL_PCIX_SLOT2)) == 0) { + (void) strcpy(card->slot_str, "PCIX"); + card->slot = -1; + } else if (strncmp(path, PEL_PCIX_SLOT1, strlen(PEL_PCIX_SLOT1)) == 0) { + (void) strcpy(card->slot_str, "PCIX"); + card->slot = -1; + } else if (strncmp(path, PEL_PCIX_SLOT0, strlen(PEL_PCIX_SLOT0)) == 0) { + (void) strcpy(card->slot_str, "PCIX"); + card->slot = -1; + } else { + (void) strcpy(card->slot_str, IOBOARD); + card->slot = -1; + } +} + +static int +pelton_get_network_instance(char *path) +{ + if (strncmp(path, PEL_NETWORK_1_PATH, + strlen(PEL_NETWORK_1_PATH)) == 0) { + return (1); + } else if (strncmp(path, PEL_NETWORK_3_PATH, + strlen(PEL_NETWORK_3_PATH)) == 0) { + return (3); + } else if (strncmp(path, PEL_NETWORK_0_PATH, + strlen(PEL_NETWORK_0_PATH)) == 0) { + return (0); + } else if (strncmp(path, PEL_NETWORK_2_PATH, + strlen(PEL_NETWORK_2_PATH)) == 0) { + return (2); + } else { + return (-1); + } +} +/* + * add all io devices under pci in io list + */ +/* ARGSUSED */ +int +pelton_pci_callback(picl_nodehdl_t pcih, void *args) +{ + int err = PICL_SUCCESS; + picl_nodehdl_t nodeh; + char path[MAXSTRLEN]; + char parent_path[MAXSTRLEN]; + char piclclass[PICL_CLASSNAMELEN_MAX]; + char name[MAXSTRLEN]; + char model[MAXSTRLEN]; + char *compatible; + char binding_name[MAXSTRLEN]; + struct io_card pci_card; + int32_t instance; + + err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, parent_path, + sizeof (parent_path)); + if (err != PICL_SUCCESS) { + return (err); + } + + /* Walk through the children */ + + err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, "pciex") == 0) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } + + if (strcmp(piclclass, PICL_CLASS_PCI) == 0) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } + + err = picl_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH, + path, sizeof (path)); + if (err != PICL_SUCCESS) { + return (err); + } + + (void) strlcpy(pci_card.notes, path, sizeof (pci_card.notes)); + + get_bus_type(parent_path, &pci_card); + + get_slot_number(parent_path, &pci_card); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_NAME, &name, + sizeof (name)); + if (err == PICL_PROPNOTFOUND) + (void) strcpy(name, ""); + else if (err != PICL_SUCCESS) + return (err); + + + /* Figure NAC name */ + if ((strcmp(name, NETWORK) == 0) && + (strcmp(pci_card.slot_str, IOBOARD) == 0)) { + instance = pelton_get_network_instance(path); + + (void) snprintf(pci_card.status, + sizeof (pci_card.status), "%s/%s%d", IOBOARD, + "NET", instance); + } else { + if (pci_card.slot != -1) { + (void) snprintf(pci_card.status, + sizeof (pci_card.status), "%s/%s%d", + IOBOARD, pci_card.bus_type, pci_card.slot); + } else { + (void) snprintf(pci_card.status, + sizeof (pci_card.status), "%s/%s", IOBOARD, + pci_card.bus_type); + } + } + + /* + * Get the name of this card. Iif binding_name is found, + * name will be <nodename>-<binding_name> + */ + + err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME, + &binding_name, sizeof (binding_name)); + if (err == PICL_PROPNOTFOUND) { + /* + * if compatible prop is found, name will be + * <nodename>-<compatible> + */ + err = pelton_get_first_compatible_value(nodeh, + &compatible); + if (err == PICL_SUCCESS) { + (void) strlcat(name, "-", MAXSTRLEN); + (void) strlcat(name, compatible, MAXSTRLEN); + free(compatible); + } else if (err != PICL_PROPNOTFOUND) { + return (err); + } + } else if (err != PICL_SUCCESS) { + return (err); + } else if (strcmp(name, binding_name) != 0) { + (void) strlcat(name, "-", MAXSTRLEN); + (void) strlcat(name, binding_name, MAXSTRLEN); + } + + (void) strlcpy(pci_card.name, name, sizeof (pci_card.name)); + free(name); + + /* Get the model of this card */ + + err = picl_get_propval_by_name(nodeh, OBP_PROP_MODEL, + &model, sizeof (model)); + if (err == PICL_PROPNOTFOUND) + (void) strcpy(model, ""); + else if (err != PICL_SUCCESS) + return (err); + (void) strlcpy(pci_card.model, model, sizeof (pci_card.model)); + + /* Print NAC name */ + log_printf("%-11s", pci_card.status); + /* Print IO Type */ + log_printf("%6s", pci_card.bus_type); + /* Print Slot # */ + log_printf("%5s", pci_card.slot_str); + /* Print Parent Path */ + log_printf("%46.45s", pci_card.notes); + /* Printf Card Name */ + if (strlen(pci_card.name) > 24) + log_printf("%25.24s+", pci_card.name); + else + log_printf("%26s", pci_card.name); + /* Print Card Model */ + if (strlen(pci_card.model) > 10) + log_printf("%10.9s+", pci_card.model); + else + log_printf("%10s", pci_card.model); + log_printf("\n"); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + + } + + return (PICL_WALK_CONTINUE); +} + +/* + * local functions + */ +/* + * add all io devices under pci in io list + */ +/* ARGSUSED */ +int +pelton_hw_rev_callback(picl_nodehdl_t pcih, void *args) +{ + int err = PICL_SUCCESS; + char path[MAXSTRLEN] = ""; + char device_path[MAXSTRLEN]; + char NAC[MAXSTRLEN]; + char *compatible; + int32_t revision; + int device_found; + + device_found = 0; + + err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, path, + sizeof (path)); + if (err != PICL_SUCCESS) { + return (err); + } + + if ((strcmp(path, PEL_NETWORK_0_PATH) == 0) || + (strcmp(path, PEL_NETWORK_1_PATH) == 0)) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s%d", IOBOARD, OPHIR, + 0); + revision = pelton_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if ((strcmp(path, PEL_NETWORK_2_PATH) == 0) || + (strcmp(path, PEL_NETWORK_3_PATH) == 0)) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s%d", IOBOARD, OPHIR, + 1); + revision = pelton_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if ((strcmp(path, FIRE_PATH0) == 0) || + (strcmp(path, FIRE_PATH1) == 0)) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, + "IO-BRIDGE"); + revision = pelton_get_int_propval(pcih, OBP_PROP_VERSION_NUM, + &err); + } + + if ((strcmp(path, PEL_PCIX_SLOT0) == 0) || + (strcmp(path, PEL_PCIX_SLOT1) == 0) || + (strcmp(path, PEL_PCIX_SLOT2) == 0) || + (strcmp(path, PEL_PCIX_SLOT3) == 0)) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, + PCI_BRIDGE); + revision = pelton_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if (strcmp(path, SWITCH_A_PATH) == 0) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, SWITCH_A); + revision = pelton_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if (strcmp(path, SWITCH_B_PATH) == 0) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, SWITCH_B); + revision = pelton_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + + if (strcmp(path, PEL_LSI_PATH) == 0) { + device_found = 1; + (void) snprintf(NAC, sizeof (NAC), "%s/%s", IOBOARD, + PEL_SAS_HBA); + revision = pelton_get_int_propval(pcih, OBP_PROP_REVISION_ID, + &err); + } + if (device_found == 1) { + (void) strcpy(device_path, path); + err = pelton_get_first_compatible_value(pcih, &compatible); + + /* Print NAC name */ + log_printf("%-20s", NAC); + /* Print Device Path */ + if (strlen(device_path) > 38) + log_printf("%38.37s+", device_path); + else + log_printf("%39s", device_path); + /* Print Compatible # */ + log_printf("%31s", compatible); + free(compatible); + /* Print Revision */ + log_printf("%6d", revision); + log_printf("\n"); + } + + return (PICL_WALK_CONTINUE); +} + +/* + * return the first compatible value + */ +static int +pelton_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + char *pval; + + err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE, + &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + *outbuf = pval; + return (PICL_SUCCESS); + } + + if (pinfo.type != PICL_PTYPE_TABLE) + return (PICL_FAILURE); + + /* get first string from table */ + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + + *outbuf = pval; + return (PICL_SUCCESS); +} + +static int64_t +pelton_get_int_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + int8_t int8v; + int16_t int16v; + int32_t int32v; + int64_t int64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int, uint or byte array prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT) && + (pinfo.type != PICL_PTYPE_BYTEARRAY)) { + *ret = PICL_FAILURE; + return (0); + } + + switch (pinfo.size) { + case sizeof (int8_t): + err = picl_get_propval(proph, &int8v, sizeof (int8v)); + *ret = err; + return (int8v); + case sizeof (int16_t): + err = picl_get_propval(proph, &int16v, sizeof (int16v)); + *ret = err; + return (int16v); + case sizeof (int32_t): + err = picl_get_propval(proph, &int32v, sizeof (int32v)); + *ret = err; + return (int32v); + case sizeof (int64_t): + err = picl_get_propval(proph, &int64v, sizeof (int64v)); + *ret = err; + return (int64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/ontario/common/pelton.h b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/pelton.h new file mode 100644 index 0000000000..3951933051 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/ontario/common/pelton.h @@ -0,0 +1,101 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Sun4v Platform header file. + * + * called when : + * machine_type == Pelton + * + */ + +#ifndef _PELTON_H +#define _PELTON_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PELTON_PLATFORM "SUNW,Netra-T2000" +#define PCIE_COMP_NUM 20 +#define PCIX_COMP_NUM 20 +#define IOBOARD "IOBD" +#define MOTHERBOARD "MB" +#define SWITCH_A "PCI-SWITCH0" +#define SWITCH_B "PCI-SWITCH1" +#define PCI_BRIDGE "PCI-BRIDGE" +#define OPHIR "GBE" +#define NETWORK "network" +#define PCIE "/PCIE" +#define PCIX "/PCIX" +#define FIRE_PATH0 "/pci@780" +#define FIRE_PATH1 "/pci@7c0" +#define SWITCH_A_PATH "/pci@780/pci@0" +#define SWITCH_B_PATH "/pci@7c0/pci@0" +#define PEL_NETWORK_0_PATH "/pci@780/pci@0/pci@1/network@0" +#define PEL_NETWORK_1_PATH "/pci@780/pci@0/pci@1/network@0,1" +#define PEL_NETWORK_2_PATH "/pci@7c0/pci@0/pci@2/network@0" +#define PEL_NETWORK_3_PATH "/pci@7c0/pci@0/pci@2/network@0,1" +#define PEL_PCIE_SLOT0 "/pci@7c0/pci@0/pci@8" +#define PEL_PCIX_SLOT0 "/pci@780/pci@0/pci@8/pci@0" +#define PEL_PCIX_SLOT1 "/pci@7c0/pci@0/pci@9/pci@0" +#define PEL_PCIX_SLOT2 "/pci@7c0/pci@0/pci@9/pci@0,2" +#define PEL_PCIX_SLOT3 "/pci@7c0/pci@0/pci@1/pci@0" +#define PEL_LSI_PATH "/pci@780/pci@0/pci@8/pci@0/LSILogic,sas@1" +#define PEL_SAS_HBA "SAS-SATA-HBA" + +/* + * Property names + */ +#define OBP_PROP_REG "reg" +#define OBP_PROP_CLOCK_FREQ "clock-frequency" +#define OBP_PROP_BOARD_NUM "board#" +#define OBP_PROP_REVISION_ID "revision-id" +#define OBP_PROP_VERSION_NUM "version#" +#define OBP_PROP_BOARD_TYPE "board_type" +#define OBP_PROP_ECACHE_SIZE "ecache-size" +#define OBP_PROP_IMPLEMENTATION "implementation#" +#define OBP_PROP_MASK "mask#" +#define OBP_PROP_COMPATIBLE "compatible" +#define OBP_PROP_BANNER_NAME "banner-name" +#define OBP_PROP_MODEL "model" +#define OBP_PROP_66MHZ_CAPABLE "66mhz-capable" +#define OBP_PROP_FBC_REG_ID "fbc_reg_id" +#define OBP_PROP_VERSION "version" +#define OBP_PROP_INSTANCE "instance" + +/* + * Function Headers + */ +int pelton_pci_callback(picl_nodehdl_t pcih, void *args); +int pelton_hw_rev_callback(picl_nodehdl_t pcih, void *args); + +#ifdef __cplusplus +} +#endif + +#endif /* _PELTON_H */ diff --git a/usr/src/lib/libprtdiag_psr/sparc/schumacher/Makefile b/usr/src/lib/libprtdiag_psr/sparc/schumacher/Makefile new file mode 100644 index 0000000000..60290b9370 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/schumacher/Makefile @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/schumacher/Makefile + +PRTDIAG_DIRS= picl + +all := TARGET= all +lint := TARGET= lint +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +_msg := TARGET= _msg + +.KEEP_STATE: + +all lint clean clobber install _msg : $(PRTDIAG_DIRS) + +$(PRTDIAG_DIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + diff --git a/usr/src/lib/libprtdiag_psr/sparc/schumacher/common/schumacher.c b/usr/src/lib/libprtdiag_psr/sparc/schumacher/common/schumacher.c new file mode 100644 index 0000000000..d9ec91a3e6 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/schumacher/common/schumacher.c @@ -0,0 +1,3257 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <alloca.h> +#include <errno.h> +#include <libintl.h> +#include <sys/utsname.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/openpromio.h> +#include <sys/ddi.h> +#include <syslog.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <locale.h> +#include <picl.h> +#include "pdevinfo.h" +#include "display.h" +#include "display_sun4u.h" +#include "picldefs.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +#define EM_INIT_FAIL dgettext(TEXT_DOMAIN,\ + "picl_initialize failed: %s\n") +#define EM_GET_ROOT_FAIL dgettext(TEXT_DOMAIN,\ + "Getting root node failed: %s\n") +#define EM_PRTDIAG_FAIL dgettext(TEXT_DOMAIN, "Prtdiag failed!\n") + +#define SIGN_ON_MSG dgettext(TEXT_DOMAIN,\ + "System Configuration: Sun Microsystems ") +#define SYSCLK_FREQ_MSG dgettext(TEXT_DOMAIN,\ + "System clock frequency: %d MHZ\n") +#define MEM_SIZE_MSG dgettext(TEXT_DOMAIN, "Memory size: ") + +#define DEFAULT_BOARD_NUM 0 +#define DEFAULT_PORTID 0 +#define CLK_FREQ_66MHZ 66 +#define USB -1 +#define HUB -2 + +/* bus id */ +#define PCI_TYPE 1 + +/* + * PICL classes + */ +#define PICL_CLASS_OPTIONS "options" + +/* + * Property names + */ + +#define OBP_PROP_REG "reg" +#define OBP_PROP_CLOCK_FREQ "clock-frequency" +#define OBP_PROP_BOARD_NUM "board#" +#define OBP_PROP_REVISION_ID "revision-id" +#define OBP_PROP_VERSION_NUM "version#" +#define OBP_PROP_BOARD_TYPE "board_type" +#define OBP_PROP_ECACHE_SIZE "ecache-size" +#define OBP_PROP_IMPLEMENTATION "implementation#" +#define OBP_PROP_MASK "mask#" +#define OBP_PROP_COMPATIBLE "compatible" +#define OBP_PROP_BANNER_NAME "banner-name" +#define OBP_PROP_MODEL "model" +#define OBP_PROP_66MHZ_CAPABLE "66mhz-capable" +#define OBP_PROP_FBC_REG_ID "fbc_reg_id" +#define OBP_PROP_VERSION "version" + +#define PROP_POWERFAIL_TIME "powerfail-time" +#define PICL_PROP_LOW_WARNING_THRESHOLD "LowWarningThreshold" + +#define DEFAULT_LINE_WIDTH 78 +#define HEADING_SYMBOL "=" + +#define SIZE_FIELD 11 +#define MAX_IWAYS 32 + +typedef struct bank_list { + picl_nodehdl_t nodeh; + uint32_t iway_count; + uint32_t iway[MAX_IWAYS]; + struct bank_list *next; +} bank_list_t; + +typedef struct { + uint64_t base; + uint64_t size; + int ifactor; + int bank_count; +} seg_info_t; + +static struct io_card *io_card_list = NULL; /* The head of the IO card list */ +static bank_list_t *mem_banks = NULL; +static int mem_xfersize; +static int no_xfer_size = 0; + +static const char *io_device_table[] = { + "block", + "disk", + "cdrom", + "floppy", + "tape", + "network", + "display", + "serial", + "parallel", + "scsi", + "scsi-2", + "scsi-3", + "ide", + "fcal", + "keyboard", + "mouse", + "dma" +}; + +#define NIODEVICE (sizeof (io_device_table) / sizeof (io_device_table[0])) + +static const char *bus_table[] = { + "ebus", + "isa", + "pmu" +}; + +#define NBUS (sizeof (bus_table) / sizeof (bus_table[0])) + +/* + * check if it is an IO deice + * return 1 if this is a io device; return 0 for else. + */ +static int +is_io_device(char *device_class) +{ + int i; + + for (i = 0; i < NIODEVICE; i++) { + if (strcmp(device_class, io_device_table[i]) == 0) + return (1); + } + + return (0); +} + +/* + * check if it is a bus + * return 1 if this is a bus; return 0 for else. + */ +static int +is_bus(char *device_class) +{ + int i; + + for (i = 0; i < NBUS; i++) { + if (strcmp(device_class, bus_table[i]) == 0) + return (1); + } + + return (0); +} + +/* + * search children to get the node by the nodename + * return node handler in picl_nodehdl_t *nodeh + */ +static int +picldiag_get_node_by_name(picl_nodehdl_t rooth, char *name, + picl_nodehdl_t *nodeh) +{ + picl_nodehdl_t childh; + int err; + char *nodename; + + nodename = alloca(strlen(name) + 1); + if (nodename == NULL) + return (PICL_FAILURE); + + err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_NAME, + nodename, (strlen(name) + 1)); + if (err != PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + continue; + } + + if (strcmp(nodename, name) == 0) { + *nodeh = childh; + return (PICL_SUCCESS); + } + + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + } + + return (err); +} + +/* + * get the value by the property name of the string prop + * the value will be in outbuf + * Caller must free the outbuf + */ +static int +picldiag_get_string_propval(picl_nodehdl_t modh, char *prop_name, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + char *prop_value; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + /* + * If it is not a string prop, return NULL + */ + if (pinfo.type != PICL_PTYPE_CHARSTRING) + return (PICL_FAILURE); + + prop_value = malloc(pinfo.size); + if (prop_value == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(proph, prop_value, pinfo.size); + if (err != PICL_SUCCESS) { + free(prop_value); + return (err); + } + + *outbuf = prop_value; + return (PICL_SUCCESS); +} + + +/* + * return the value as a signed integer + */ + +static int64_t +picldiag_get_int_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + int8_t int8v; + int16_t int16v; + int32_t int32v; + int64_t int64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int, uint or byte array prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT) && + (pinfo.type != PICL_PTYPE_BYTEARRAY)) { + *ret = PICL_FAILURE; + return (0); + } + + switch (pinfo.size) { + case sizeof (int8_t): + err = picl_get_propval(proph, &int8v, sizeof (int8v)); + *ret = err; + return (int8v); + case sizeof (int16_t): + err = picl_get_propval(proph, &int16v, sizeof (int16v)); + *ret = err; + return (int16v); + case sizeof (int32_t): + err = picl_get_propval(proph, &int32v, sizeof (int32v)); + *ret = err; + return (int32v); + case sizeof (int64_t): + err = picl_get_propval(proph, &int64v, sizeof (int64v)); + *ret = err; + return (int64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} + +/* + * return the value of the uint prop + */ +static uint64_t +picldiag_get_uint_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + uint8_t uint8v; + uint16_t uint16v; + uint32_t uint32v; + uint64_t uint64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int or uint prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) { + *ret = PICL_FAILURE; + return (0); + } + + /* uint prop */ + + switch (pinfo.size) { + case sizeof (uint8_t): + err = picl_get_propval(proph, &uint8v, sizeof (uint8v)); + *ret = err; + return (uint8v); + case sizeof (uint16_t): + err = picl_get_propval(proph, &uint16v, sizeof (uint16v)); + *ret = err; + return (uint16v); + case sizeof (uint32_t): + err = picl_get_propval(proph, &uint32v, sizeof (uint32v)); + *ret = err; + return (uint32v); + case sizeof (uint64_t): + err = picl_get_propval(proph, &uint64v, sizeof (uint64v)); + *ret = err; + return (uint64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} + +/* + * return the value of the float prop + */ +static float +picldiag_get_float_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + float floatv; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return ((float)0); + } + + /* + * If it is not a float prop, return failure + */ + if (pinfo.type != PICL_PTYPE_FLOAT) { + *ret = PICL_FAILURE; + return ((float)0); + } + + *ret = picl_get_propval(proph, &floatv, sizeof (floatv)); + return (floatv); +} + +/* + * get the clock frequency + */ +static int +picldiag_get_clock_freq(picl_nodehdl_t modh, uint32_t *freq) +{ +#define ROUND_TO_MHZ(x) (((x) + 500000)/ 1000000) + int err; + uint64_t clk_freq; + + clk_freq = picldiag_get_uint_propval(modh, OBP_PROP_CLOCK_FREQ, &err); + if (err != PICL_SUCCESS) + return (err); + + *freq = ROUND_TO_MHZ(clk_freq); + + return (PICL_SUCCESS); +} + +/* + * get the clock frequency from parent + */ +static int +picldiag_get_clock_from_parent(picl_nodehdl_t nodeh, uint32_t *clk) +{ + picl_nodehdl_t parenth; + int err; + + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &parenth, sizeof (parenth)); + + while (err == PICL_SUCCESS) { + err = picldiag_get_clock_freq(parenth, clk); + if (err != PICL_PROPNOTFOUND) + return (err); + + err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT, + &parenth, sizeof (parenth)); + } + + return (err); +} + +/* + * get _fru_parent prop + * If not found, then travese superiors (parent nodes) until + * a _fru_parent property is found. + * If not found, no fru parent + */ +static int +picldiag_get_fru_parent(picl_nodehdl_t nodeh, picl_nodehdl_t *fruparenth) +{ + picl_nodehdl_t fruh; + int err; + + /* find fru parent */ + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_FRU_PARENT, + &fruh, sizeof (fruh)); + + if (err != PICL_SUCCESS) + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_LOC_PARENT, + &fruh, sizeof (fruh)); + + while (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &nodeh, sizeof (nodeh)); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_FRU_PARENT, + &fruh, sizeof (fruh)); + if (err != PICL_SUCCESS) + err = picl_get_propval_by_name(nodeh, + PICL_REFPROP_LOC_PARENT, &fruh, sizeof (fruh)); + } + + if (err == PICL_SUCCESS) + *fruparenth = fruh; + + return (err); +} + +/* + * get label + * + * To get the label, use the following algorithm: + * Lookup "Label" property in the fru node itself. If no + * Label found, then traverse superiors (parent nodes) until + * a Label property is found. + * if not found, then no label + */ +static int +picldiag_get_label(picl_nodehdl_t nodeh, char **label) +{ + int err; + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, label); + + while (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &nodeh, sizeof (nodeh)); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, + label); + } + + return (err); +} + +/* + * get combined label + * + * like picldiag_get_label, except concatenates the labels of parent locations + * eg SB0/P3 for processor P3 on system board SB0 + * + * if caller specifies non-zero label length, label will be cut to specified + * length. + * negative length is left justified, non-negative length is right justified + */ +static int +picldiag_get_combined_label(picl_nodehdl_t nodeh, char **label, int lablen) +{ + int err; + char *ptr; + char *ptr1 = NULL; + char *ptr2; + int len; + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, &ptr1); + if (err != PICL_PROPNOTFOUND && err != PICL_SUCCESS) + return (err); + + for (;;) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &nodeh, sizeof (nodeh)); + if (err == PICL_PROPNOTFOUND) + break; + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, &ptr); + if (err == PICL_SUCCESS) { + if (ptr1 == NULL) { + ptr1 = ptr; + } else { + ptr2 = malloc(strlen(ptr1) + strlen(ptr) + 2); + if (ptr2 == NULL) + return (PICL_FAILURE); + (void) strlcpy(ptr2, ptr, strlen(ptr)-1); + (void) strlcat(ptr2, "/", 1); + (void) strlcat(ptr2, ptr1, strlen(ptr1)-1); + (void) strlcat(ptr2, "\0", 1); + + (void) free(ptr); + (void) free(ptr1); + ptr1 = ptr2; + } + } else if (err != PICL_PROPNOTFOUND) { + return (err); + } + } + + if (ptr1 == NULL) + return (PICL_PROPNOTFOUND); + + len = strlen(ptr1); + /* if no string truncation is desired or required */ + if ((lablen == 0) || (len <= abs(lablen))) { + *label = ptr1; + return (PICL_SUCCESS); + } + + /* string truncation is required; alloc space for (lablen + \0) */ + ptr = malloc(abs(lablen) + 1); + if (ptr == 0) + return (PICL_FAILURE); + if (lablen > 0) { + /* right justification; label = "+<string>\0" */ + strlcpy(ptr, "+", 1); + strlcat(ptr, ptr1 + len - lablen + 1, lablen + 1); + } else { + /* left justification; label = "<string>+\0" */ + strlcpy(ptr, ptr1, abs(lablen) - 1); + strcat(ptr, "+"); + } + + *label = ptr; + return (PICL_SUCCESS); +} + +/* + * return the first compatible value + */ +static int +picldiag_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + char *pval; + + err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE, + &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + *outbuf = pval; + return (PICL_SUCCESS); + } + + if (pinfo.type != PICL_PTYPE_TABLE) + return (PICL_FAILURE); + + /* get first string from table */ + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + + *outbuf = pval; + return (PICL_SUCCESS); +} + +/* + * print the header in the center + */ +static void +logprintf_header(char *header, size_t line_width) +{ + size_t start_pos; + size_t i; + + log_printf("\n"); + start_pos = (line_width - strlen(header) - 2) / 2; + + for (i = 0; i < start_pos; i++) + log_printf("%s", HEADING_SYMBOL); + + log_printf(" %s ", header); + + for (i = 0; i < start_pos; i++) + log_printf("%s", HEADING_SYMBOL); + + log_printf("\n"); +} + +/* + * print the size + */ +static void +logprintf_size(uint64_t size) +{ + + uint64_t kbyte = 1024; + uint64_t mbyte = 1024 * 1024; + uint64_t gbyte = 1024 * 1024 * 1024; + uint64_t residue; + char buf[SIZE_FIELD]; + + if (size >= gbyte) { + residue = size % gbyte; + if (residue == 0) + snprintf(buf, sizeof (buf), "%dGB", + (int)(size / gbyte)); + else + snprintf(buf, sizeof (buf), "%.2fGB", + (float)size / gbyte); + } else if (size >= mbyte) { + residue = size % mbyte; + if (residue == 0) + snprintf(buf, sizeof (buf), "%dMB", + (int)(size / mbyte)); + else + snprintf(buf, sizeof (buf), "%.2fMB", + (float)size / mbyte); + } else { + residue = size % kbyte; + if (residue == 0) + snprintf(buf, sizeof (buf), "%dKB", + (int)(size / kbyte)); + else + snprintf(buf, sizeof (buf), "%.2fKB", + (float)size / kbyte); + } + + log_printf("%-10s ", buf); +} + +/* + * display platform banner + */ +static int +display_platform_banner(picl_nodehdl_t plafh) +{ + char *platform; + char *banner_name; + int err; + + /* + * get PICL_PROP_MACHINE and PICL_PROP_BANNER_NAME + */ + log_printf(SIGN_ON_MSG); + err = picldiag_get_string_propval(plafh, PICL_PROP_MACHINE, + &platform); + if (err != PICL_SUCCESS) + return (err); + log_printf(" %s", platform); + free(platform); + + err = picldiag_get_string_propval(plafh, OBP_PROP_BANNER_NAME, + &banner_name); + if (err != PICL_SUCCESS) + return (err); + log_printf(" %s", banner_name); + free(banner_name); + + log_printf("\n"); + return (PICL_SUCCESS); +} + +/* + * display the clock frequency + */ +static int +display_system_clock(picl_nodehdl_t plafh) +{ + uint32_t system_clk; + int err; + + err = picldiag_get_clock_freq(plafh, &system_clk); + if (err != PICL_SUCCESS) + return (err); + + log_printf(SYSCLK_FREQ_MSG, system_clk); + + return (PICL_SUCCESS); +} + +/* + * callback function to display the memory size + */ +/*ARGSUSED*/ +static int +memory_callback(picl_nodehdl_t memh, void *args) +{ + uint64_t mem_size; + int err; + + log_printf(MEM_SIZE_MSG); + mem_size = picldiag_get_uint_propval(memh, PICL_PROP_SIZE, &err); + if (err == PICL_SUCCESS) + logprintf_size(mem_size); + log_printf("\n"); + no_xfer_size = 0; + mem_xfersize = picldiag_get_uint_propval(memh, PICL_PROP_TRANSFER_SIZE, + &err); + if (err == PICL_PROPNOTFOUND) + no_xfer_size = 1; + return (PICL_WALK_TERMINATE); +} + +/* + * callback function to print cpu information + */ +/*ARGSUSED*/ +static int +cpu_callback(picl_nodehdl_t nodeh, void *args) +{ + int err; + int id; + uint64_t uintval; + uint32_t freq; + char *impl_name; + char *status; + picl_prophdl_t parenth; + char *label; + + /* + * If no ID is found, return + */ + id = picldiag_get_uint_propval(nodeh, PICL_PROP_ID, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + log_printf(" %2d ", id); + + /* + * If no freq is found, return + */ + err = picldiag_get_clock_freq(nodeh, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + log_printf(dgettext(TEXT_DOMAIN, "%4d MHz "), freq); + + /* Ecache size */ + uintval = picldiag_get_uint_propval(nodeh, OBP_PROP_ECACHE_SIZE, &err); + if (err == PICL_PROPNOTFOUND) + log_printf(" - "); + else if (err == PICL_SUCCESS) + logprintf_size(uintval); + else + return (err); + + /* Implementation */ + impl_name = NULL; + err = picldiag_get_string_propval(nodeh, PICL_PROP_NAME, &impl_name); + if (err != PICL_SUCCESS) + log_printf(dgettext(TEXT_DOMAIN, " <unknown> ")); + else + log_printf(" %-22s ", impl_name); + + /* CPU Mask */ + uintval = picldiag_get_uint_propval(nodeh, OBP_PROP_MASK, &err); + if (err == PICL_PROPNOTFOUND) + log_printf(" - "); + else if (err == PICL_SUCCESS) + log_printf("%2lld.%-2lld ", (uintval >> 4) & 0xf, + uintval & 0xf); + else + return (err); + + /* + * Status - if the node has a status property then display that + * otherwise display the State property + */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_STATUS, &status); + if (err == PICL_SUCCESS) { + log_printf("%-12s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != + PICL_PROPVALUNAVAILABLE && err != PICL_ENDOFLIST) { + return (err); + } else { + err = picldiag_get_string_propval(nodeh, + PICL_PROP_STATE, &status); + if (err == PICL_SUCCESS) { + log_printf("%-12s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != + PICL_PROPVALUNAVAILABLE && err != + PICL_ENDOFLIST) { + return (err); + } else { + log_printf(dgettext(TEXT_DOMAIN, "unknown ")); + } + } + + /* + * Location: use label of fru parent + */ + err = picldiag_get_fru_parent(nodeh, &parenth); + if (err == PICL_PROPNOTFOUND) { + log_printf(" - "); + } else if (err == PICL_SUCCESS) { + err = picldiag_get_combined_label(parenth, &label, 12); + if (err == PICL_PROPNOTFOUND) + log_printf(" - "); + else if (err == PICL_SUCCESS) { + log_printf("%s", label); + free(label); + } else + return (err); + } else + return (err); + + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +/* + * display cpu information + */ +static int +display_cpu_info(picl_nodehdl_t plafh) +{ + int err; + + /* + * Display the table header for CPUs . Then display the CPU + * frequency, cache size, and processor revision on all the boards. + */ + logprintf_header(dgettext(TEXT_DOMAIN, "CPUs"), DEFAULT_LINE_WIDTH); + log_printf(dgettext(TEXT_DOMAIN, " E$ CPU" + " CPU\n")); + log_printf(dgettext(TEXT_DOMAIN, + "CPU Freq Size Implementation" + " Mask Status Location\n")); + log_printf("--- -------- ---------- --------------------- " + "----- ------ --------\n"); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_CPU, PICL_CLASS_CPU, + cpu_callback); + return (err); +} + +/* + * Inserts an io_card structure into the list. + */ +static void +add_io_card(uint32_t board, uint32_t bus_id, uint32_t slot, char *label, + uint32_t freq, char *name, char *model, char *status, char *devfs_path) +{ + struct io_card card; + + card.display = 1; + card.board = board; + switch (bus_id) { + case PCI_TYPE: + strlcpy(card.bus_type, PCI_NAME, MAXSTRLEN); + break; + default: /* won't reach here */ + strlcpy(card.bus_type, "", MAXSTRLEN); + break; + } + if (label == NULL) + card.slot = slot; + else { + card.slot = PCI_SLOT_IS_STRING; + (void) strlcpy(card.slot_str, label, MAXSTRLEN); + } + card.freq = freq; + card.status[0] = '\0'; + card.name[0] = '\0'; + card.model[0] = '\0'; + card.notes[0] = '\0'; + if (status != NULL) + strlcpy(card.status, status, MAXSTRLEN); + if (name != NULL) + strlcpy(card.name, name, MAXSTRLEN); + if (model != NULL) + strlcpy(card.model, model, MAXSTRLEN); + if (status != NULL) + strlcpy(card.status, status, MAXSTRLEN); + if (devfs_path != NULL) + strlcpy(card.notes, devfs_path, MAXSTRLEN); + + io_card_list = insert_io_card(io_card_list, &card); +} + +static void +append_to_bank_list(bank_list_t *newptr) +{ + bank_list_t *ptr; + + if (mem_banks == NULL) { + mem_banks = newptr; + return; + } + ptr = mem_banks; + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = newptr; +} + +static void +free_bank_list(void) +{ + bank_list_t *ptr; + bank_list_t *tmp; + + for (ptr = mem_banks; ptr != NULL; ptr = tmp) { + tmp = ptr->next; + free(ptr); + } + mem_banks = NULL; +} + + +/* + * print label for memory module + */ +static int +logprintf_memory_module_label(picl_nodehdl_t moduleh) +{ + picl_nodehdl_t fruparenth; + int err; + char *label; + + err = picldiag_get_fru_parent(moduleh, &fruparenth); + if (err == PICL_PROPNOTFOUND) { + log_printf("-"); + return (PICL_SUCCESS); + } else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruparenth, &label, 30); + if (err == PICL_PROPNOTFOUND) + log_printf("-"); + else if (err == PICL_SUCCESS) { + log_printf("%-15s", label); + free(label); + } else + return (err); + + return (PICL_SUCCESS); +} + +/* + * print the bank id and add the bank handle in the bank list + * return the head of the bank list + */ +static int +membank_callback(picl_nodehdl_t bankh, void *args) +{ + int err; + int64_t id; + uint64_t match; + uint64_t mask; + int i; + bank_list_t *newptr; + seg_info_t *segp = args; + + /* + * print the bank id in the segment table contains column + */ + id = picldiag_get_uint_propval(bankh, PICL_PROP_ID, &err); + if (segp->bank_count > 0) + log_printf(","); + if (err == PICL_PROPNOTFOUND) + log_printf("-"); + else if (err == PICL_SUCCESS) + log_printf("%-lld", id); + else + return (err); + segp->bank_count++; + + /* + * Save the bank information for later (print_bank_table) + */ + newptr = malloc(sizeof (*newptr)); + if (newptr == NULL) + return (PICL_FAILURE); + + newptr->nodeh = bankh; + newptr->iway_count = 0; + newptr->next = NULL; + append_to_bank_list(newptr); + + /* + * Compute the way numbers for the bank + */ + if (no_xfer_size) + return (PICL_WALK_CONTINUE); + + match = picldiag_get_uint_propval(bankh, PICL_PROP_ADDRESSMATCH, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + mask = picldiag_get_uint_propval(bankh, PICL_PROP_ADDRESSMASK, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + i = 0; + while ((i < segp->ifactor) && (newptr->iway_count < MAX_IWAYS)) { + if (((segp->base + i * mem_xfersize) & mask) == match) + newptr->iway[newptr->iway_count++] = i; + ++i; + } + return (PICL_WALK_CONTINUE); +} + + +/* + * find the memory bank and add the bank handle in the bank list + * return the head of the bank list + */ +static int +logprintf_bankinfo(picl_nodehdl_t segh, seg_info_t *segp) +{ + int err; + + log_printf(dgettext(TEXT_DOMAIN, "BankIDs ")); + /* + * find memory-bank + */ + segp->bank_count = 0; + err = picl_walk_tree_by_class(segh, PICL_CLASS_MEMORY_BANK, segp, + membank_callback); + log_printf("\n"); + return (err); +} + +/* + * print the label of memory module or the memory module bank ids + */ +static int +logprintf_seg_contains_col(picl_nodehdl_t nodeh, seg_info_t *segp) +{ + picl_nodehdl_t moduleh; + int err; + + /* + * find memory-module if referenced directly from the memory-segment + * (ie no memory banks) + */ + err = picl_get_propval_by_name(nodeh, PICL_REFPROP_MEMORY_MODULE, + &moduleh, sizeof (moduleh)); + if ((err != PICL_SUCCESS) && (err != PICL_PROPNOTFOUND)) + return (err); + if (err == PICL_SUCCESS) { + err = logprintf_memory_module_label(moduleh); + log_printf("\n"); + return (err); + } + + /* + * memory-module not referenced directly from the memory segment + * so list memory banks instead + */ + err = logprintf_bankinfo(nodeh, segp); + return (err); +} + +/* + * find all memory modules under the given memory module group + * and print its label + */ +static int +logprintf_memory_module_group_info(picl_nodehdl_t memgrph, uint64_t mcid) +{ + int err; + int64_t id; + boolean_t got_status; + picl_nodehdl_t moduleh; + char piclclass[PICL_CLASSNAMELEN_MAX]; + picl_nodehdl_t fruparenth; + char *status; + + id = picldiag_get_uint_propval(memgrph, PICL_PROP_ID, &err); + if (err == PICL_PROPNOTFOUND) + id = -1; + else if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(memgrph, PICL_PROP_CHILD, &moduleh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + /* controller id */ + log_printf("%-8lld ", mcid); + + /* group id */ + if (id == -1) { + log_printf("- "); + } else { + log_printf("%-8lld ", id); + } + + err = picl_get_propval_by_name(moduleh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, PICL_CLASS_MEMORY_MODULE) == 0) { + err = logprintf_memory_module_label(moduleh); + if (err != PICL_SUCCESS) + return (err); + } + + got_status = B_FALSE; + err = picldiag_get_fru_parent(moduleh, &fruparenth); + if (err == PICL_SUCCESS) { + err = picldiag_get_string_propval(fruparenth, + PICL_PROP_OPERATIONAL_STATUS, &status); + if (err == PICL_SUCCESS) { + got_status = B_TRUE; + } else if (err != PICL_PROPNOTFOUND) + return (err); + } else if (err != PICL_PROPNOTFOUND) + return (err); + + if (!got_status) { + err = picldiag_get_string_propval(moduleh, + PICL_PROP_STATUS, &status); + if (err == PICL_SUCCESS) + got_status = B_TRUE; + else if (err != PICL_PROPNOTFOUND) + return (err); + } + if (got_status) { + log_printf("%s", status); + free(status); + } + err = picl_get_propval_by_name(moduleh, PICL_PROP_PEER, + &moduleh, sizeof (picl_nodehdl_t)); + + log_printf("\n"); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + return (err); +} + +/* + * search children to find memory module group under memory-controller + */ +static int +find_memory_module_group(picl_nodehdl_t mch, int *print_header) +{ + picl_nodehdl_t memgrph; + uint64_t mcid; + int err; + char piclclass[PICL_CLASSNAMELEN_MAX]; + + mcid = picldiag_get_uint_propval(mch, OBP_PROP_PORTID, &err); + if (err == PICL_PROPNOTFOUND) + mcid = DEFAULT_PORTID; + else if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(mch, PICL_PROP_CHILD, + &memgrph, sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(memgrph, + PICL_PROP_CLASSNAME, piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, PICL_CLASS_MEMORY_MODULE_GROUP) == 0) { + if (*print_header == 1) { + log_printf( + dgettext(TEXT_DOMAIN, + "\nMemory Module Groups:\n")); + log_printf("--------------------------"); + log_printf("------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "ControllerID GroupID Labels\n")); + log_printf("--------------------------"); + log_printf("------\n"); + *print_header = 0; + } + err = logprintf_memory_module_group_info(memgrph, mcid); + if (err != PICL_SUCCESS) + return (err); + } + + err = picl_get_propval_by_name(memgrph, PICL_PROP_PEER, + &memgrph, sizeof (picl_nodehdl_t)); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + return (err); +} + +/* + * print memory module group table per memory-controller + */ +static int +print_memory_module_group_table(picl_nodehdl_t plafh) +{ + picl_nodehdl_t mch; + int err; + char piclclass[PICL_CLASSNAMELEN_MAX]; + int print_header; + + print_header = 1; + + /* + * find memory-controller + */ + err = picl_get_propval_by_name(plafh, PICL_PROP_CHILD, &mch, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(mch, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + if (strcmp(piclclass, PICL_CLASS_MEMORY_CONTROLLER) != 0) { + err = print_memory_module_group_table(mch); + if (err != PICL_SUCCESS) + return (err); + err = picl_get_propval_by_name(mch, PICL_PROP_PEER, + &mch, sizeof (picl_nodehdl_t)); + continue; + } + + err = find_memory_module_group(mch, &print_header); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(mch, PICL_PROP_PEER, + &mch, sizeof (picl_nodehdl_t)); + } + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + + return (err); +} + +/* + * print bank table + */ +static int +print_bank_table(void) +{ + bank_list_t *ptr; + picl_nodehdl_t bankh; + picl_nodehdl_t memgrph; + picl_nodehdl_t mch; + int err; + int32_t i; + uint64_t size; + int id; + + log_printf(dgettext(TEXT_DOMAIN, "\nBank Table:\n")); + log_printf("---------------------------------------"); + log_printf("--------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, " Physical Location\n")); + log_printf(dgettext(TEXT_DOMAIN, "ID ControllerID GroupID ")); + log_printf(dgettext(TEXT_DOMAIN, "Size Interleave Way\n")); + log_printf("---------------------------------------"); + log_printf("--------------------\n"); + + for (ptr = mem_banks; ptr != NULL; ptr = ptr->next) { + bankh = ptr->nodeh; + id = picldiag_get_uint_propval(bankh, PICL_PROP_ID, &err); + if (err != PICL_SUCCESS) + log_printf("%-8s ", "-"); + else + log_printf("%-8d ", id); + + /* find memory-module-group */ + err = picl_get_propval_by_name(bankh, + PICL_REFPROP_MEMORY_MODULE_GROUP, &memgrph, + sizeof (memgrph)); + if (err == PICL_PROPNOTFOUND) { + log_printf("%-8s ", "-"); + log_printf("%-8s ", "-"); + } else if (err != PICL_SUCCESS) + return (err); + else { + /* + * get controller id + */ + err = picl_get_propval_by_name(memgrph, + PICL_PROP_PARENT, &mch, sizeof (picl_nodehdl_t)); + if (err != PICL_SUCCESS) + return (err); + + id = picldiag_get_uint_propval(mch, OBP_PROP_PORTID, + &err); + if (err == PICL_PROPNOTFOUND) + id = DEFAULT_PORTID; /* use default */ + else if (err != PICL_SUCCESS) + return (err); + + log_printf("%-8d ", id); + + /* get group id */ + id = picldiag_get_uint_propval(memgrph, PICL_PROP_ID, + &err); + if (err == PICL_PROPNOTFOUND) + log_printf("- "); + else if (err == PICL_SUCCESS) + log_printf("%-8d ", id); + else + return (err); + } + + size = picldiag_get_uint_propval(bankh, PICL_PROP_SIZE, &err); + if (err == PICL_PROPNOTFOUND) + log_printf("- "); + else if (err == PICL_SUCCESS) + logprintf_size(size); + else + return (err); + + log_printf(" "); + for (i = 0; i < ptr->iway_count; i++) { + if (i != 0) + log_printf(","); + log_printf("%d", ptr->iway[i]); + } + + log_printf("\n"); + } + return (PICL_SUCCESS); +} + +/* + * callback function to print segment, add the bank in the list and + * return the bank list + */ +/* ARGSUSED */ +static int +memseg_callback(picl_nodehdl_t segh, void *args) +{ + seg_info_t seginfo; + int err; + + /* get base address */ + seginfo.base = picldiag_get_uint_propval(segh, PICL_PROP_BASEADDRESS, + &err); + if (err == PICL_PROPNOTFOUND) { + log_printf("-\n"); + return (PICL_WALK_CONTINUE); + } else if (err == PICL_SUCCESS) + log_printf("0x%-16llx ", seginfo.base); + else + return (err); + + /* get size */ + seginfo.size = picldiag_get_uint_propval(segh, PICL_PROP_SIZE, &err); + if (err == PICL_PROPNOTFOUND) { + log_printf("-\n"); + return (PICL_WALK_CONTINUE); + } else if (err == PICL_SUCCESS) + logprintf_size(seginfo.size); + else + return (err); + + /* get interleave factor */ + seginfo.ifactor = picldiag_get_uint_propval(segh, + PICL_PROP_INTERLEAVE_FACTOR, &err); + + if (err == PICL_PROPNOTFOUND) { + log_printf(" -\n"); + return (PICL_WALK_CONTINUE); + } else if (err == PICL_SUCCESS) + log_printf(" %-2d ", seginfo.ifactor); + else + return (err); + + seginfo.bank_count = 0; + err = logprintf_seg_contains_col(segh, &seginfo); + if (err != PICL_SUCCESS) + return (err); + return (PICL_WALK_CONTINUE); +} + +/* + * search children to find memory-segment and set up the bank list + */ +static int +find_segments(picl_nodehdl_t plafh) +{ + int err; + + log_printf(dgettext(TEXT_DOMAIN, "Segment Table:\n")); + log_printf("------------------------------"); + log_printf("-----------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Base Address Size ")); + log_printf(dgettext(TEXT_DOMAIN, "Interleave Factor Contains\n")); + log_printf("------------------------------"); + log_printf("-----------------------------------------\n"); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT, + NULL, memseg_callback); + return (err); +} + +/* + * display memory configuration + */ +static int +display_memory_config(picl_nodehdl_t plafh) +{ + int err; + + logprintf_header(dgettext(TEXT_DOMAIN, "Memory Configuration"), + DEFAULT_LINE_WIDTH); + + mem_banks = NULL; + err = find_segments(plafh); + + if ((err == PICL_SUCCESS) && (mem_banks != NULL)) + print_bank_table(); + + free_bank_list(); + + return (print_memory_module_group_table(plafh)); +} + +/* + * print the hub device + */ +static int +logprintf_hub_devices(picl_nodehdl_t hubh) +{ + char *name; + int portnum; + char *labelp; + picl_nodehdl_t parenth; + int err; + + err = picldiag_get_string_propval(hubh, PICL_PROP_NAME, &name); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-12.12s ", name); + free(name); + + err = picl_get_propval_by_name(hubh, PICL_REFPROP_LOC_PARENT, &parenth, + sizeof (picl_nodehdl_t)); + + if (err == PICL_SUCCESS) { + /* Read the Label */ + err = picldiag_get_label(parenth, &labelp); + if (err == PICL_SUCCESS) { + log_printf("%s\n", labelp); + free(labelp); + return (PICL_SUCCESS); + } else if (err != PICL_PROPNOTFOUND) { + log_printf("\n"); + return (err); + } + } else if (err != PICL_PROPNOTFOUND) { + log_printf("\n"); + return (err); + } + + /* No Label, try the reg */ + err = picl_get_propval_by_name(hubh, OBP_PROP_REG, &portnum, + sizeof (portnum)); + if (err == PICL_PROPNOTFOUND) + log_printf(" -\n"); + else if (err != PICL_SUCCESS) { + log_printf("\n"); + return (err); + } else + log_printf("%3d\n", portnum); + + return (PICL_SUCCESS); +} + +/* + * callback functions to display hub devices + */ +/* ARGSUSED */ +static int +print_usb_devices(picl_nodehdl_t hubh, void *arg) +{ + picl_nodehdl_t chdh; + char *rootname; + int type = *(int *)arg; + int hubnum; + int err; + + err = picl_get_propval_by_name(hubh, PICL_PROP_CHILD, &chdh, + sizeof (picl_nodehdl_t)); + + /* print header */ + if (err == PICL_SUCCESS) { + err = picldiag_get_string_propval(hubh, PICL_PROP_NAME, + &rootname); + if (err != PICL_SUCCESS) + return (err); + + if (type == USB) { + log_printf("\n==============================="); + log_printf(dgettext(TEXT_DOMAIN, + " %s Devices "), rootname); + } else { + /* Get its hub number */ + err = picl_get_propval_by_name(hubh, + OBP_PROP_REG, &hubnum, sizeof (hubnum)); + if ((err != PICL_SUCCESS) && + (err != PICL_PROPNOTFOUND)) { + free(rootname); + return (err); + } + + log_printf("\n==============================="); + if (err == PICL_SUCCESS) + log_printf(dgettext(TEXT_DOMAIN, + " %s#%d Devices "), + rootname, hubnum); + else + log_printf(dgettext(TEXT_DOMAIN, + " %s Devices "), rootname); + } + + log_printf("===============================\n\n"); + log_printf(dgettext(TEXT_DOMAIN, "Name Port#\n")); + log_printf("------------ -----\n"); + free(rootname); + + do { + logprintf_hub_devices(chdh); + + err = picl_get_propval_by_name(chdh, PICL_PROP_PEER, + &chdh, sizeof (picl_nodehdl_t)); + } while (err == PICL_SUCCESS); + } + + + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback functions to display usb devices + */ +/* ARGSUSED */ +static int +usb_callback(picl_nodehdl_t usbh, void *args) +{ + int err; + int type; + + type = USB; + err = print_usb_devices(usbh, &type); + if (err != PICL_WALK_CONTINUE) + return (err); + type = HUB; + err = picl_walk_tree_by_class(usbh, NULL, &type, print_usb_devices); + if (err == PICL_SUCCESS) + err = PICL_WALK_CONTINUE; + return (err); +} + + +/* + * find usb devices and print its information + */ +static int +display_usb_devices(picl_nodehdl_t plafh) +{ + int err; + + /* + * get the usb node + */ + err = picl_walk_tree_by_class(plafh, PICL_CLASS_USB, NULL, + usb_callback); + return (err); +} + + + +/* + * If nodeh is the io device, add it into the io list and return + * If it is not an io device and it has the subtree, traverse the subtree + * and add all leaf io devices + */ +static int +add_io_leaves(picl_nodehdl_t nodeh, char *parentname, uint32_t board, + uint32_t bus_id, uint64_t slot, uint32_t freq, char *model, char *status) +{ + picl_nodehdl_t childh; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + int err; + char *nameval; + char piclclass[PICL_CLASSNAMELEN_MAX]; + char nodename[MAXSTRLEN]; + char name[MAXSTRLEN]; + char *devfs_path; + char *compatible; + picl_nodehdl_t fruparenth; + char *label; + char binding_name[MAXSTRLEN]; + + err = picl_get_propinfo_by_name(nodeh, PICL_PROP_NAME, &pinfo, + &proph); + if (err != PICL_SUCCESS) + return (err); + + nameval = alloca(pinfo.size); + if (nameval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(proph, nameval, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + (void) strlcpy(nodename, nameval, MAXSTRLEN); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + /* if binding_name is found, name will be <nodename>-<binding_name> */ + err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME, + binding_name, sizeof (binding_name)); + if (err == PICL_PROPNOTFOUND) { + /* + * if compatible prop is found, name will be + * <nodename>-<compatible> + */ + err = picldiag_get_first_compatible_value(nodeh, &compatible); + if (err == PICL_SUCCESS) { + strlcat(nodename, "-", MAXSTRLEN); + strlcat(nodename, compatible, MAXSTRLEN); + free(compatible); + } else if (err != PICL_PROPNOTFOUND) { + return (err); + } + } else if (err != PICL_SUCCESS) { + return (err); + } else if (strcmp(nodename, binding_name) != 0) { + if (strcmp(nodename, piclclass) == 0) { + /* + * nodename same as binding name - + * no need to display twice + */ + strlcpy(nodename, binding_name, MAXSTRLEN); + } else { + strlcat(nodename, "-", MAXSTRLEN); + strlcat(nodename, binding_name, MAXSTRLEN); + } + } + + /* + * If it is an immediate child under pci and not + * a bus node, add it to the io list. + * If it is a child under sub-bus and it is in an io + * device, add it to the io list. + */ + if (((parentname == NULL) && (!is_bus(piclclass))) || + ((parentname != NULL) && (is_io_device(piclclass)))) { + if (parentname == NULL) + (void) snprintf(name, MAXSTRLEN, "%s", nodename); + else + (void) snprintf(name, MAXSTRLEN, "%s/%s", parentname, + nodename); + + /* + * append the class if its class is not a generic + * obp-device class + */ + if (strcmp(piclclass, PICL_CLASS_OBP_DEVICE)) + (void) snprintf(name, MAXSTRLEN, "%s (%s)", name, + piclclass); + + err = picldiag_get_fru_parent(nodeh, &fruparenth); + if (err == PICL_PROPNOTFOUND) { + label = NULL; + } else if (err != PICL_SUCCESS) { + return (err); + } else { + err = picldiag_get_combined_label(fruparenth, &label, + 15); + if (err == PICL_PROPNOTFOUND) + label = NULL; + else if (err != PICL_SUCCESS) + return (err); + } + /* devfs-path */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH, + &devfs_path); + if (err == PICL_PROPNOTFOUND) + devfs_path = NULL; + else if (err != PICL_SUCCESS) + return (err); + + add_io_card(board, bus_id, slot, label, freq, name, + model, status, devfs_path); + if (label != NULL) + free(label); + if (devfs_path != NULL) + free(devfs_path); + return (PICL_SUCCESS); + } + + /* + * If there is any child, Go through each child. + */ + + err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD, + &childh, sizeof (picl_nodehdl_t)); + + /* there is a child */ + while (err == PICL_SUCCESS) { + if (parentname == NULL) + (void) strlcpy(name, nodename, MAXSTRLEN); + else + (void) snprintf(name, MAXSTRLEN, "%s/%s", parentname, + nodename); + + err = add_io_leaves(childh, name, board, bus_id, slot, freq, + model, status); + if (err != PICL_SUCCESS) + return (err); + /* + * get next child + */ + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + } + + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + return (err); +} + + +/* + * add all io devices under pci in io list + */ +/* ARGSUSED */ +static int +pci_callback(picl_nodehdl_t pcih, void *args) +{ + picl_nodehdl_t nodeh; + int err; + char piclclass[PICL_CLASSNAMELEN_MAX]; + uint32_t boardnum; + uint32_t bus_id; + uint32_t slot; + uint32_t freq; + char *model; + char *status; + + /* Fill in common infomation */ + bus_id = PCI_TYPE; + + /* + * Check if it has the freq, if not, + * If not, use its parent's freq + * if its parent's freq is not found, return + */ + err = picldiag_get_clock_freq(pcih, &freq); + if (err == PICL_PROPNOTFOUND) { + err = picldiag_get_clock_from_parent(pcih, &freq); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + } else if (err != PICL_SUCCESS) + return (err); + + /* + * If no board# is found, set boardnum to 0 + */ + boardnum = picldiag_get_uint_propval(pcih, OBP_PROP_BOARD_NUM, &err); + if (err == PICL_PROPNOTFOUND) + boardnum = DEFAULT_BOARD_NUM; + else if (err != PICL_SUCCESS) + return (err); + + /* Walk through the children */ + + err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + piclclass, sizeof (piclclass)); + if (err != PICL_SUCCESS) + return (err); + + /* + * Skip PCI bridge and USB devices because they will be + * processed later + */ + if ((strcmp(piclclass, PICL_CLASS_PCI) == 0) || + (strcmp(piclclass, PICL_CLASS_USB) == 0)) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } + + /* Get the device id for pci card */ + slot = picldiag_get_uint_propval(nodeh, + PICL_PROP_DEVICE_ID, &err); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, + &nodeh, sizeof (picl_nodehdl_t)); + continue; + } else if (err != PICL_SUCCESS) + return (err); + + /* Get the model of this card */ + err = picldiag_get_string_propval(nodeh, OBP_PROP_MODEL, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_string_propval(nodeh, PICL_PROP_STATUS, + &status); + if (err == PICL_PROPNOTFOUND) { + status = malloc(5); + if (status == NULL) + return (PICL_FAILURE); + strlcpy(status, "okay", 5); + } else if (err != PICL_SUCCESS) + return (err); + + err = add_io_leaves(nodeh, NULL, boardnum, bus_id, slot, + freq, model, status); + + if (model != NULL) + free(model); + + if (status != NULL) + free(status); + + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + + } + + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + + return (err); +} + + +/* + * loop through all children and add io devices in io list + */ +static int +process_io_leaves(picl_nodehdl_t rooth) +{ + picl_nodehdl_t nodeh; + char classval[PICL_CLASSNAMELEN_MAX]; + int err; + + err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + classval, sizeof (classval)); + if (err != PICL_SUCCESS) + return (err); + + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + } + + if (err == PICL_PROPNOTFOUND) + return (PICL_SUCCESS); + + return (err); +} + + +/* + * find all io devices and add them in the io list + */ +static int +gather_io_cards(picl_nodehdl_t plafh) +{ + int err; + + /* + * look for io devices under the immediate children of platform + */ + err = process_io_leaves(plafh); + + if (err != PICL_SUCCESS) + return (err); + + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_PCI, + PICL_CLASS_PCI, pci_callback); + if (err != PICL_SUCCESS) + return (err); + return (err); +} + +static void +picldiag_display_io_cards(struct io_card *list) +{ + static int banner = 0; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + if (banner == 0) { + log_printf(dgettext(TEXT_DOMAIN, + "Bus Freq Slot + Name +\n"), 0); + log_printf(dgettext(TEXT_DOMAIN, "Type MHz Status " + "Path " + "Model"), 0); + log_printf("\n", 0); + log_printf("---- ---- ---------- " + "---------------------------- " + "--------------------", 0); + log_printf("\n", 0); + banner = 1; + } + + for (p = list; p != NULL; p = p -> next) { + log_printf("%-4s ", p->bus_type, 0); + log_printf("%3d ", p->freq, 0); + /* + * We check to see if it's an int or + * a char string to display for slot. + */ + if (p->slot == PCI_SLOT_IS_STRING) + log_printf("%10s ", p->slot_str, 0); + else + log_printf("%10d ", p->slot, 0); + + log_printf("%-28.28s", p->name, 0); + if (strlen(p->name) > 28) + log_printf("+ ", 0); + else + log_printf(" ", 0); + log_printf("%-19.19s", p->model, 0); + if (strlen(p->model) > 19) + log_printf("+", 0); + log_printf("\n", 0); + log_printf(" %10s ", p->status, 0); + if (strlen(p->notes) > 0) + log_printf("%s", p->notes, 0); + log_printf("\n\n", 0); + } +} + +/* + * display all io devices + */ +static int +display_io_device_info(picl_nodehdl_t plafh) +{ + int err; + + err = gather_io_cards(plafh); + if (err != PICL_SUCCESS) + return (err); + + logprintf_header(dgettext(TEXT_DOMAIN, "IO Devices"), + DEFAULT_LINE_WIDTH); + + picldiag_display_io_cards(io_card_list); + + free_io_cards(io_card_list); + + return (PICL_SUCCESS); +} + +/* + * print fan device information + */ +static int +logprintf_fan_info(picl_nodehdl_t fanh) +{ + int err; + char *label; + char *unit; + int64_t speed; + int64_t min_speed; + picl_nodehdl_t fruph; + + err = picldiag_get_fru_parent(fanh, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 14); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-14s ", label); + free(label); + + err = picldiag_get_label(fanh, &label); + if (err == PICL_SUCCESS) { + log_printf("%-14s ", label); + free(label); + } else if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf(" - "); + } else + return (err); + + speed = picldiag_get_uint_propval(fanh, PICL_PROP_FAN_SPEED, &err); + if (err == PICL_SUCCESS) { + min_speed = picldiag_get_uint_propval(fanh, + PICL_PROP_LOW_WARNING_THRESHOLD, &err); + if (err != PICL_SUCCESS) + min_speed = 0; + if (speed < min_speed) { + log_printf(dgettext(TEXT_DOMAIN, + "failed (%lld"), speed); + err = picldiag_get_string_propval(fanh, + PICL_PROP_FAN_SPEED_UNIT, &unit); + if (err == PICL_SUCCESS) { + log_printf("%s", unit); + free(unit); + } + log_printf(")"); + } else { + log_printf(dgettext(TEXT_DOMAIN, "okay")); + } + } else { + err = picldiag_get_string_propval(fanh, + PICL_PROP_FAN_SPEED_UNIT, &unit); + if (err == PICL_SUCCESS) { + log_printf("%-12s ", unit); + free(unit); + } + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +fan_callback(picl_nodehdl_t fanh, void *arg) +{ + int *countp = arg; + int err; + + if (*countp == 0) { + log_printf(dgettext(TEXT_DOMAIN, "Fan Status:\n")); + log_printf("---------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Location Sensor Status \n")); + log_printf("---------------------------------------\n"); + } + *countp += 1; + err = logprintf_fan_info(fanh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find fan device and print its speed + */ +static int +display_fan_speed(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_FAN, + &print_header, fan_callback); + return (err); +} + +/* + * print temperature sensor information + */ +static int +logprintf_temp_info(picl_nodehdl_t temph) +{ + int err; + char *label; + int64_t temperature; + int64_t threshold; + picl_nodehdl_t fruph; + char *status = "unknown"; + int got_temp = 0; + + err = picldiag_get_fru_parent(temph, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 14); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-14s ", label); + free(label); + + err = picldiag_get_label(temph, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-14s ", label); + free(label); + + temperature = picldiag_get_int_propval(temph, PICL_PROP_TEMPERATURE, + &err); + if (err == PICL_SUCCESS) { + got_temp = 1; + status = "okay"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_LOW_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature < threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_LOW_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature < threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_HIGH_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature > threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_int_propval(temph, PICL_PROP_HIGH_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_temp && temperature > threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + err = picldiag_get_string_propval(temph, PICL_PROP_CONDITION, &status); + if (err == PICL_SUCCESS) { + log_printf("%s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } else { + log_printf("%s ", status); + if (strcmp(status, "failed") == 0 || + strcmp(status, "warning") == 0) + log_printf("(%.2lldC)", temperature); + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +temp_callback(picl_nodehdl_t temph, void *arg) +{ + int err; + int *countp = arg; + + if (*countp == 0) { + log_printf("\n"); + log_printf("---------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Temperature sensors:\n")); + log_printf("------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Location Sensor Status\n")); + log_printf("------------------------------------\n"); + } + *countp += 1; + err = logprintf_temp_info(temph); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find temp sensors and print the temp + */ +/* ARGSUSED */ +static int +display_temp(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_TEMPERATURE_SENSOR, + &print_header, temp_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_TEMPERATURE_INDICATOR, + &print_header, temp_callback); + return (err); +} + +/* + * print current sensor information + */ +static int +logprintf_current_info(picl_nodehdl_t currenth) +{ + int err; + char *label; + float current; + float threshold; + picl_nodehdl_t fruph; + char *status = "unknown"; + int got_current = 0; + + err = picldiag_get_fru_parent(currenth, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 10); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-10s ", label); + free(label); + + err = picldiag_get_label(currenth, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-10s ", label); + free(label); + + current = picldiag_get_float_propval(currenth, PICL_PROP_CURRENT, &err); + if (err == PICL_SUCCESS) { + status = "okay"; + got_current = 1; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, PICL_PROP_LOW_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_current && current < threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, PICL_PROP_LOW_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_current && current < threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, PICL_PROP_HIGH_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_current && current > threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(currenth, + PICL_PROP_HIGH_SHUTDOWN, &err); + if (err == PICL_SUCCESS) { + if (got_current && current > threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + err = picldiag_get_string_propval(currenth, + PICL_PROP_CONDITION, &status); + if (err == PICL_SUCCESS) { + log_printf(" %s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } else { + log_printf("%s ", status); + if (strcmp(status, "failed") == 0 || + strcmp(status, "warning") == 0) + log_printf("(%.2fA)", current); + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +current_callback(picl_nodehdl_t currh, void *arg) +{ + int err; + int *countp = arg; + + if (*countp == 0) { + log_printf("------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Current sensors:\n")); + log_printf("------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Location Sensor Status\n")); + log_printf("------------------------------\n"); + } + *countp += 1; + err = logprintf_current_info(currh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find curr sensors and print the curr + */ +/* ARGSUSED */ +static int +display_current(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_CURRENT_SENSOR, + &print_header, current_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_CURRENT_INDICATOR, + &print_header, current_callback); + return (err); +} + +/* + * print voltage sensor information + */ +static int +logprintf_voltage_info(picl_nodehdl_t voltageh) +{ + int err; + char *label; + float voltage; + float threshold; + picl_nodehdl_t fruph; + char *status = "unknown"; + int got_voltage = 0; + + err = picldiag_get_fru_parent(voltageh, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 10); + if (err != PICL_SUCCESS) + return (err); + + log_printf("%-10s ", label); + free(label); + + err = picldiag_get_label(voltageh, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-12s ", label); + free(label); + + voltage = picldiag_get_float_propval(voltageh, PICL_PROP_VOLTAGE, &err); + if (err == PICL_SUCCESS) { + status = "okay"; + got_voltage = 1; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, PICL_PROP_LOW_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage < threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, PICL_PROP_LOW_SHUTDOWN, + &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage < threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, PICL_PROP_HIGH_WARNING, + &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage > threshold) + status = "warning"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + threshold = picldiag_get_float_propval(voltageh, + PICL_PROP_HIGH_SHUTDOWN, &err); + if (err == PICL_SUCCESS) { + if (got_voltage && voltage > threshold) + status = "failed"; + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } + + err = picldiag_get_string_propval(voltageh, + PICL_PROP_CONDITION, &status); + if (err == PICL_SUCCESS) { + log_printf("%s", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + return (err); + } else { + log_printf("%s ", status); + if (strcmp(status, "warning") == 0 || + strcmp(status, "failed") == 0) + log_printf("(%.2fV)", voltage); + } + + log_printf("\n"); + return (PICL_SUCCESS); +} + +static int +voltage_callback(picl_nodehdl_t voltageh, void *arg) +{ + int *countp = arg; + int err; + + if (*countp == 0) { + log_printf("--------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Voltage sensors:\n")); + log_printf("-------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Location Sensor Status\n")); + log_printf("-------------------------------\n"); + } + *countp += 1; + err = logprintf_voltage_info(voltageh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find voltage sensors and print voltage + */ +/* ARGSUSED */ +static int +display_voltage(picl_nodehdl_t plafh) +{ + int err; + int print_header; + + print_header = 0; + err = picl_walk_tree_by_class(plafh, PICL_CLASS_VOLTAGE_SENSOR, + &print_header, voltage_callback); + if (err != PICL_SUCCESS) + return (err); + err = picl_walk_tree_by_class(plafh, PICL_CLASS_VOLTAGE_INDICATOR, + &print_header, voltage_callback); + return (err); +} + +/* + * print led device information + */ +static int +logprintf_led_info(picl_nodehdl_t ledh) +{ + int err; + char *label; + char *state; + char *color; + picl_nodehdl_t fruph; + + err = picldiag_get_fru_parent(ledh, &fruph); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_combined_label(fruph, &label, 10); + if (err != PICL_SUCCESS) { + log_printf(" - ", label); + } else { + log_printf("%-10s ", label); + free(label); + } + + err = picldiag_get_label(ledh, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-20s ", label); + free(label); + + err = picldiag_get_string_propval(ledh, PICL_PROP_STATE, &state); + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf(" - "); + } else if (err != PICL_SUCCESS) { + return (err); + } else { + log_printf("%-10s ", state); + free(state); + } + + err = picldiag_get_string_propval(ledh, PICL_PROP_COLOR, &color); + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf("\n"); + } else if (err != PICL_SUCCESS) { + return (err); + } else { + log_printf("%-16s\n", color); + free(color); + } + + return (PICL_SUCCESS); +} + +static int +led_callback(picl_nodehdl_t ledh, void *arg) +{ + int *countp = arg; + int err; + + if (*countp == 0) { + + log_printf("--------------------------------------" + "------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Led State:\n")); + log_printf("--------------------------------------" + "------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Location Led State" + " Color\n")); + log_printf("--------------------------------------" + "------------\n"); + } + *countp += 1; + err = logprintf_led_info(ledh); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * callback function search children to find led devices and print status + */ +/* ARGSUSED */ +static int +display_led_status(picl_nodehdl_t plafh) +{ + int print_header; + + print_header = 0; + picl_walk_tree_by_class(plafh, PICL_CLASS_LED, + &print_header, led_callback); + return (PICL_SUCCESS); +} + +/* + * print keyswitch device information + */ +static int +logprintf_keyswitch_info(picl_nodehdl_t keyswitchh, picl_nodehdl_t fruph) +{ + int err; + char *label; + char *state; + + err = picldiag_get_combined_label(fruph, &label, 10); + if (err != PICL_SUCCESS) { + log_printf("%-14s", " -"); + } else { + log_printf("%-14s ", label); + free(label); + } + + err = picldiag_get_label(keyswitchh, &label); + if (err != PICL_SUCCESS) + return (err); + log_printf("%-11s ", label); + free(label); + + err = picldiag_get_string_propval(keyswitchh, PICL_PROP_STATE, &state); + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) { + log_printf(" -\n"); + } else if (err != PICL_SUCCESS) { + return (err); + } else { + log_printf("%s\n", state); + free(state); + } + + return (PICL_SUCCESS); +} + +static int +keyswitch_callback(picl_nodehdl_t keyswitchh, void *arg) +{ + int *countp = arg; + int err; + picl_nodehdl_t fruph; + + /* + * Tamale simulates a key-switch on ENxS. So the presence of a + * node of class keyswitch is not sufficient. If it has a fru parent + * or location parent, then believe it. + */ + err = picl_get_propval_by_name(keyswitchh, PICL_REFPROP_FRU_PARENT, + &fruph, sizeof (fruph)); + if (err == PICL_PROPNOTFOUND) { + err = picl_get_propval_by_name(keyswitchh, + PICL_REFPROP_LOC_PARENT, &fruph, sizeof (fruph)); + } + if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) + return (PICL_WALK_CONTINUE); + if (err != PICL_SUCCESS) + return (err); + + if (*countp == 0) { + log_printf("-----------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Keyswitch:\n")); + log_printf("-----------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Location Keyswitch State\n")); + log_printf("-----------------------------------------\n"); + } + *countp += 1; + err = logprintf_keyswitch_info(keyswitchh, fruph); + if (err == PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + return (err); +} + +/* + * search children to find keyswitch device(s) and print status + */ +/* ARGSUSED */ +static int +display_keyswitch(picl_nodehdl_t plafh) +{ + int print_header = 0; + + picl_walk_tree_by_class(plafh, PICL_CLASS_KEYSWITCH, + &print_header, keyswitch_callback); + return (PICL_SUCCESS); +} + +/* + * display environment status + */ +static int +display_envctrl_status(picl_nodehdl_t plafh) +{ + logprintf_header(dgettext(TEXT_DOMAIN, "Environmental Status"), + DEFAULT_LINE_WIDTH); + + display_fan_speed(plafh); + display_temp(plafh); + display_current(plafh); + display_voltage(plafh); + display_keyswitch(plafh); + display_led_status(plafh); + + return (PICL_SUCCESS); +} + +/* + * print fru operational status + */ +static int +logprintf_fru_oper_status(picl_nodehdl_t fruh, int *countp) +{ + int err; + char *label; + char *status; + + err = picldiag_get_combined_label(fruh, &label, 15); + if (err != PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + + err = picldiag_get_string_propval(fruh, + PICL_PROP_OPERATIONAL_STATUS, &status); + if (err == PICL_SUCCESS) { + if (*countp == 0) { + logprintf_header(dgettext(TEXT_DOMAIN, + "FRU Operational Status"), + DEFAULT_LINE_WIDTH); + log_printf("-------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Fru Operational Status:\n")); + log_printf("-------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, + "Location Status \n")); + log_printf("-------------------------\n"); + } + *countp += 1; + log_printf("%-15s ", label); + free(label); + log_printf("%s\n", status); + free(status); + } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) { + free(label); + return (err); + } else { + free(label); + } + return (PICL_WALK_CONTINUE); +} + +static int +fru_oper_status_callback(picl_nodehdl_t fruh, void *arg) +{ + int err; + + err = logprintf_fru_oper_status(fruh, (int *)arg); + return (err); +} + +/* + * display fru operational status + */ +static int +display_fru_oper_status(picl_nodehdl_t frutreeh) +{ + int print_header; + + print_header = 0; + picl_walk_tree_by_class(frutreeh, PICL_CLASS_FRU, + &print_header, fru_oper_status_callback); + return (PICL_SUCCESS); +} + +/* + * check if the node having the version prop + * If yes, print its nodename and version + */ +/* ARGSUSED */ +static int +asicrev_callback(picl_nodehdl_t nodeh, void *arg) +{ + uint32_t version; + char *name; + char *model; + char *status; + int err; + + version = picldiag_get_uint_propval(nodeh, OBP_PROP_VERSION_NUM, + &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + /* devfs-path */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH, &name); + if (err == PICL_PROPNOTFOUND) + name = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* model */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_BINDING_NAME, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* status */ + err = picldiag_get_string_propval(nodeh, PICL_PROP_STATUS, &status); + if (err == PICL_PROPNOTFOUND) + status = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* + * Display the data + */ + + /* name */ + if (name != NULL) { + log_printf("%-22s ", name); + free(name); + } else + log_printf("%-22s ", "unknown"); + /* model */ + if (model != NULL) { + log_printf("%-15s ", model); + free(model); + } else + log_printf("%-15s ", "unknown"); + /* status */ + if (status == NULL) + log_printf("%-15s ", "okay"); + else { + log_printf("%-15s ", status); + free(status); + } + /* revision */ + log_printf(" %-4d\n", version); + + return (PICL_WALK_CONTINUE); +} + +/* + * traverse the tree to display asic revision id for ebus + */ +/* ARGSUSED */ +static int +ebus_callback(picl_nodehdl_t ebush, void *arg) +{ + uint32_t id; + char *name; + int err; + char *model; + char *status; + + id = picldiag_get_uint_propval(ebush, OBP_PROP_REVISION_ID, &err); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_CONTINUE); + else if (err != PICL_SUCCESS) + return (err); + + /* devfs-path */ + err = picldiag_get_string_propval(ebush, PICL_PROP_DEVFS_PATH, &name); + if (err == PICL_PROPNOTFOUND) + name = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* model */ + err = picldiag_get_string_propval(ebush, PICL_PROP_BINDING_NAME, + &model); + if (err == PICL_PROPNOTFOUND) + model = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* status */ + err = picldiag_get_string_propval(ebush, PICL_PROP_STATUS, &status); + if (err == PICL_PROPNOTFOUND) + status = NULL; + else if (err != PICL_SUCCESS) + return (err); + + /* + * Display the data + */ + + /* name */ + if (name != NULL) { + log_printf("%-22s ", name); + free(name); + } else + log_printf("%-22s ", "unknown"); + /* model */ + if (model != NULL) { + log_printf("%-15s ", model); + free(model); + } else + log_printf("%-15s ", "unknown"); + /* status */ + if (status == NULL) + log_printf("%-15s ", "okay"); + else { + log_printf("%-15s ", status); + free(status); + } + /* revision */ + log_printf(" %-4d\n", id); + + return (PICL_WALK_CONTINUE); +} + +/* + * display asic revision id + */ +static int +display_hw_revisions(picl_nodehdl_t plafh) +{ + int err; + + /* Print the header */ + logprintf_header(dgettext(TEXT_DOMAIN, "HW Revisions"), + DEFAULT_LINE_WIDTH); + + log_printf(dgettext(TEXT_DOMAIN, "ASIC Revisions:\n")); + log_printf("-----------------------------"); + log_printf("--------------------------------------\n"); + log_printf(dgettext(TEXT_DOMAIN, "Path Device")); + log_printf(dgettext(TEXT_DOMAIN, + " Status Revision\n")); + log_printf("-----------------------------"); + log_printf("--------------------------------------\n"); + + err = picl_walk_tree_by_class(plafh, NULL, NULL, asicrev_callback); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_EBUS, + NULL, ebus_callback); + if (err != PICL_SUCCESS) + return (err); + + log_printf("\n"); + + return (err); +} + +/* + * find the options node and its powerfail_time prop + * If found, display the list of latest powerfail. + */ +/* ARGSUSED */ +static int +options_callback(picl_nodehdl_t nodeh, void *arg) +{ + time_t value; + char *failtime; + int err; + + err = picldiag_get_string_propval(nodeh, PROP_POWERFAIL_TIME, + &failtime); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_TERMINATE); + else if (err != PICL_SUCCESS) + return (err); + + value = (time_t)atoi(failtime); + free(failtime); + if (value == 0) + return (PICL_WALK_TERMINATE); + + log_printf(dgettext(TEXT_DOMAIN, "Most recent AC Power Failure:\n")); + log_printf("=============================\n"); + log_printf("%s", ctime(&value)); + log_printf("\n"); + return (PICL_WALK_TERMINATE); +} + +/* + * display the OBP and POST prom revisions + */ +/* ARGSUSED */ +static int +flashprom_callback(picl_nodehdl_t flashpromh, void *arg) +{ + picl_prophdl_t proph; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + picl_propinfo_t pinfo; + char *prom_version = NULL; + char *obp_version = NULL; + int err; + + err = picl_get_propinfo_by_name(flashpromh, OBP_PROP_VERSION, + &pinfo, &proph); + if (err == PICL_PROPNOTFOUND) + return (PICL_WALK_TERMINATE); + else if (err != PICL_SUCCESS) + return (err); + + log_printf(dgettext(TEXT_DOMAIN, "System PROM revisions:\n")); + log_printf("----------------------\n"); + + /* + * If it's a table prop, the first element is OBP revision + * The second one is POST revision. + * If it's a charstring prop, the value will be only OBP revision + */ + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + prom_version = alloca(pinfo.size); + if (prom_version == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, prom_version, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + log_printf("%s\n", prom_version); + } + + if (pinfo.type != PICL_PTYPE_TABLE) /* not supported type */ + return (PICL_WALK_TERMINATE); + + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err == PICL_SUCCESS) { + /* get first row */ + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + prom_version = alloca(pinfo.size); + if (prom_version == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, prom_version, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + log_printf("%s\n", prom_version); + + /* get second row */ + err = picl_get_next_by_col(rowproph, &rowproph); + if (err == PICL_SUCCESS) { + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + obp_version = alloca(pinfo.size); + if (obp_version == NULL) + return (PICL_FAILURE); + err = picl_get_propval(rowproph, obp_version, + pinfo.size); + if (err != PICL_SUCCESS) + return (err); + log_printf("%s\n", obp_version); + } + } + + return (PICL_WALK_TERMINATE); +} + +static int +display_system_info(int serrlog, int log_flag, picl_nodehdl_t rooth) +{ + int err; + picl_nodehdl_t plafh; + picl_nodehdl_t frutreeh; + + err = picldiag_get_node_by_name(rooth, PICL_NODE_PLATFORM, &plafh); + if (err != PICL_SUCCESS) + return (err); + + if (!log_flag) { + err = display_platform_banner(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_system_clock(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY, + PICL_CLASS_MEMORY, memory_callback); + if (err != PICL_SUCCESS) + return (err); + + err = display_cpu_info(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_io_device_info(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_memory_config(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = display_usb_devices(plafh); + if (err != PICL_SUCCESS) + return (err); + } + + if (serrlog) { + err = picl_walk_tree_by_class(rooth, PICL_CLASS_OPTIONS, + NULL, options_callback); + if (err != PICL_SUCCESS) + return (err); + + err = picldiag_get_node_by_name(rooth, PICL_NODE_FRUTREE, + &frutreeh); + + /* return ok if no frutree in picl on schumacher */ + if (err != PICL_SUCCESS) + return (PICL_SUCCESS); + + err = display_fru_oper_status(frutreeh); + if (err != PICL_SUCCESS) + return (err); + + err = display_hw_revisions(plafh); + if (err != PICL_SUCCESS) + return (err); + + err = picl_walk_tree_by_class(plafh, PICL_CLASS_FLASHPROM, + NULL, flashprom_callback); + if (err != PICL_SUCCESS) + return (err); + } + + return (PICL_SUCCESS); +} + +/* ARGSUSED */ +int +do_prominfo(int serrlog, char *pgname, int log_flag, int prt_flag) +{ + int err; + char *errstr; + int done; + picl_nodehdl_t rooth; + + err = picl_initialize(); + if (err != PICL_SUCCESS) { + fprintf(stderr, EM_INIT_FAIL, picl_strerror(err)); + exit(1); + } + + do { + done = 1; + err = picl_get_root(&rooth); + if (err != PICL_SUCCESS) { + fprintf(stderr, EM_GET_ROOT_FAIL, picl_strerror(err)); + exit(1); + } + + err = display_system_info(serrlog, log_flag, rooth); + + if ((err == PICL_STALEHANDLE) || (err == PICL_INVALIDHANDLE)) + done = 0; + } while (!done); + + if (err != PICL_SUCCESS) { + errstr = picl_strerror(err); + fprintf(stderr, EM_PRTDIAG_FAIL); + fprintf(stderr, "%s\n", errstr? errstr : " "); + } + + (void) picl_shutdown(); + + return (0); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/schumacher/picl/Makefile b/usr/src/lib/libprtdiag_psr/sparc/schumacher/picl/Makefile new file mode 100644 index 0000000000..9ab6ff4330 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/schumacher/picl/Makefile @@ -0,0 +1,89 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/schumacher/picl/Makefile + +UTSBASE = $(SRC)/uts + +PLATFORM_OBJECTS= schumacher.o + +objs/%.o pics/%.o: ../common/%.c + $(COMPILE.c) $(IFLAGS) -o $@ $< + $(POST_PROCESS_O) + +include $(SRC)/lib/libprtdiag_psr/sparc/Makefile.com + +SRCS= $(OBJECTS:%.o=../common/%.c) + +LDLIBS += -lpicl + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../../libprtdiag/inc +IFLAGS += -I$(SRC)/cmd/picl/plugins/inc +LINTFLAGS += $(IFLAGS) + +PLATFORM= SUNW,Netra-CP3010 + +.KEEP_STATE: + +PLATLIBS= $(PLATFORM:%=$(USR_PLAT_DIR)/%/lib/) + +install: all $(PLATLIBS) $(USR_PSM_LIBS) \ + $(LINKED_PRTDIAG_DIRS) + +# +# install rules for /usr/platform/${PLATFORM}/lib/libprtdiag_psr.so.1 +# +$(PLATLIBS): + $(INS.dir) + +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +$(USR_PSM_LIB_DIR): + $(INS.dir) + +$(LINKED_DIRS): $(USR_PLAT_DIR) + -$(INS.dir.root.sys) + +$(LINKED_LIB_DIRS): $(LINKED_DIRS) + -$(INS.dir.root.sys) + +$(LINKED_PRTDIAG_DIRS): $(USR_PLAT_DIR) + -$(INS.slink6) + +# +# Rules for making message files +# + +MSGFILES= ../common/schumacher.c +POFILE= libprtdiag_psr_schumacher_picl.po + +$(POFILE): pofile_MSGFILES + +_msg: $(MSGDOMAINPOFILE) + +include $(SRC)/Makefile.msg.targ + diff --git a/usr/src/lib/libprtdiag_psr/sparc/serengeti/Makefile b/usr/src/lib/libprtdiag_psr/sparc/serengeti/Makefile new file mode 100644 index 0000000000..011a74efcd --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/serengeti/Makefile @@ -0,0 +1,78 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/serengeti/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= serengeti.o + +include ../Makefile.com + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../libprtdiag/inc +IFLAGS += -I$(UTSBASE)/sun4u/serengeti +IFLAGS += -I$(UTSBASE)/sun4u +LINTFLAGS += $(IFLAGS) + +LDLIBS += -lcfgadm + +PLATFORM=SUNW,Sun-Fire + + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/serengeti; $(MAKE) $(USR_PSM_LIB_DIR) +# +# install rule +# +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +# +# used for message files +# +POFILE= libprtdiag_psr_serengeti.po +POFILES= serengeti.po + + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/serengeti.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/sparc/serengeti/common/serengeti.c b/usr/src/lib/libprtdiag_psr/sparc/serengeti/common/serengeti.c new file mode 100644 index 0000000000..63a8c36092 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/serengeti/common/serengeti.c @@ -0,0 +1,2113 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Serengeti Platform specific functions. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <kstat.h> +#include <string.h> +#include <assert.h> +#include <alloca.h> +#include <libintl.h> +#include <fcntl.h> +#include <varargs.h> + +#include <sys/openpromio.h> +#include <sys/sysmacros.h> + +#include <sys/serengeti.h> +#include <sys/sgfrutypes.h> + +#include <pdevinfo.h> +#include <display.h> +#include <pdevinfo_sun4u.h> +#include <display_sun4u.h> +#include <libprtdiag.h> + +#include <config_admin.h> + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +#define SCHIZO_COMPATIBLE "pci108e,8001" +#define XMITS_COMPATIBLE "pci108e,8002" + +#define ACTIVE 0 +#define INACTIVE 1 +#define DISPLAY_INFO 40 + +#define EVNT2STR(e) ((e) == CFGA_STAT_NONE ? "none" : \ + (e) == CFGA_STAT_EMPTY ? "empty" : \ + (e) == CFGA_STAT_DISCONNECTED ? "disconnected" : \ + (e) == CFGA_STAT_CONNECTED ? "connected" : \ + (e) == CFGA_STAT_UNCONFIGURED ? "unconfigured" : \ + (e) == CFGA_STAT_CONFIGURED ? "configured" : \ + "unknown") + +#define COND2STR(c) ((c) == CFGA_COND_UNKNOWN ? "unknown" : \ + (c) == CFGA_COND_OK ? "ok" : \ + (c) == CFGA_COND_FAILING ? "failing" : \ + (c) == CFGA_COND_FAILED ? "failed" : \ + (c) == CFGA_COND_UNUSABLE ? "unusable" : \ + "???") + +#define SG_CLK_FREQ_TO_MHZ(x) (((x) + 500000) / 1000000) + +#define MAX_STATUS_LEN 8 +#define SG_FAIL "fail" +#define SG_DISABLED "disabled" +#define SG_DEGRADED "degraded" +#define SG_OK "ok" + +#define SG_SCHIZO_FAILED 1 +#define SG_SCHIZO_GOOD 0 + +#define DEFAULT_MAX_FREQ 66 /* 66 MHz */ +#define PCIX_MAX_FREQ 100 /* 100 MHz */ + +#define CFG_CPU "::cpu" + +#define CFG_SET_FRU_NAME_NODE(str, num) \ +{ \ + char tmp_str[MAX_FRU_NAME_LEN]; \ + sprintf(tmp_str, "/N%d", num); \ + strncat(str, tmp_str, sizeof (tmp_str)); \ +} + +#define CFG_SET_FRU_NAME_CPU_BOARD(str, num) \ +{ \ + char tmp_str[MAX_FRU_NAME_LEN]; \ + sprintf(tmp_str, ".%s%d", SG_HPU_TYPE_CPU_BOARD_ID, num); \ + strncat(str, tmp_str, sizeof (tmp_str)); \ +} + +#define CFG_SET_FRU_NAME_MODULE(str, num) \ +{ \ + char tmp_str[MAX_FRU_NAME_LEN]; \ + sprintf(tmp_str, "%s%d", CFG_CPU, num); \ + strncat(str, tmp_str, sizeof (tmp_str)); \ +} + +extern int print_flag; + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (Serengeti systems only) + */ +int do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag); +void *get_prop_val(Prop *prop); +Prop *find_prop(Prom_node *pnode, char *name); +char *get_node_name(Prom_node *pnode); +char *get_node_type(Prom_node *pnode); +void add_node(Sys_tree *, Prom_node *); +void display_pci(Board_node *); +void display_ffb(Board_node *, int); +void display_io_cards(struct io_card *list); +void display_cpu_devices(Sys_tree *tree); +void display_cpus(Board_node *board); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats); +void get_failed_parts(void); +int display_failed_parts(Sys_tree *tree); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); +void print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, + char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id); + +/* Local Functions */ +static void serengeti_display_hw_revisions(Prom_node *root, + Board_node *bnode); +static Board_node *serengeti_find_board(Sys_tree *root, int board, int nodeid); +static Board_node *serengeti_insert_board(Sys_tree *root, int board, int nid); +static int display_schizo_revisions(Board_node *bdlist, int mode); +static void display_sgsbbc_revisions(Board_node *bdlist); +static void serengeti_display_board_info(int state); +static void serengeti_display_board_info_header(int state); +static boolean_t cpu_node_configured(char *const node); +static void display_io_max_bus_speed(struct io_card *p); +static void display_io_slot_info(struct io_card *p); +static void get_slot_name(struct io_card *card, char *slot_name); + +/* The bus max freq is determined based on board level in use */ +int board_bus_max_freq = DEFAULT_MAX_FREQ; /* 66MHz default */ + +/* + * Serengeti now uses both the devinfo tree and the OBP tree for it's + * prtdiag. The devinfo tree is used for getting the HW config of the + * system and the OBP device tree is used for listing the failed HW + * in the system. This is because devinfo currently does not include + * any PROM nodes with a status of 'fail' so we need to go to OBP to + * get a list of failed HW. We use the tree flag to allow the same code + * to walk both trees. + * + * We really need to look at having a single tree for all platforms! + */ +#define DEVINFO_TREE 1 +#define OBP_TREE 2 + +static int tree = DEVINFO_TREE; + +#ifdef DEBUG +#define D_PRINTFINDENT printfindent +void +printfindent(int indent, char *fmt, ...) +{ + va_list ap; + int i = 0; + for (i = 0; i < indent; i ++) + printf("\t"); + + va_start(ap); + (void) vprintf(fmt, ap); + va_end(ap); +} +#else +#define D_PRINTFINDENT +#endif + +/* + * display_pci + * Display all the PCI IO cards on this board. + */ +void +display_pci(Board_node *board) +{ + struct io_card *card_list = NULL; + struct io_card card; + void *value; + Prom_node *pci; + Prom_node *card_node; + Prom_node *pci_bridge_node; + Prom_node *child_pci_bridge_node; + char *slot_name = NULL; /* info in "slot-names" prop */ + char *child_name; + char *name, *type; + char *pname, *ptype; + char buf[MAXSTRLEN]; + int *int_val; + int pci_bus; + int pci_bridge = 0; + int pci_bridge_dev_no; + char *slot_name_arr[SG_MAX_SLOTS_PER_IO_BD] = {NULL}; + int i; + int portid; + int level = 0; + int version, *pversion; +#ifdef DEBUG + int slot_name_bits; +#endif + + if (board == NULL) + return; + + /* Initialize all the common information */ + card.display = TRUE; + card.board = board->board_num; + card.node_id = board->node_id; + + /* + * Search for each schizo and xmits, then find/display all nodes under + * each schizo and xmits node found. + */ + for (pci = dev_find_node_by_compatible(board->nodes, SCHIZO_COMPATIBLE); + pci != NULL; + pci = dev_next_node_by_compatible(pci, SCHIZO_COMPATIBLE)) { + + /* set max freq for this board */ + board_bus_max_freq = DEFAULT_MAX_FREQ; + /* + * Find out if this is a PCI or cPCI IO Board. + * If "enum-impl" property exists in pci node => cPCI. + */ + value = get_prop_val(find_prop(pci, "enum-impl")); + if (value == NULL) { + (void) sprintf(card.bus_type, "PCI"); + } else { + (void) sprintf(card.bus_type, "cPCI"); + } + + if (strstr((char *)get_prop_val( + find_prop(pci, "compatible")), XMITS_COMPATIBLE)) { + sprintf(card.notes, "%s", XMITS_COMPATIBLE); + /* + * With XMITS 3.X and PCI-X mode, the bus speed + * can be higher than 66MHZ. + */ + value = (int *)get_prop_val + (find_prop(pci, "module-revision#")); + if (value) { + pversion = (int *)value; + version = *pversion; + if (version >= 4) + board_bus_max_freq = PCIX_MAX_FREQ; + } + } else if (strstr((char *)get_prop_val( + find_prop(pci, "compatible")), SCHIZO_COMPATIBLE)) + sprintf(card.notes, "%s", SCHIZO_COMPATIBLE); + else + sprintf(card.notes, " "); + + /* + * Get slot-name properties from parent node and + * store them in an array. + */ + value = (char *)get_prop_val(find_prop(pci, "slot-names")); + if (value != NULL) { +#ifdef DEBUG + /* save the 4 byte bitmask */ + slot_name_bits = *(int *)value; +#endif + /* array starts after first int */ + slot_name_arr[0] = (char *)value + sizeof (int); + + D_PRINTFINDENT(0, "slot_name_arr[0] is [%s]\n", + slot_name_arr[0]); + + for (i = 1; i < SG_MAX_SLOTS_PER_IO_BD; i++) { + slot_name_arr[i] = (char *)slot_name_arr[i - 1] + + strlen(slot_name_arr[i - 1]) +1; + + D_PRINTFINDENT(0, "slot_name_arr[%d] is [%s]\n", i, + slot_name_arr[i]); + + } + } + + /* + * Search for Children of this node ie. Cards. + * Note: any of these cards can be a pci-bridge + * that itself has children. If we find a + * pci-bridge we need to handle it specially. + * + * There can now be the condition of a pci-bridge + * being the child of a pci-bridge which create a + * two levels of pci-bridges. This special condition + * needs to be handled as well. The variable level + * is used to track the depth of the tree. This + * variable is then used to find instances of this case. + */ + level = 0; + card_node = pci->child; + while (card_node != NULL) { + pci_bridge = 0; + + /* If it doesn't have a name, skip it */ + name = (char *)get_prop_val( + find_prop(card_node, "name")); + if (name == NULL) { + card_node = card_node->sibling; + continue; + } + D_PRINTFINDENT(level, "NAME is %s\n", name); + + type = (char *)get_prop_val( + find_prop(card_node, "device_type")); + + /* + * get dev# and func# for this card from the + * 'reg' property. + */ + int_val = (int *)get_prop_val( + find_prop(card_node, "reg")); + if (int_val != NULL) { + card.dev_no = (((*int_val) & 0xF800) >> 11); + card.func_no = (((*int_val) & 0x700) >> 8); + } else { + card.dev_no = -1; + card.func_no = -1; + } + + /* + * If this is a pci-bridge, then store it's dev# + * as it's children nodes need this to get their slot#. + * We set the pci_bridge flag so that we know we are + * looking at a pci-bridge node. This flag gets reset + * every time we enter this while loop. + */ + + /* + * Check for a PCI-PCI Bridge for PCI and cPCI + * IO Boards using the name and type properties. + * + * If level is greater then 0, then check the parent + * node to see if it was also a pci-bridge. We do not + * this when level is 0 as this will see the schizo or + * xmits device as a pci-bridge node. This will mess + * up the slot number of child nodes. + */ + if ((type != NULL) && + (strncmp(name, "pci", 3) == 0) && + (strcmp(type, "pci") == 0)) { + if (level > 0) { + pname = (char *)get_prop_val( + find_prop(card_node->parent, "name")); + ptype = (char *)get_prop_val( + find_prop(card_node->parent, + "device_type")); + + if ((ptype != NULL) && (pname != NULL) && + (strncmp(pname, "pci", 3) == 0) && + (strcmp(ptype, "pci") == 0)) { + child_pci_bridge_node = card_node; + } else { + pci_bridge_dev_no = card.dev_no; + pci_bridge_node = card_node; + } + } else { + pci_bridge_dev_no = card.dev_no; + pci_bridge_node = card_node; + } + pci_bridge = TRUE; + + D_PRINTFINDENT(level, + "pci_bridge_dev_no is [%d]\n", + pci_bridge_dev_no); + } + + /* + * Get slot-names property from slot_names_arr. + * If we are the child of a pci_bridge we use the + * dev# of the pci_bridge as an index to get + * the slot number. We know that we are a child of + * a pci-bridge if our parent is the same as the last + * pci_bridge node found above. + */ + if (type) + D_PRINTFINDENT(level, + "*** name is [%s] - type is [%s]\n", + name, type); + else + D_PRINTFINDENT(level, + "*** name is [%s]\n", name); + + if (card.dev_no != -1) { + /* + * We compare this cards parent node with the + * pci_bridge_node to see if it's a child. + */ + if (((level > 0) && + (card_node->parent->parent == + pci_bridge_node)) || + (card_node->parent == pci_bridge_node)) { + /* use dev_no of pci_bridge */ + D_PRINTFINDENT(level, + " pci_bridge_dev_no is [%d]\n", + pci_bridge_dev_no); + + slot_name = + slot_name_arr[pci_bridge_dev_no -1]; + } else { + /* use cards own dev_no */ + D_PRINTFINDENT(level, + " card.dev_no is [%d]\n", + card.dev_no); + + slot_name = + slot_name_arr[card.dev_no - 1]; + } + + get_slot_name(&card, slot_name); + + } else { + (void) sprintf(card.slot_str, "%c", '-'); + } + + /* + * Get the portid of the schizo and xmits that this card + * lives under. + */ + portid = -1; + value = get_prop_val(find_prop(pci, "portid")); + if (value != NULL) { + portid = *(int *)value; + } + card.schizo_portid = portid; + +#ifdef DEBUG + (void) sprintf(card.notes, "%s portid [%d] dev_no[%d]" + " slot_name[%s] name_bits[%d]", + card.notes, + portid, + card.dev_no, slot_name, + slot_name_bits); +#endif + + /* + * Find out whether this is PCI bus A or B + * using the 'reg' property. + */ + int_val = (int *)get_prop_val + (find_prop(pci, "reg")); + + if (int_val != NULL) { + int_val ++; /* skip over first integer */ + pci_bus = ((*int_val) & 0x7f0000); + if (pci_bus == 0x600000) + card.pci_bus = 'A'; + else if (pci_bus == 0x700000) + card.pci_bus = 'B'; + else + card.pci_bus = '-'; + } else { + card.pci_bus = '-'; + } + + + /* + * Check for failed status. + */ + if (node_status(card_node, SG_FAIL)) + strncpy(card.status, SG_FAIL, + sizeof (SG_FAIL)); + else if (node_status(card_node, SG_DISABLED)) + strncpy(card.status, SG_DISABLED, + sizeof (SG_DISABLED)); + else + strncpy(card.status, SG_OK, + sizeof (SG_OK)); + + /* Get the model of this card */ + value = get_prop_val(find_prop(card_node, "model")); + if (value == NULL) + card.model[0] = '\0'; + else { + (void) sprintf(card.model, "%s", + (char *)value); + /* Skip sgsbbc nodes, they are not cards */ + if (strcmp(card.model, "SUNW,sgsbbc") == 0) { + card_node = card_node->sibling; + continue; + } + } + + /* + * Check if further processing is necessary to display + * this card uniquely. + */ + distinguish_identical_io_cards(name, card_node, &card); + + /* + * The card may have a "clock-frequency" but we + * are not interested in that. Instead we get the + * "clock-frequency" of the PCI Bus that the card + * resides on. PCI-A can operate at 33Mhz or 66Mhz + * depending on what card is plugged into the Bus. + * PCI-B always operates at 33Mhz. + * + */ + int_val = get_prop_val(find_prop(pci, + "clock-frequency")); + if (int_val != NULL) { + card.freq = SG_CLK_FREQ_TO_MHZ(*int_val); + } else { + card.freq = -1; + } + + /* + * Figure out how we want to display the name + */ + value = get_prop_val(find_prop(card_node, + "compatible")); + if (value != NULL) { + /* use 'name'-'compatible' */ + (void) sprintf(buf, "%s-%s", name, + (char *)value); + } else { + /* just use 'name' */ + (void) sprintf(buf, "%s", name); + } + name = buf; + + /* + * If this node has children, add the device_type + * of the child to the name value of this card. + */ + child_name = (char *)get_node_name(card_node->child); + if ((card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(card_node->child, + "device_type")); + if (value != NULL) { + /* add device_type of child to name */ + (void) sprintf(card.name, "%s/%s (%s)", + name, child_name, + (char *)value); + } else { + /* just add childs name */ + (void) sprintf(card.name, "%s/%s", name, + child_name); + } + } else { + (void) sprintf(card.name, "%s", (char *)name); + } + + /* + * If this is a pci-bridge, then add the word + * 'pci-bridge' to it's model. + */ + if (pci_bridge) { + if (strlen(card.model) == 0) + (void) sprintf(card.model, + "%s", "pci-bridge"); + else + (void) sprintf(card.model, + "%s/pci-bridge", card.model); + } + + /* insert this card in the list to be displayed later */ + card_list = insert_io_card(card_list, &card); + + /* + * If we are dealing with a pci-bridge, we need to move + * down to the children of this bridge if there are any. + * + * If we are not, we are either dealing with a regular + * card (in which case we move onto the sibling of this + * card) or we are dealing with a child of a pci-bridge + * (in which case we move onto the child's siblings or + * if there are no more siblings for this child, we + * move onto the parents siblings). + * + * Once we reach the last child node of a pci-bridge, + * we need to back up the tree to the parents sibling + * node. If our parent has no more siblings, we need + * to check our grand parent for siblings. + * + * If we have no more siblings, we simply point to + * to the child's sibling which moves us onto the next + * bus leaf. + * + * The variable level gets adjusted on some of the + * conditions as this is used to track level within + * the tree we have reached. + */ + if (pci_bridge) { + if (card_node->child != NULL) { + level++; + card_node = card_node->child; + } else + card_node = card_node->sibling; + } else { + if ((card_node->parent == pci_bridge_node) && + (card_node->sibling == NULL)) { + card_node = pci_bridge_node->sibling; + if (level > 0) + level--; + } else if ((card_node->parent == + child_pci_bridge_node) && + (card_node->parent->parent == + pci_bridge_node)) { + if ((child_pci_bridge_node->sibling) && + (card_node->sibling == NULL)) { + card_node = + child_pci_bridge_node->sibling; + if (level > 0) + level--; + } else if ((pci_bridge_node->sibling) && + (card_node->sibling == NULL)) { + card_node = + pci_bridge_node->sibling; + if (level > 1) + level = level - 2; + else if (level > 0) + level--; + } else + card_node = card_node->sibling; + } else + card_node = card_node->sibling; + } + } /* end-while */ + } /* end-for */ + + display_io_cards(card_list); + free_io_cards(card_list); +} + +/* + * display_ffb + * + * There are no FFB's on a Serengeti, however in the generic library, + * the display_ffb() function is implemented so we have to define an + * empty function here. + */ +/*ARGSUSED0*/ +void +display_ffb(Board_node *board, int table) +{} + +static void +serengeti_display_board_info_header(int state) +{ + char *fmt = "%-9s %-11s %-12s %-12s %-9s %-40s\n"; + + log_printf("\n", 0); + log_printf("=========================", 0); + if (state == ACTIVE) + log_printf(dgettext(TEXT_DOMAIN, + " Active Boards for Domain "), 0); + else + log_printf(dgettext(TEXT_DOMAIN, + " Available Boards/Slots for Domain "), 0); + log_printf("===========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + + log_printf(fmt, "", "Board", "Receptacle", "Occupant", "", "", 0); + + log_printf(fmt, "FRU Name", "Type", "Status", "Status", + "Condition", "Info", 0); + + log_printf(fmt, "---------", "-----------", "-----------", + "------------", "---------", + "----------------------------------------", 0); +} + +static void +serengeti_display_board_info(int state) +{ + int i, z, ret; + int nlist = 0; + int available_board_count = 0; + struct cfga_list_data *board_cfg = NULL; + char *err_string = NULL; + char tmp_id[CFGA_LOG_EXT_LEN + 1]; + char tmp_info[DISPLAY_INFO + 1]; + const char listops[] = "class=sbd"; + struct cfga_list_data dat; + cfga_flags_t flags = NULL; + + ret = config_list_ext(0, NULL, &board_cfg, &nlist, + NULL, listops, + &err_string, flags); + + if (ret == CFGA_OK) { + serengeti_display_board_info_header(state); + for (i = 0; i < nlist; i++) { + dat = board_cfg[i]; + + if ((state != ACTIVE) && + (dat.ap_o_state == CFGA_STAT_CONFIGURED)) + continue; + else if ((state == ACTIVE) && + (dat.ap_o_state != CFGA_STAT_CONFIGURED)) + continue; + if (state == INACTIVE) + available_board_count++; + + memcpy(tmp_id, dat.ap_log_id, CFGA_LOG_EXT_LEN); + tmp_id[CFGA_LOG_EXT_LEN] = '\0'; + for (z = 0; z < strlen(tmp_id); z++) { + if (tmp_id[z] == '.') + tmp_id[z] = '/'; + } + log_printf("/%-8s ", tmp_id, 0); + log_printf("%-11s ", dat.ap_type, 0); + + log_printf("%-12s ", EVNT2STR(dat.ap_r_state), 0); + log_printf("%-12s ", EVNT2STR(dat.ap_o_state), 0); + log_printf("%-8s ", COND2STR(dat.ap_cond), 0); + + memcpy(tmp_info, dat.ap_info, DISPLAY_INFO); + tmp_info[DISPLAY_INFO - 1] = '\0'; + if (strlen(tmp_info) >= (DISPLAY_INFO - 1)) + tmp_info[DISPLAY_INFO - 2] = '+'; + log_printf("%-*s\n", (DISPLAY_INFO - 1), tmp_info, 0); + } + if ((state == INACTIVE) && + (available_board_count == 0)) { + log_printf(dgettext(TEXT_DOMAIN, + "There are currently no " + "Boards/Slots available " + "to this Domain\n"), 0); + } + } + if (board_cfg) + free(board_cfg); + if (err_string) + free(err_string); +} + +/* + * add_node + * + * This function adds a board node to the board structure where that + * that node's physical component lives. + */ +void +add_node(Sys_tree *root, Prom_node *pnode) +{ + int board = -1; + int portid = -1; + int nodeid = -1; + + void *value = NULL; + Board_node *bnode = NULL; + Prom_node *p = NULL; + char *type; + + /* Get the board number of this board from the portid prop */ + if ((value = get_prop_val(find_prop(pnode, "portid"))) == NULL) { + if ((type = get_node_type(pnode)) && (strcmp(type, "cpu") == 0)) + value = + get_prop_val(find_prop(pnode->parent, "portid")); + } + if (value != NULL) { + portid = *(int *)value; + } + + nodeid = SG_PORTID_TO_NODEID(portid); + board = SG_PORTID_TO_BOARD_NUM(portid); + + /* find the board node with the same board number */ + if ((bnode = serengeti_find_board(root, board, nodeid)) == NULL) { + bnode = serengeti_insert_board(root, board, nodeid); + } + + /* now attach this prom node to the board list */ + /* Insert this node at the end of the list */ + pnode->sibling = NULL; + if (bnode->nodes == NULL) + bnode->nodes = pnode; + else { + p = bnode->nodes; + while (p->sibling != NULL) + p = p->sibling; + p->sibling = pnode; + } +} + + + +/* + * Print out all the io cards in the list. Also print the column + * headers if told to do so. + */ +void +display_io_cards(struct io_card *list) +{ + char *fmt = "%-10s %-4s %-4s %-4s %-4s %-4s %-4s %-4s %-4s %-34s"; + + static int banner = FALSE; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + if (banner == FALSE) { + log_printf(fmt, "", "", "", "", "", "Bus", "Max", + "", "", "", 0); + log_printf("\n", 0); + log_printf(fmt, "", "IO", "Port", "Bus", "", "Freq", "Bus", + "Dev,", "", "", 0); + log_printf("\n", 0); + log_printf(fmt, "FRU Name", "Type", " ID", "Side", "Slot", + "MHz", "Freq", "Func", "State", "Name", 0); +#ifdef DEBUG + log_printf("Model Notes\n", 0); +#else + log_printf("Model\n", 0); +#endif + + log_printf(fmt, "----------", "----", "----", "----", "----", + "----", "----", "----", "-----", + "--------------------------------", 0); +#ifdef DEBUG + log_printf("---------------------- ", 0); +#endif + log_printf("----------------------\n", 0); + banner = TRUE; + } + + for (p = list; p != NULL; p = p -> next) { + + display_io_slot_info(p); + + display_io_max_bus_speed(p); + + log_printf("\n", 0); + } +} + +static void +display_io_slot_info(struct io_card *p) +{ + char fru_name[MAX_FRU_NAME_LEN] = ""; + + SG_SET_FRU_NAME_NODE(fru_name, p->node_id); + SG_SET_FRU_NAME_IO_BOARD(fru_name, p->board); + SG_SET_FRU_NAME_MODULE(fru_name, p->schizo_portid % 2); + + log_printf("%-8s ", fru_name, 0); + log_printf("%-4s ", p->bus_type, 0); + log_printf("%-3d ", p->schizo_portid, 0); + log_printf("%c ", p->pci_bus, 0); + log_printf("%-1s ", p->slot_str, 0); + log_printf("%-3d ", p->freq, 0); +} + +#define BUS_SPEED_PRINT(speed) log_printf(" %d ", speed, 0) + +static void +display_io_max_bus_speed(struct io_card *p) +{ + int speed = board_bus_max_freq; + + switch (p->pci_bus) { + case 'A': + BUS_SPEED_PRINT(speed); + break; + case 'B': + if (strcmp(p->notes, XMITS_COMPATIBLE) == 0) { + if ((strncmp(p->slot_str, "1", 1) == 0) || + (strncmp(p->slot_str, "0", 1) == 0)) + BUS_SPEED_PRINT(33); + else + BUS_SPEED_PRINT(speed); + } else + BUS_SPEED_PRINT(33); + break; + default: + log_printf(" - ", 0); + break; + } + + log_printf("%-1d,%-1d ", p->dev_no, p->func_no, 0); + log_printf("%-5s ", p->status, 0); + + log_printf("%-32.32s%c ", p->name, + ((strlen(p->name) > 32) ? '+' : ' '), 0); + log_printf("%-22.22s%c", p->model, + ((strlen(p->model) > 22) ? '+' : ' '), 0); +#ifdef DEBUG + log_printf(" %s", p->notes, 0); +#endif /* DEBUG */ +} + +void +display_cpu_devices(Sys_tree *tree) +{ + Board_node *bnode; + + /* printf formats */ + char *fmt1 = "%-10s %-7s %-4s %-4s %-7s %-4s\n"; + + /* + * Display the table header for CPUs . Then display the CPU + * frequency, cache size, and processor revision of all cpus. + */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(" CPUs ", 0); + log_printf("=========================", 0); + log_printf("======================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + + log_printf(fmt1, "", "CPU ", "Run", " E$", "CPU", "CPU", 0); + + log_printf(fmt1, "FRU Name", "ID ", "MHz", " MB", + "Impl.", "Mask", 0); + + log_printf(fmt1, "----------", "-------", "----", "----", + "-------", "----", 0); + + /* Now display all of the cpus on each board */ + bnode = tree->bd_list; + while (bnode != NULL) { + display_cpus(bnode); + bnode = bnode->next; + } + + log_printf("\n", 0); +} + +static boolean_t +cpu_node_configured(char *const node) +{ + int ret, i; + int nlist = 0; + boolean_t rv; + char *err_string = NULL; + char *const *ap_args = NULL; + struct cfga_list_data *statlist = NULL; + struct cfga_list_data dat; + cfga_flags_t flags = CFGA_FLAG_LIST_ALL; + + if (node == NULL) + return (FALSE); + + ap_args = &node; + ret = config_list_ext(1, &node, &statlist, &nlist, + NULL, NULL, &err_string, flags); + + if (ret == CFGA_OK) { + dat = statlist[0]; + + if (dat.ap_o_state == CFGA_STAT_CONFIGURED) + rv = TRUE; + else + rv = FALSE; + } else { + rv = FALSE; + } + if (statlist) + free(statlist); + if (err_string) + free(err_string); + return (rv); +} + +/* + * Display the CPUs present on this board. + */ +void +display_cpus(Board_node *board) +{ + Prom_node *cpu; + int freq; /* CPU clock frequency */ + int ecache_size; /* External cache size */ + int board_num = board->board_num; + int *mid; + int *impl; + int *mask; + int decoded_mask; + int *coreid; + int mid_prev = -1; + int ecache_size_prev = 0; + char fru_prev[MAX_FRU_NAME_LEN] = ""; + + /* + * display the CPUs' operating frequency, cache size, impl. field + * and mask revision. + */ + for (cpu = dev_find_type(board->nodes, "cpu"); cpu != NULL; + cpu = dev_next_type(cpu, "cpu")) { + char fru_name[MAX_FRU_NAME_LEN] = ""; + char cfg_fru_name[MAX_FRU_NAME_LEN] = ""; + + mid = (int *)get_prop_val(find_prop(cpu, "portid")); + if (mid == NULL) + mid = (int *)get_prop_val(find_prop(cpu, "cpuid")); + freq = SG_CLK_FREQ_TO_MHZ(get_cpu_freq(cpu)); + ecache_size = get_ecache_size(cpu); + impl = (int *)get_prop_val(find_prop(cpu, "implementation#")); + mask = (int *)get_prop_val(find_prop(cpu, "mask#")); + + /* Do not display a failed CPU node */ + if ((impl == NULL) || (freq == 0) || (node_failed(cpu))) + continue; + + /* FRU Name */ + SG_SET_FRU_NAME_NODE(fru_name, board->node_id); + + SG_SET_FRU_NAME_CPU_BOARD(fru_name, board_num); + SG_SET_FRU_NAME_MODULE(fru_name, *mid % 4); + + if (CPU_IMPL_IS_CMP(*impl)) { + coreid = (int *)get_prop_val(find_prop(cpu, + "reg")); + if (coreid == NULL) { + continue; + } + + /* + * The assumption is made that 2 cores will always be + * listed together in the device tree. If either core + * is "bad" then the FRU will not be listed. + * + * As display_cpus on Serengeti does actually process + * all cpu's per board a copy of the fru_name needs to + * be made as the following core may not be its + * sibling. If this is the case it is assumed that a + * sibling core has failed, so the fru should not be + * displayed. + * + * For the first instance of a core, fru_prev is + * expected to be empty. The current values are then + * stored and the next board->nodes is processed. If + * this is a sibling core, the ecache size it tallied + * and the previous value reset and processing + * continues. + * + * If the following core is not a sibling, the new + * values are stored and the next board->nodes is + * processed. + */ + if (strncmp(fru_prev, "", sizeof (fru_prev)) == 0) { + strncpy(fru_prev, fru_name, sizeof (fru_name)); + mid_prev = *mid; + ecache_size_prev = ecache_size; + continue; + } else { + if (strncmp(fru_name, fru_prev, + sizeof (fru_prev)) == 0) { + /* + * Jaguar has a split E$, so the size + * for both cores must be added together + * to get the total size for the entire + * chip. + * + * Panther E$ (L3) is logically shared, + * so the total size is equal to the + * core size. + */ + if (IS_JAGUAR(*impl)) { + ecache_size += ecache_size_prev; + } + + ecache_size_prev = 0; + strncpy(fru_prev, "", + sizeof (fru_prev)); + } else { + mid_prev = *mid; + ecache_size_prev = ecache_size; + strncpy(fru_prev, fru_name, + sizeof (fru_name)); + continue; + } + } + } + + /* + * If cpu is not configured, do not display it + */ + CFG_SET_FRU_NAME_NODE(cfg_fru_name, board->node_id); + CFG_SET_FRU_NAME_CPU_BOARD(cfg_fru_name, board_num); + CFG_SET_FRU_NAME_MODULE(cfg_fru_name, *mid % 4); + + if (!(cpu_node_configured(cfg_fru_name))) { + continue; + } + + + log_printf("%-10s ", fru_name, 0); + + /* CPU MID */ + if (CPU_IMPL_IS_CMP(*impl)) { + log_printf("%3d,%3d ", mid_prev, *mid, 0); + mid_prev = -1; + } else + log_printf("%3d ", *mid, 0); + + /* Running frequency */ + log_printf(" %4d ", freq, 0); + + /* Ecache size */ + if (ecache_size == 0) + log_printf("%3s ", "N/A", 0); + else + log_printf("%4.1f ", + (float)ecache_size / (float)(1<<20), + 0); + + /* Implementation */ + if (impl == NULL) { + log_printf("%6s ", " N/A", 0); + } else { + switch (*impl) { + case CHEETAH_IMPL: + log_printf("%-7s ", "US-III", 0); + break; + case CHEETAH_PLUS_IMPL: + log_printf("%-7s ", "US-III+", 0); + break; + case JAGUAR_IMPL: + log_printf("%-7s ", "US-IV", 0); + break; + case PANTHER_IMPL: + log_printf("%-7s ", "US-IV+", 0); + break; + default: + log_printf("%-7x ", *impl, 0); + break; + } + } + + /* CPU Mask */ + if (mask == NULL) { + log_printf(" %3s ", "N/A", 0); + } else { + if (IS_CHEETAH(*impl)) + decoded_mask = REMAP_CHEETAH_MASK(*mask); + else + decoded_mask = *mask; + + log_printf(" %d.%d ", + (decoded_mask >> 4) & 0xf, + decoded_mask & 0xf, 0); + } + + log_printf("\n", 0); + } +} + + +/*ARGSUSED3*/ +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " Hardware Failures "), 0); + log_printf("==================================", 0); + log_printf("\n", 0); + + /* + * Get a list of failed parts (ie. devices with a status of + * 'fail') from the OBP device tree and display them. + */ + get_failed_parts(); + + /* return unless -v option specified */ + if (!flag) { + log_printf("\n", 0); + return; + } + + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + /* Print the PROM revisions here */ + serengeti_display_hw_revisions(root, tree->bd_list); +} + +/* + * local functions - functions that are only needed inside this library + */ + +static void +serengeti_display_hw_revisions(Prom_node *root, Board_node *bdlist) +{ + Prom_node *pnode; + char *value; + + /* Print the header */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " HW Revisions "), 0); + log_printf("=======================================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + + /* Display Prom revision header */ + log_printf("System PROM revisions:\n", 0); + log_printf("----------------------\n", 0); + + /* + * Display OBP version info + */ + pnode = dev_find_node(root, "openprom"); + if (pnode != NULL) { + value = (char *)get_prop_val(find_prop(pnode, "version")); + log_printf("%s\n\n", value, 0); + } else { + log_printf("OBP ???\n\n", value, 0); + } + + /* + * Display ASIC revisions + */ + log_printf("IO ASIC revisions:\n", 0); + log_printf("------------------\n", 0); + + log_printf(" Port\n", 0); + log_printf("FRU Name Model ID Status", 0); +#ifdef DEBUG + log_printf(" Version Notes\n", 0); +#else + log_printf(" Version\n", 0); +#endif + /* ---------FRU Name--Model-----------Port-Status */ + log_printf("----------- --------------- ---- ---------- " +#ifdef DEBUG + "------- " +#endif + "-------\n", 0); + /* + * Display SCHIZO version info + */ + display_schizo_revisions(bdlist, SG_SCHIZO_GOOD); + + /* + * Display sgsbbc version info + */ + display_sgsbbc_revisions(bdlist); +} + +/* + * This function displays Schizo and Xmits revision of boards + */ +static int +display_schizo_revisions(Board_node *bdlist, int mode) +{ + Prom_node *pnode; + int *int_val; + int portid; + int prev_portid = -1; + char *model; + char *status_a, *status_b; + char status[MAX_STATUS_LEN]; + int version; + int node_id; +#ifdef DEBUG + uint32_t a_notes, b_notes; +#endif + int pci_bus; + /* + * rv is used when mode is set to SG_SCHIZO_FAILED. + * We need to signal if a failure is found so that + * the correct headers/footers can be printed. + * + * rv = 1 implies a failed/disavled schizo device + * rv = 0 implies all other cases + */ + int rv = 0; + Board_node *bnode; + void *value; + + bnode = bdlist; + while (bnode != NULL) { + /* + * search this board node for all Schizos + */ + for (pnode = dev_find_node_by_compatible(bnode->nodes, + SCHIZO_COMPATIBLE); pnode != NULL; + pnode = dev_next_node_by_compatible(pnode, + SCHIZO_COMPATIBLE)) { + + char fru_name[MAX_FRU_NAME_LEN] = ""; + + /* + * get the reg property to determine + * whether we are looking at side A or B + */ + int_val = (int *)get_prop_val + (find_prop(pnode, "reg")); + if (int_val != NULL) { + int_val ++; /* second integer in array */ + pci_bus = ((*int_val) & 0x7f0000); + } + + /* get portid */ + int_val = (int *)get_prop_val + (find_prop(pnode, "portid")); + if (int_val == NULL) + continue; + + portid = *int_val; + + /* + * If this is a new portid and it is PCI bus B, + * we skip onto the PCI bus A. (PCI-A and PCI-B share + * the same portid) + */ + if ((portid != prev_portid) && (pci_bus == 0x700000)) { + prev_portid = portid; + /* status */ + status_b = (char *)get_prop_val + (find_prop(pnode, "status")); +#ifdef DEBUG + b_notes = pci_bus; +#endif + continue; /* skip to the next schizo */ + } + + /* + * This must be side A of the same Schizo. + * Gather all its props and display them. + */ +#ifdef DEBUG + a_notes = pci_bus; +#endif + + prev_portid = portid; + + /* get the node-id */ + node_id = SG_PORTID_TO_NODEID(portid); + + /* model */ + model = (char *)get_prop_val + (find_prop(pnode, "model")); + + /* version */ + value = (int *)get_prop_val + (find_prop(pnode, "module-revision#")); + + if (value) + int_val = (int *)value; + else + int_val = (int *)get_prop_val + (find_prop(pnode, "version#")); + if (int_val != NULL) + version = *int_val; + else + version = -1; + + /* status */ + status_a = (char *)get_prop_val(find_prop + (pnode, "status")); + + /* + * Display the data + */ + /* FRU Name */ + SG_SET_FRU_NAME_NODE(fru_name, node_id); + SG_SET_FRU_NAME_IO_BOARD(fru_name, + SG_IO_BD_PORTID_TO_BD_NUM(portid)); + SG_SET_FRU_NAME_MODULE(fru_name, portid % 2); + + if (mode == SG_SCHIZO_FAILED) { + if ((status_a != (char *)NULL) && + ((status_b != (char *)NULL))) { + if ((strcmp + (status_a, SG_DISABLED) == 0) && + (strcmp(status_b, + SG_DISABLED) == 0)) { + log_printf("\tFRU Type : %s\n ", + model, 0); + log_printf("\tLocation : %s\n", + fru_name, 0); + log_printf + ("\tPROM status: %s\n\n", + SG_DISABLED, 0); + rv = 1; + } + } + continue; + } + /* + * This section of code is executed when displaying + * non-failed schizo devices. If the mode is set to + * SG_SCHIZO_FAILED, then this section of code will + * not be executed + */ + if ((status_a == (char *)NULL) && + ((status_b == (char *)NULL))) + sprintf(status, " %s ", SG_OK); + else if ((status_a == (char *)NULL) && + ((strcmp(status_b, SG_DISABLED) == 0))) + sprintf(status, " %s", SG_DEGRADED); + else if ((status_b == (char *)NULL) && + ((strcmp(status_a, SG_DISABLED) == 0))) + sprintf(status, " %s", SG_DEGRADED); + else + continue; + + log_printf("%-12s", fru_name, 0); + + /* model */ + + if (model != NULL) + log_printf("%-15s ", model, 0); + else + log_printf("%-15s ", "unknown", 0); + /* portid */ + log_printf("%-3d ", portid, 0); + + /* status */ + log_printf("%s", status, 0); + + /* version */ + log_printf(" %-4d ", version, 0); +#ifdef DEBUG + log_printf("0x%x 0x%x", a_notes, b_notes, 0); + log_printf(" %d", portid, 0); +#endif + log_printf("\n", 0); + } + bnode = bnode->next; + } + return (rv); +} + +static void +display_sgsbbc_revisions(Board_node *bdlist) +{ + + Prom_node *pnode; + int *int_val; + int portid; + char *model; + char *status; + int revision; + int node_id; + Board_node *bnode; + +#ifdef DEBUG + char *slot_name; + char notes[30]; + char *value; +#endif + + bnode = bdlist; + while (bnode != NULL) { + /* + * search this board node for all sgsbbc's + */ + for (pnode = dev_find_node_by_type(bnode->nodes, "model", + "SUNW,sgsbbc"); pnode != NULL; + pnode = dev_next_node_by_type(pnode, "model", + "SUNW,sgsbbc")) { + + char fru_name[MAX_FRU_NAME_LEN] = ""; + + /* + * We need to go to this node's parent to + * get a portid to tell us what board it is on + */ + int_val = (int *)get_prop_val + (find_prop(pnode->parent, "portid")); + if (int_val == NULL) + continue; + + portid = *int_val; + /* get the node-id */ + node_id = SG_PORTID_TO_NODEID(portid); + + /* model */ + model = (char *)get_prop_val + (find_prop(pnode, "model")); + + /* status */ + status = (char *)get_prop_val(find_prop + (pnode, "status")); + + /* revision */ + int_val = (int *)get_prop_val + (find_prop(pnode, "revision-id")); + if (int_val != NULL) + revision = *int_val; + else + revision = -1; + +#ifdef DEBUG + value = (char *)get_prop_val( + find_prop(pnode->parent, "slot-names")); + if (value != NULL) { + /* Skip the 4 byte bitmask */ + slot_name = (char *)value + sizeof (int); + } else { + strcpy(slot_name, "not_found"); + } + (void) sprintf(notes, "[%s] portid [%d]", slot_name, + portid); +#endif + /* + * Display the data + */ + /* FRU Name */ + SG_SET_FRU_NAME_NODE(fru_name, node_id); + SG_SET_FRU_NAME_IO_BOARD(fru_name, + SG_IO_BD_PORTID_TO_BD_NUM(portid)); + SG_SET_FRU_NAME_MODULE(fru_name, portid % 2); + log_printf("%-12s", fru_name, 0); + + /* model */ + if (model != NULL) + log_printf("%-15s ", model, 0); + else + log_printf("%-15s ", "unknown", 0); + /* portid */ + log_printf("%-3d ", portid, 0); + /* status */ + if (status == (char *)NULL) + log_printf(" ok ", 0); + else + log_printf(" fail ", 0); + /* revision */ + log_printf(" %-4d ", revision, 0); +#ifdef DEBUG + log_printf("%s", notes, 0); +#endif + log_printf("\n", 0); + } + bnode = bnode->next; + } +} + +/*ARGSUSED0*/ +void +display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) +{ + serengeti_display_board_info(ACTIVE); + serengeti_display_board_info(INACTIVE); +} + +/* + * display_failed_parts + * + * Display the failed parts in the system. This function looks for + * the status property in all PROM nodes contained in the Sys_tree + * passed in. + */ +int +display_failed_parts(Sys_tree *tree) +{ + int system_failed = 0; + int bank_failed = 0; + int schizo_failed = FALSE; + int portid, nodeid, board; + Board_node *bnode = tree->bd_list; + Prom_node *pnode; + int *coreid, *impl; + print_flag = TRUE; + + /* + * go through all of the OBP nodes looking for + * failed units. + */ + while (bnode != NULL) { + + pnode = find_failed_node(bnode->nodes); + if ((pnode != NULL) && !system_failed) { + system_failed = TRUE; + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, + "Failed Field Replaceable Units (FRU) in " + "System:\n"), 0); + log_printf("==========================" + "====================\n", 0); + } + + while (pnode != NULL) { + void *status; + char *name, *type, *model; + + char fru_name[MAX_FRU_NAME_LEN] = ""; + + status = get_prop_val(find_prop(pnode, "status")); + name = get_node_name(pnode); + + /* sanity check of data retreived from PROM */ + if ((status == NULL) || (name == NULL)) { + pnode = next_failed_node(pnode); + continue; + } + + type = get_node_type(pnode); + portid = get_id(pnode); + model = (char *)get_prop_val + (find_prop(pnode, "model")); + + /* + * Determine whether FRU is CPU module, Mem Controller, + * PCI card, schizo,xmits or sgsbbc. + */ + if ((model != NULL) && strstr(model, "sgsbbc")) { + /* + * sgsbbc / bootbus-controller + */ + portid = get_id(pnode->parent); + nodeid = SG_PORTID_TO_NODEID(portid); + board = SG_PORTID_TO_BOARD_NUM(portid); + + SG_SET_FRU_NAME_NODE(fru_name, nodeid); + SG_SET_FRU_NAME_IO_BOARD(fru_name, board); + SG_SET_FRU_NAME_MODULE(fru_name, portid % 2); + + log_printf("\tFailed Device : %s (%s)\n", model, + name, 0); + log_printf("\tLocation : %s\n", fru_name, 0); + + } else if (strstr(name, "pci") && (portid == -1)) { + /* + * PCI Bridge if name = pci and it doesn't + * have a portid. + */ + portid = get_id(pnode->parent); + nodeid = SG_PORTID_TO_NODEID(portid); + board = SG_PORTID_TO_BOARD_NUM(portid); + + SG_SET_FRU_NAME_NODE(fru_name, nodeid); + SG_SET_FRU_NAME_IO_BOARD(fru_name, board); + SG_SET_FRU_NAME_MODULE(fru_name, portid % 2); + + log_printf("\tFRU type : ", 0); + log_printf("PCI Bridge Device\n", 0); + log_printf("\tLocation : %s\n", fru_name, 0); + + } else if ((type != NULL) && + (strstr(type, "cpu") || + strstr(type, "memory-controller"))) { + /* + * CPU or memory controller + */ + portid = get_id(pnode); + /* + * For cpu nodes that belong to a CMP, the + * portid is stored in the parent "cmp" node. + */ + if (portid == -1) + portid = get_id(pnode->parent); + nodeid = SG_PORTID_TO_NODEID(portid); + board = SG_PORTID_TO_BOARD_NUM(portid); + + SG_SET_FRU_NAME_NODE(fru_name, nodeid); + SG_SET_FRU_NAME_CPU_BOARD(fru_name, board); + SG_SET_FRU_NAME_MODULE(fru_name, portid % 4); + + log_printf("\tFRU type : ", 0); + + if (strstr(type, "memory-controller")) + log_printf("Memory Controller on ", 0); + + log_printf("UltraSPARC module\n", 0); + + log_printf("\tLocation : %s\n", fru_name, 0); + + } else { + /* + * It should only be a PCI card if we get to + * here but lets check to be sure. + */ + char *parents_model, *grandparents_model; + Prom_node *parent_pnode; + int pci_card_found = 0; + + if (pnode->parent != NULL) + parent_pnode = pnode->parent; + + /* + * Is our parent a schizo or xmits + */ + parents_model = (char *)get_prop_val + (find_prop(pnode->parent, "model")); + if ((parents_model != NULL) && + (strstr(parents_model, "SUNW,schizo") || + strstr(parents_model, "SUNW,xmits"))) { + portid = get_id(pnode->parent); + pci_card_found = TRUE; + } + + /* + * Is our grandparent a schizo xmits + */ + grandparents_model = (char *)get_prop_val + (find_prop(parent_pnode->parent, "model")); + if ((grandparents_model != NULL) && + (strstr(grandparents_model, + "SUNW,schizo") || + strstr(grandparents_model, + "SUNW,xmits"))) { + portid = get_id(parent_pnode->parent); + pci_card_found = TRUE; + } + + if (pci_card_found) { + nodeid = SG_PORTID_TO_NODEID(portid); + board = SG_PORTID_TO_BOARD_NUM(portid); + + SG_SET_FRU_NAME_NODE(fru_name, nodeid); + SG_SET_FRU_NAME_IO_BOARD(fru_name, + board); + SG_SET_FRU_NAME_MODULE(fru_name, + portid % 2); + + log_printf("\tFRU type :", 0); + log_printf(" PCI Card\n", 0); + log_printf("\tLocation : %s\n", + fru_name, 0); + } + } + log_printf("\tPROM status: %s\n\n", status, 0); + + pnode = next_failed_node(pnode); + } + bnode = bnode->next; + + } + + bank_failed = display_us3_failed_banks(system_failed); + schizo_failed = display_schizo_revisions(tree->bd_list, + SG_SCHIZO_FAILED); + if (system_failed || bank_failed || schizo_failed) + return (1); + else + return (0); +} + + +/* + * This routine displays the memory configuration for all boards in the + * system. + */ +/*ARGSUSED0*/ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + Board_node *bnode = tree->bd_list; + + log_printf("========================= Memory Configuration" + " ===============================\n", 0); + log_printf("\n Logical Logical Logical ", 0); + log_printf("\n Port Bank Bank Bank " + "DIMM Interleave Interleave", 0); + log_printf("\nFRU Name ID Num Size Status " + "Size Factor Segment", 0); + log_printf("\n------------- ---- ---- ------ ----------- " + "------ ---------- ----------", 0); + + while (bnode != NULL) { + if (get_us3_mem_regs(bnode)) { + log_printf(dgettext(TEXT_DOMAIN, + "\nFailed to get memory information.\n"), 0); + return; + } + bnode = bnode->next; + } + + /* Display what we have found */ + display_us3_banks(); +} + +/* + * This function provides Serengeti's formatting of the memory config + * information that get_us3_mem_regs() and display_us3_banks() code has + * gathered. It overrides the generic print_us3_memory_line() code + * which prints an error message. + */ +void +print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, + char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id) +{ + int nodeid, board, mcid; + char fru_name[MAX_FRU_NAME_LEN] = ""; + + mcid = SG_PORTID_TO_SAFARI_ID(portid); + nodeid = SG_PORTID_TO_NODEID(portid); + board = SG_PORTID_TO_BOARD_NUM(portid); + + SG_SET_FRU_NAME_NODE(fru_name, nodeid); + SG_SET_FRU_NAME_CPU_BOARD(fru_name, board); + SG_SET_FRU_NAME_MODULE(fru_name, mcid % 4); + SG_SET_FRU_NAME_BANK(fru_name, (bank_id % 4) % 2); + + log_printf("\n%-13s %2d %2d %4lldMB %11-s %4lldMB " + " %2d-way %d", + fru_name, mcid, + (bank_id % 4), bank_size, bank_status, dimm_size, + intlv, seg_id, 0); +} + +void +print_us3_failed_memory_line(int portid, int bank_id, char *bank_status) +{ + int nodeid, board, mcid; + char fru_name[MAX_FRU_NAME_LEN] = ""; + + mcid = SG_PORTID_TO_SAFARI_ID(portid); + nodeid = SG_PORTID_TO_NODEID(portid); + board = SG_PORTID_TO_BOARD_NUM(portid); + + SG_SET_FRU_NAME_NODE(fru_name, nodeid); + SG_SET_FRU_NAME_CPU_BOARD(fru_name, board); + SG_SET_FRU_NAME_MODULE(fru_name, mcid % 4); + SG_SET_FRU_NAME_BANK(fru_name, (bank_id % 4) % 2); + + log_printf("\tFRU type : ", 0); + log_printf("Physical Memory Bank\n", 0); + log_printf("\tLocation : %s (Logical Bank %2d)\n", + fru_name, (bank_id %4), 0); + log_printf("\tPROM status: %s\n\n", bank_status, 0); +} + + +/* + * Find the requested board struct in the system device tree. + * + * This function overrides the functionality of the generic find_board() + * function in libprtdiag, but since we need to pass another parameter, + * we cannot simply overlay the symbol table. + */ +static Board_node * +serengeti_find_board(Sys_tree *root, int board, int nodeid) +{ + Board_node *bnode = root->bd_list; + + while ((bnode != NULL) && + ((board != bnode->board_num) || (nodeid != bnode->node_id))) { + bnode = bnode->next; + } + return (bnode); +} + + +/* + * Add a board to the system list in order (sorted by NodeID then board#). + * Initialize all pointer fields to NULL. + */ +static Board_node * +serengeti_insert_board(Sys_tree *root, int board, int nodeid) +{ + Board_node *bnode; + Board_node *temp = root->bd_list; + + if ((bnode = (Board_node *) malloc(sizeof (Board_node))) == NULL) { + perror("malloc"); + exit(1); + } + + bnode->nodes = NULL; + bnode->next = NULL; + bnode->board_num = board; + bnode->node_id = nodeid; + bnode->board_type = UNKNOWN_BOARD; + + if (temp == NULL) + root->bd_list = bnode; + + else if ((temp->board_num > board) && (temp->node_id >= nodeid)) { + bnode->next = temp; + root->bd_list = bnode; + + } else { + while ((temp->next != NULL) && + ((board > temp->next->board_num) || + (nodeid > temp->node_id))) + temp = temp->next; + + bnode->next = temp->next; + temp->next = bnode; + } + root->board_cnt++; + + return (bnode); +} + +/* + * We call do_devinfo() in order to use the libdevinfo device tree + * instead of OBP's device tree. + */ +int +do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag) +{ + + return (do_devinfo(syserrlog, pgname, log_flag, prt_flag)); + +} + +/* + * return the property value for the Prop passed in depending on + * which tree (OBP/DEVINFO) is being used. + */ +void * +get_prop_val(Prop *prop) +{ + if (prop == NULL) + return (NULL); + + /* Check which tree is being used. */ + if (tree == DEVINFO_TREE) + return ((void *)(prop->value.val_ptr)); + else { + if (prop->value.opp.holds_array) + return ((void *)(prop->value.opp.oprom_array)); + else + return ((void *)(&prop->value.opp.oprom_node[0])); + } +} + +/* + * Search a Prom node and retrieve the property with the correct + * name depending on which tree (OBP/DEVINFO) is being used. + */ +Prop * +find_prop(Prom_node *pnode, char *name) +{ + Prop *prop; + + if (pnode == NULL) + return (NULL); + + if (pnode->props == NULL) + return (NULL); + + prop = pnode->props; + + /* Check which tree is being used. */ + if (tree == DEVINFO_TREE) { + while ((prop != NULL) && + (strcmp((char *)(prop->name.val_ptr), name))) + prop = prop->next; + } else { + while ((prop != NULL) && (strcmp((char *) + (prop->name.opp.oprom_array), name))) + prop = prop->next; + } + return (prop); +} + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the name property + * depending on which tree (OBP/DEVINFO) is being used. + */ +char * +get_node_name(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) + return (NULL); + + prop = pnode->props; + while (prop != NULL) { + /* Check which tree is being used. */ + if (tree == DEVINFO_TREE) { + if (strcmp("name", (char *)prop->name.val_ptr) == 0) + return ((char *)prop->value.val_ptr); + } else { + if (strcmp("name", prop->name.opp.oprom_array) == 0) + return (prop->value.opp.oprom_array); + } + prop = prop->next; + } + return (NULL); +} + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the device_type property + * depending on which tree (OBP/DEVINFO) is being used. + */ +char * +get_node_type(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) + return (NULL); + + prop = pnode->props; + while (prop != NULL) { + /* Check which tree is being used. */ + if (tree == DEVINFO_TREE) { + if (strcmp("device_type", (char *)prop->name.val_ptr) + == 0) + return ((char *)prop->value.val_ptr); + } else { + if (strcmp("device_type", prop->name.opp.oprom_array) + == 0) + return (prop->value.opp.oprom_array); + } + prop = prop->next; + } + return (NULL); +} + +/* + * Take a snapshot of the OBP device tree and walk this snapshot + * to find all failed HW (ie. devices with a status property of + * 'fail'). Call display_failed_parts() to display the failed HW. + */ +void +get_failed_parts(void) +{ + int system_failed = 0; + Sys_tree obp_sys_tree; /* system information */ + + /* set the the system tree fields */ + obp_sys_tree.sys_mem = NULL; + obp_sys_tree.boards = NULL; + obp_sys_tree.bd_list = NULL; + obp_sys_tree.board_cnt = 0; + + if (promopen(O_RDONLY)) { + (void) fprintf(stderr, "%s", + dgettext(TEXT_DOMAIN, "openprom device " + "open failed")); + return; + } + + if ((is_openprom() == 0) || (next(0) == 0)) { + (void) fprintf(stderr, "%s", + dgettext(TEXT_DOMAIN, "openprom device " + "error encountered.")); + return; + } + + tree = OBP_TREE; /* Switch to the OBP tree */ + + (void) walk(&obp_sys_tree, NULL, next(0)); + + system_failed = display_failed_parts(&obp_sys_tree); + + if (!system_failed) { + log_printf(dgettext(TEXT_DOMAIN, + "No Hardware failures found in System\n"), 0); + } + promclose(); + tree = DEVINFO_TREE; /* Switch back to the DEVINFO tree */ +} + +/* + * get_slot_name figures out the slot no. for the card. In the case of + * XMITS slots 2 & 3 and slots 6 & 7 are reversed in slot_name by OBP + * so we need to cater for this to correctly identify the slot no. + */ +static void +get_slot_name(struct io_card *card, char *slot_name) +{ + char tmp_ptr[2]; + + if (strlen(slot_name) != 0) { + if (strcmp(card->notes, XMITS_COMPATIBLE) == 0) { + (void) sprintf(tmp_ptr, "%c", + slot_name[strlen(slot_name) -1]); + switch (tmp_ptr[0]) { + case '2': + (void) sprintf(card->slot_str, "%c", '3'); + break; + case '3': + (void) sprintf(card->slot_str, "%c", '2'); + break; + case '6': + (void) sprintf(card->slot_str, "%c", '7'); + break; + case '7': + (void) sprintf(card->slot_str, "%c", '6'); + break; + default: + (void) sprintf(card->slot_str, "%c", + slot_name[strlen(slot_name) -1]); + } + } else + (void) sprintf(card->slot_str, "%c", + slot_name[strlen(slot_name) -1]); + } else + (void) sprintf(card->slot_str, "-"); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/snowbird/Makefile b/usr/src/lib/libprtdiag_psr/sparc/snowbird/Makefile new file mode 100644 index 0000000000..e2742a5665 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/snowbird/Makefile @@ -0,0 +1,95 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/snowbird/Makefile +# +# Need to make libctsmc library first. +# +LIBCTSMC = ../../../../../src/cmd/picl/plugins/sun4u/snowbird/lib/libctsmc +all: ctsmclib +ctsmclib: $(LIBCTSMC)/libctsmc.c $(LIBCTSMC)/smclib.h + cd $(LIBCTSMC); $(MAKE) + +LIBBASE = ../../../../../src/lib +UTSBASE = ../../../../uts +SUN4U_INC_BASE = $(UTSBASE)/sun4u/sys +SB_INC_BASE = $(UTSBASE)/sun4u/snowbird/sys + +PLATFORM_OBJECTS= snowbird.o + +include ../Makefile.com + +IFLAGS = -I$(USR_PLAT_DIR)/sun4u/include -I../../../libprtdiag/inc -I$(LIBBASE)/libdevinfo +IFLAGS += -I$(SUN4U_INC_BASE) -I$(SB_INC_BASE) -I$(SRC)/cmd/picl/plugins/inc +IFLAGS += -I$(LIBCTSMC) +LINTFLAGS += $(IFLAGS) +LDLIBS += -L$(LIBCTSMC) +LDLIBS += -L$(LIBBASE)/libdevinfo -ldevinfo -L$(LIBBASE)/libcfgadm \ + -lcfgadm -lpicl -lctsmc +DYNFLAGS += -R/usr/platform/SUNW,Netra-CP2300/lib + + +# +# SUNW,Netra-CP2300 platform can link to +# /usr/platform/SUNW,Netra-CP2300/lib/libprtdiag_psr.so +# +PLATFORM=SUNW,Netra-CP2300 + +.KEEP_STATE: + +PLATLIBS= $(PLATFORM:%=$(USR_PLAT_DIR)/%/lib/) + +install: all $(PLATLIBS) $(USR_PSM_LIBS) + +# +# install rules for SUNW,Netra-CP2300/lib/libprtdiag_psr.so +# +$(PLATLIBS): + $(INS.dir) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/snowbird; $(MAKE) $(USR_PSM_LIB_DIR) + +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +# New additions to generate msg file +POFILE = libprtdiag_psr_snowbird.po +POFILES = snowbird.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/snowbird.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/sparc/snowbird/common/snowbird.c b/usr/src/lib/libprtdiag_psr/sparc/snowbird/common/snowbird.c new file mode 100644 index 0000000000..a1c952e720 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/snowbird/common/snowbird.c @@ -0,0 +1,1120 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * This program prints the diagnostics of Sanibel system. It + * also prints other miscellaneous information about watchdog, temperature + * of CPU sensor, firmware versions of SMC and, micro controller role + * etc. The basic sources of output is PICL, and SMC. + */ + +/* includes */ + +#include <stdio.h> +#include <strings.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#include <dirent.h> +#include <sys/param.h> +#include <picl.h> +#include <libintl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/systeminfo.h> +#include <sys/openpromio.h> +#include <fcntl.h> +#include <smc_if.h> +#include <stropts.h> +#include <alloca.h> +#include <errno.h> +#include <poll.h> +#include <stdlib.h> +#include <unistd.h> +#include <kstat.h> +#include <sys/utsname.h> +#include <stddef.h> +#include <pdevinfo.h> +#include <display_sun4u.h> +#include <libprtdiag.h> +#include <smclib.h> +#include <smc_commands.h> +#include <picldefs.h> + +/* #defines for the PICL library API usage and local static variables */ +#define PD_CPCI_SLOT_TYPE "cpci" +#define PD_PCI_SLOT_TYPE "pci" +#define PD_PRESENT 1 +#define PD_BLANK " " +#define PD_ENABLED 1 +#define PD_DISABLED 0 +#define SNOWBIRD "SUNW,Netra-CP2300" +#define CHASSIS_NODE_NAME "chassis" + +/* #defines for the SMC and IPMI commands */ +#define POLL_TIMEOUT 10000 +#define DEFAULT_SEQN 0xff + +/* SMC driver */ +#define PD_SMC_DRV_PATH "/dev/ctsmc" + +/* Constants */ +#define OBP_PROP_BANNER_NAME "banner-name" +#define OBP_PROP_CLOCK_FREQ "clock-frequency" + + + +/* #defines for local usage */ +#define PD_SUCCESS 0 +#define PD_FAILURE 1 +#define PD_INTERNAL_FAILURE 2 +#define PD_ERROR -1 + +/* static global variables */ +static int pd_print_option; +static uint8_t pd_smc_glbl_enabl_rsp[2]; +static boolean_t pd_hdr_prt = B_TRUE; +static int pd_smc_fd = 0; + + +/* function declarations used in this program */ +static uint32_t pd_check_for_snowbird(); +static uint32_t pd_prt_snowbird_diag(); +static uint32_t pd_check_cpu_health(); +static uint32_t pd_check_tty_debug_mode(); +static uint32_t pd_query_SMC_firmware_version(); +static uint32_t pd_check_slots(); +int32_t pd_prt_slot_info(picl_nodehdl_t, void *); +int do_prominfo(int syserrlog, char *pname, int log_flag, int prt_flag); +static uint32_t pd_query_watchdog_state(); +int pd_check_wd_state(picl_nodehdl_t, void *); +static uint32_t pd_print_fruinfo_hdr(); +static uint32_t pd_print_device_info(int); +static uint32_t pd_get_role_information(); +static uint32_t pd_get_message_flags(); +static uint32_t pd_get_reset_mode(); +static uint32_t pd_get_sensor_reading(); +static uint32_t pd_get_sensor_threshold(); +static uint32_t pd_prt_cpci_condition(picl_nodehdl_t nodeh); +static uint32_t pd_check_location_parent(picl_nodehdl_t nodeh); +static uint64_t +picldiag_get_uint_propval(picl_nodehdl_t modh, char *prop_name, int *ret); +static int picldiag_get_clock_freq(picl_nodehdl_t modh, uint32_t *freq); +static int display_system_clock(picl_nodehdl_t plafh); + +/* + * return the value of the uint prop + */ +static uint64_t +picldiag_get_uint_propval(picl_nodehdl_t modh, char *prop_name, int *ret) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + uint8_t uint8v; + uint16_t uint16v; + uint32_t uint32v; + uint64_t uint64v; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) { + *ret = err; + return (0); + } + + /* + * If it is not an int or uint prop, return failure + */ + if ((pinfo.type != PICL_PTYPE_INT) && + (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) { + *ret = PICL_FAILURE; + return (0); + } + + /* uint prop */ + + switch (pinfo.size) { + case sizeof (uint8_t): + err = picl_get_propval(proph, &uint8v, sizeof (uint8v)); + *ret = err; + return (uint8v); + case sizeof (uint16_t): + err = picl_get_propval(proph, &uint16v, sizeof (uint16v)); + *ret = err; + return (uint16v); + case sizeof (uint32_t): + err = picl_get_propval(proph, &uint32v, sizeof (uint32v)); + *ret = err; + return (uint32v); + case sizeof (uint64_t): + err = picl_get_propval(proph, &uint64v, sizeof (uint64v)); + *ret = err; + return (uint64v); + default: /* not supported size */ + *ret = PICL_FAILURE; + return (0); + } +} + + + +/* + * get the clock frequency + */ +static int +picldiag_get_clock_freq(picl_nodehdl_t modh, uint32_t *freq) +{ +#define ROUND_TO_MHZ(x) (((x) + 500000)/ 1000000) + + int err; + uint64_t clk_freq; + + clk_freq = picldiag_get_uint_propval(modh, OBP_PROP_CLOCK_FREQ, &err); + if (err != PICL_SUCCESS) + return (err); + + *freq = ROUND_TO_MHZ(clk_freq); + + return (PICL_SUCCESS); +} + + +/* + * display the clock frequency + */ +static int +display_system_clock(picl_nodehdl_t plafh) +{ + uint32_t system_clk; + int err; + + err = picldiag_get_clock_freq(plafh, &system_clk); + if (err != PICL_SUCCESS) + return (err); + + log_printf(dgettext(TEXT_DOMAIN, + "System clock frequency: %d MHZ\n"), system_clk); + + return (PICL_SUCCESS); +} + + +/* + * get the value by the property name of the string prop + * Caller must free the outbuf + */ +static int +picldiag_get_string_propval(picl_nodehdl_t modh, char *prop_name, char **outbuf) +{ + int err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + char *prop_value; + + err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + /* + * If it is not a string prop, return NULL + */ + if (pinfo.type != PICL_PTYPE_CHARSTRING) + return (PICL_FAILURE); + + prop_value = malloc(pinfo.size); + if (prop_value == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(proph, prop_value, pinfo.size); + if (err != PICL_SUCCESS) { + free(prop_value); + return (err); + } + + *outbuf = prop_value; + return (PICL_SUCCESS); +} + + + +/* + * display platform banner + */ +static int +display_platform_banner(picl_nodehdl_t plafh) +{ + char *platform; + char *banner_name; + int err; + + /* + * get PICL_PROP_MACHINE and PICL_PROP_BANNER_NAME + */ + log_printf(dgettext(TEXT_DOMAIN, + "System Configuration: Sun Microsystems "), 0); + err = picldiag_get_string_propval(plafh, PICL_PROP_MACHINE, + &platform); + if (err != PICL_SUCCESS) + return (err); + log_printf(" %s", platform, 0); + free(platform); + + err = picldiag_get_string_propval(plafh, OBP_PROP_BANNER_NAME, + &banner_name); + if (err != PICL_SUCCESS) + return (err); + log_printf(" %s", banner_name, 0); + free(banner_name); + + log_printf("\n", 0); + return (PICL_SUCCESS); +} + +/* + * search children to get the node by the nodename + */ +static int +picldiag_get_node_by_name(picl_nodehdl_t rooth, char *name, + picl_nodehdl_t *nodeh) +{ + picl_nodehdl_t childh; + int err; + char *nodename; + + nodename = alloca(strlen(name) + 1); + if (nodename == NULL) + return (PICL_FAILURE); + + err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_NAME, + nodename, (strlen(name) + 1)); + if (err != PICL_SUCCESS) { + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + continue; + } + + if (strcmp(nodename, name) == 0) { + *nodeh = childh; + return (PICL_SUCCESS); + } + + err = picl_get_propval_by_name(childh, PICL_PROP_PEER, + &childh, sizeof (picl_nodehdl_t)); + } + + return (err); +} + + +/* + * This routine is invoked when prtdiag starts execution. It prints + * system configuration, memory size, initializes PICL and acts as + * a driver routine for prtdiag output for Snowbird. + */ +/* ARGSUSED */ +int +do_prominfo(int syserrlog, char *pname, int log_flag, int prt_flag) +{ + + struct mem_total memory_total; /* total memory in system */ + struct grp_info grps; + uint8_t status = PD_SUCCESS; + picl_nodehdl_t rooth; + picl_nodehdl_t plafh; + struct system_kstat_data *kstats = NULL; + Sys_tree *tree = NULL; + + sys_clk = -1; + pd_print_option = syserrlog; + + if ((status = picl_initialize()) != PICL_SUCCESS) { + log_printf("prtdiag: failed to initialize the PICL\n", 0); + exit(1); + } + + if ((status = picl_get_root(&rooth)) != PICL_SUCCESS) { + log_printf("prtdiag: failed\n", 0); + exit(1); + } + + status = picldiag_get_node_by_name(rooth, PICL_NODE_PLATFORM, &plafh); + if (status != PICL_SUCCESS) + return (status); + + if (!log_flag) { + + status = display_platform_banner(plafh); + if (status != PICL_SUCCESS) + return (status); + + status = display_system_clock(plafh); + if (status != PICL_SUCCESS) + return (status); + + /* display the memory Size */ + display_memorysize(tree, kstats, &grps, &memory_total); + } + + if ((pd_smc_fd = open(PD_SMC_DRV_PATH, O_RDWR)) == -1) + return (PD_FAILURE); + + if ((status = pd_check_for_snowbird()) != PD_SUCCESS) + return (status); + + if ((status = pd_prt_snowbird_diag()) != PD_SUCCESS) + return (status); + + (void) close(pd_smc_fd); + + if (picl_shutdown() != PICL_SUCCESS) + return (PD_INTERNAL_FAILURE); + + return (PD_SUCCESS); + +} + +/* + * This routine prints out the platform name. + */ + +static uint32_t +pd_check_for_snowbird() +{ + + char si_platform[30]; + + if (sysinfo(SI_PLATFORM, si_platform, sizeof (si_platform)) == -1) { + return (PD_FAILURE); + } + /* is it a Snowbird? */ + if (strcmp(si_platform, SNOWBIRD) != 0) + return (PD_FAILURE); + + log_printf("platform Type : %s\n", si_platform, 0); + return (PD_SUCCESS); + +} + + +/* + * Driver routine for satellite specific output. This is also used by + * host driver routine as all satellite information is printed by host. + * It also prints some host specific information for formatting purposes + */ + +static uint32_t +pd_prt_snowbird_diag() +{ + uint8_t status = PD_SUCCESS; + if ((status = pd_check_cpu_health()) != PD_SUCCESS) { + return (status); + } + if (pd_print_option) { + + log_printf( + "\n %11s Other Miscellaneous Information \n", + PD_BLANK, 0); + log_printf( + "%12s ------------------------------- \n", + PD_BLANK, 0); + + if ((status = pd_get_role_information()) != PD_SUCCESS) { + return (status); + } + + if (pd_smc_glbl_enabl_rsp[1] & 0x10) { + log_printf( + "IPMI Response Notification\t\tEnabled\n", 0); + } else { + log_printf( + "IPMI Response Notification\t\tDisabled\n", 0); + } + if ((status = pd_query_SMC_firmware_version()) != PD_SUCCESS) { + return (status); + } + + if ((status = pd_check_tty_debug_mode()) != PD_SUCCESS) { + return (status); + } + + if ((status = pd_get_reset_mode()) != PD_SUCCESS) { + return (status); + } + + if ((status = pd_get_message_flags()) != PD_SUCCESS) { + return (status); + } + + if ((status = pd_query_watchdog_state()) != PD_SUCCESS) { + return (status); + } + + if ((status = pd_get_sensor_reading()) != PD_SUCCESS) { + return (status); + } + + if ((status = pd_get_sensor_threshold()) != PD_SUCCESS) { + return (status); + } + + } + return (status); + +} + +/* + * This routine prints the mode in which SMC is running. It uses the + * response from SMC global enables to determine the mode + */ +static uint32_t +pd_check_tty_debug_mode() +{ + + if (pd_smc_glbl_enabl_rsp[1] & 0x20) { + log_printf("SMC verbose mode\t\t\tON\n", 0); + } else { + log_printf("SMC verbose mode\t\t\tOFF\n", 0); + } + + return (PD_SUCCESS); +} + +/* This routine prints SMC f/w version */ +static uint32_t +pd_query_SMC_firmware_version() +{ + + sc_reqmsg_t req_pkt; + sc_rspmsg_t rsp_pkt; + uint8_t ver, rev, bldrev; + + + smc_init_smc_msg(&req_pkt, SMC_QUERY_FIRMWARE_VERSION, + DEFAULT_SEQN, 0); + smc_send_msg(-1, &req_pkt, &rsp_pkt, POLL_TIMEOUT); + ver = (rsp_pkt.data[0] & 0xf0) >> 4; + rev = rsp_pkt.data[0] & 0x0f; + bldrev = rsp_pkt.data[2] & 0x3f; + + log_printf("SMC f/w version is\t\t\t%d.%d.%d\n", ver, rev, bldrev, 0); + + return (PD_SUCCESS); + +} + +/* + * This routine checks CPU's health by using SMC self test results command + * It acts as driver routine for printing cPCI slot information + */ +static uint32_t +pd_check_cpu_health() +{ + + sc_reqmsg_t req_pkt; + sc_rspmsg_t rsp_pkt; + uint8_t dev_id = 0x1f; +#ifdef DEBUG + uint8_t i2c_chk = 0x40; +#endif + uint8_t mem_test = 0x20; + + smc_init_smc_msg(&req_pkt, SMC_GET_SMC_SELF_TEST_RESULT, + DEFAULT_SEQN, 0); + smc_send_msg(-1, &req_pkt, &rsp_pkt, POLL_TIMEOUT); + + dev_id = rsp_pkt.data[0] & dev_id; + +#ifdef DEBUG + if (rsp_pkt.data[0] & i2c_chk) { + pd_print_device_info(dev_id); + } +#endif + if (rsp_pkt.data[0] & mem_test) { + pd_print_device_info(dev_id); + } + return (pd_check_slots()); + +} + +/* + * This routine decodes error message for CPU failures and prints details + * of the failure + */ +static uint32_t +pd_print_device_info(int dev_id) +{ + + switch (dev_id) { + case 1: + log_printf("Mux Philip 9540\n", 0); + break; + case 2: + log_printf("cpu temp max1617\n", 0); + break; + case 3: + log_printf("pmc temp max 1617\n", 0); + break; + case 4: + log_printf("MB HS temp max 1617\n", 0); + break; + case 5: + log_printf("MB mem temp max1617\n", 0); + break; + case 6: + log_printf("MB gpio Philip8574\n", 0); + break; + case 7: + log_printf("MB Fru ID ID i2c eep\n", 0); + break; + case 8: + log_printf("MB enet ID ID i2d eep\n", 0); + break; + case 9: + log_printf("MB gpio Philip8574A\n", 0); + break; + case 10: + log_printf("SDRAM mod1 temp max1617\n", 0); + break; + case 11: + log_printf("SDRAM mod ID ID i2c eep\n", 0); + break; + case 12: + log_printf("SDRAM mod2 temp max1617\n", 0); + break; + case 13: + log_printf("SDRAM mod ID ID i2c eep\n", 0); + break; + case 14: + log_printf("Power mod temp ds1721\n", 0); + break; + case 15: + log_printf("Power mod gpio Philip 8574\n", 0); + break; + case 16: + log_printf("Power mod ID eep ST M24C01\n", 0); + break; + case 17: + log_printf("SMC ID i2c eep\n", 0); + break; + + default: + log_printf("device id unknown\n", 0); + break; + + } + + return (PD_SUCCESS); + +} + +/* + * This routine walks PICL tree by "Location" class and calls prt_slot_info + * routine to print the slot information + */ + +/*ARGSUSED*/ +static uint32_t +pd_check_slots() +{ + + picl_nodehdl_t nodeh; + char *c_args = NULL; + + if (picl_get_root(&nodeh) != PICL_SUCCESS) + return (PD_INTERNAL_FAILURE); + + + if (picl_walk_tree_by_class(nodeh, PICL_CLASS_LOCATION, + (void *)c_args, pd_prt_slot_info) != PICL_SUCCESS) { + return (PD_INTERNAL_FAILURE); + } + + return (PD_SUCCESS); + +} + + +/*ARGSUSED*/ +int32_t + +pd_prt_slot_info(picl_nodehdl_t nodeh, void *c_args) +{ + + char *valbuf; + char label_txt[30]; + int unit_no = -1, ctr = 0; + picl_nodehdl_t childh; + picl_propinfo_t propinfo; + picl_prophdl_t proph; + + /* if not immediate child of "chassis" node, ignore it */ + if (pd_check_location_parent(nodeh) != PD_SUCCESS) + return (PD_INTERNAL_FAILURE); + + + /* get the label on the location */ + if (picl_get_prop_by_name(nodeh, PICL_PROP_LABEL, + &proph) != PICL_SUCCESS) + return (PD_INTERNAL_FAILURE); + + if (picl_get_propinfo(proph, &propinfo) != PICL_SUCCESS) + return (PD_INTERNAL_FAILURE); + + valbuf = (char *) malloc(sizeof (char) * (propinfo.size)); + if (valbuf == NULL) + return (PD_INTERNAL_FAILURE); + + if (picl_get_propval(proph, (void *)valbuf, propinfo.size) + != PICL_SUCCESS) { + free(valbuf); + return (PD_INTERNAL_FAILURE); + } + + while (valbuf[ctr] != ' ' && valbuf[ctr] != NULL) { + label_txt[ctr] = valbuf[ctr]; + ++ctr; + } + + label_txt[ctr++] = '\0'; + + if (valbuf[ctr] != NULL) { + unit_no = atoi(valbuf+ctr); + } + + free(valbuf); + + /* get the slot type for the location */ + if (picl_get_prop_by_name(nodeh, PICL_PROP_SLOT_TYPE, + &proph) != PICL_SUCCESS) + return (PD_INTERNAL_FAILURE); + + if (picl_get_propinfo(proph, & propinfo) != PICL_SUCCESS) + return (PD_INTERNAL_FAILURE); + + valbuf = (char *) malloc(sizeof (char) * (propinfo.size)); + if (valbuf == NULL) + return (PD_INTERNAL_FAILURE); + + if (picl_get_propval(proph, (void *)valbuf, + propinfo.size) != PICL_SUCCESS) { + free(valbuf); + return (PD_INTERNAL_FAILURE); + } + + if ((strcmp(valbuf, PD_CPCI_SLOT_TYPE) == 0) || + (strcmp(valbuf, PD_PCI_SLOT_TYPE) == 0)) { + (void) pd_print_fruinfo_hdr(); + log_printf("\n%s ", label_txt, 0); + + /* For Snowbird no unit number is present on the label */ + unit_no = 1; + log_printf(" %d Yes cPSB IO Slot\n", unit_no, 0); + + if (picl_get_propval_by_name(nodeh, PICL_PROP_CHILD, + &childh, sizeof (childh)) == PICL_SUCCESS) { + pd_prt_cpci_condition(childh); + } + /* For Snowbird auto configuration is always enabled */ + log_printf("%29s Properties:\n", PD_BLANK, 0); + log_printf("%31s auto-config = enabled\n", PD_BLANK, 0); + } + + + free(valbuf); + return (PD_SUCCESS); + +} + + + +static uint32_t +pd_print_fruinfo_hdr() +{ + + log_printf( + "\n %19s FRU Information \n", + PD_BLANK, 0); + log_printf( + "%11s ------------------------------------------------\n", + PD_BLANK, 0); + + log_printf(dgettext(TEXT_DOMAIN, + "FRU FRU FRU Miscellaneous\n"), 0); + log_printf(dgettext(TEXT_DOMAIN, + "Type Unit# Present Information\n"), 0); + log_printf("---- ----- -------", 0); + log_printf(" --------------------------------\n", 0); + return (PD_SUCCESS); + +} + +static uint32_t +pd_check_location_parent(picl_nodehdl_t nodeh) +{ + + picl_nodehdl_t parenth; + char *prop_name; + + if (picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, + &parenth, sizeof (parenth)) != PICL_SUCCESS) { + return (PD_FAILURE); + } + + prop_name = (char *) malloc(sizeof (char) * PICL_PROPNAMELEN_MAX); + if (prop_name == NULL) { + return (PD_FAILURE); + } + + if (picl_get_propval_by_name(parenth, PICL_PROP_NAME, (void *)prop_name, + PICL_PROPNAMELEN_MAX) != PICL_SUCCESS) { + free(prop_name); + return (PD_FAILURE); + } + + if (strcmp(prop_name, CHASSIS_NODE_NAME) == 0) { + free(prop_name); + return (PD_SUCCESS); + } else { + free(prop_name); + return (PD_FAILURE); + } + +} + + +/*ARGSUSED*/ +static uint32_t +pd_query_watchdog_state() +{ + + picl_nodehdl_t nodehandle; + char *c_args = NULL; + + if (picl_get_root(&nodehandle) != PICL_SUCCESS) { + return (PD_INTERNAL_FAILURE); + } + + if (picl_walk_tree_by_class(nodehandle, PICL_CLASS_WATCHDOG_TIMER, + (void *)c_args, pd_check_wd_state) != PICL_SUCCESS) + return (PD_INTERNAL_FAILURE); + + return (PD_SUCCESS); + +} + +/*ARGSUSED*/ +int +pd_check_wd_state(picl_nodehdl_t nodeh, void *c_args) +{ + + char *prop_name, *valbuf; + picl_propinfo_t propinfo; + picl_prophdl_t proph; + + prop_name = (char *) malloc(sizeof (char) * PICL_PROPNAMELEN_MAX); + if (prop_name == NULL) { + return (PICL_WALK_TERMINATE); + } + + if (picl_get_propval_by_name(nodeh, PICL_PROP_NAME, + (void *)prop_name, PICL_PROPNAMELEN_MAX) != PICL_SUCCESS) { + free(prop_name); + return (PICL_WALK_TERMINATE); + } + + if ((picl_get_prop_by_name(nodeh, PICL_PROP_STATE, + &proph)) != PICL_SUCCESS) { + free(prop_name); + return (PICL_WALK_TERMINATE); + } + + if ((picl_get_propinfo(proph, &propinfo)) != PICL_SUCCESS) { + free(prop_name); + return (PICL_WALK_TERMINATE); + } + + valbuf = (char *) malloc(sizeof (char) * (propinfo.size)); + if (valbuf == NULL) { + free(prop_name); + return (PICL_WALK_TERMINATE); + } + + if ((picl_get_propval(proph, (void *)valbuf, + propinfo.size)) != PICL_SUCCESS) { + free(valbuf); + free(prop_name); + return (PICL_WALK_TERMINATE); + } + + if (pd_hdr_prt) { + log_printf("\n Watch Dog Status \n", 0); + log_printf(" ---------------- \n", 0); + log_printf("Node Status\n", 0); + log_printf("---- ------\n", 0); + pd_hdr_prt = B_FALSE; + } + + log_printf("%s ", prop_name, 0); + log_printf("%s\n", valbuf, 0); + + free(prop_name); + free(valbuf); + return (PICL_WALK_CONTINUE); + +} + + +static uint32_t +pd_get_role_information() +{ + + sc_reqmsg_t req_pkt; + sc_rspmsg_t rsp_pkt; + uint8_t usparc_role; + + smc_init_smc_msg(&req_pkt, SMC_GET_ROLE_INFO, + DEFAULT_SEQN, 0); + smc_send_msg(-1, &req_pkt, &rsp_pkt, POLL_TIMEOUT); + usparc_role = rsp_pkt.data[1]; + + log_printf(dgettext(TEXT_DOMAIN, + "UltraSPARC Host Role\t\t\t"), 0); + if (usparc_role & 0x80) { + log_printf( + dgettext(TEXT_DOMAIN, + "System Board Computer (SBC)\n"), 0); + } + if (usparc_role & 0x40) { + log_printf(dgettext(TEXT_DOMAIN, + "Standby System Board Computer (Standby SBC)\n"), 0); + } + if (usparc_role & 0x20) { + log_printf(dgettext(TEXT_DOMAIN, + "Alternate System Board Computer (Alternate SBC)\n"), 0); + } + if (usparc_role & 0x10) { + log_printf(dgettext(TEXT_DOMAIN, + "Satellite Board Computer (SAT)\n"), 0); + } + return (PD_SUCCESS); + +} + + +static uint32_t +pd_get_message_flags() +{ + + sc_reqmsg_t req_pkt; + sc_rspmsg_t rsp_pkt; + + smc_init_smc_msg(&req_pkt, SMC_GET_MESSAGE_FLAGS, + DEFAULT_SEQN, 0); + smc_send_msg(-1, &req_pkt, &rsp_pkt, POLL_TIMEOUT); + + if (rsp_pkt.data[0] & 0x01) { + log_printf("Messages Available in queue Recieving\n", 0); + } else { + log_printf("No messages in queue for Recieving\n", 0); + } + + return (PD_SUCCESS); + + +} + + + +static uint32_t +pd_get_reset_mode() +{ + + sc_reqmsg_t req_pkt; + sc_rspmsg_t rsp_pkt; + + + smc_init_smc_msg(&req_pkt, SMC_GET_CONFIG_BLOCK, + DEFAULT_SEQN, 0); + smc_send_msg(-1, &req_pkt, &rsp_pkt, POLL_TIMEOUT); + + log_printf("Reset Mode\t\t\t\t%x \n", rsp_pkt.data[2], 0); + + return (PD_SUCCESS); + +} + + +static uint32_t +pd_get_sensor_reading() +{ + + + sc_reqmsg_t req_pkt; + sc_rspmsg_t rsp_pkt; + + req_pkt.data[0] = 0x0e; + + smc_init_smc_msg(&req_pkt, SMC_SENSOR_READING_GET, + DEFAULT_SEQN, 1); + smc_send_msg(-1, &req_pkt, &rsp_pkt, POLL_TIMEOUT); + log_printf("\nCPU Node Temperature Information\n", PD_BLANK, 0); + log_printf("--------------------------------\n", PD_BLANK, 0); + log_printf("Temperature Reading: %d\n\n", rsp_pkt.data[0], 0); + + return (PD_SUCCESS); + +} + + +static uint32_t +pd_get_sensor_threshold() +{ + + + sc_reqmsg_t req_pkt; + sc_rspmsg_t rsp_pkt; + uint8_t thres_mask; + req_pkt.data[0] = 0x0e; + + smc_init_smc_msg(&req_pkt, SMC_SENSOR_THRESHOLD_GET, + DEFAULT_SEQN, 1); + smc_send_msg(-1, &req_pkt, &rsp_pkt, POLL_TIMEOUT); + log_printf("Critical Threshold Information\n", 0); + log_printf("------------------------------\n", 0); + + thres_mask = rsp_pkt.data[0]; + + if (thres_mask & 0x20) { + log_printf("High Power-Off Threshold %9s", PD_BLANK, 0); + if (rsp_pkt.data[6] & 0x80) { + log_printf("-%d\n", + (int)((uint8_t)~rsp_pkt.data[6] + 1), 0); + } else { + log_printf(" %d\n", rsp_pkt.data[6], 0); + } + } + + if (thres_mask & 0x10) { + log_printf("High Shutdown Threshold %10s", PD_BLANK, 0); + if (rsp_pkt.data[5] & 0x80) { + log_printf("-%d\n", + (int)((uint8_t)~rsp_pkt.data[5] + 1), 0); + } else { + log_printf(" %d\n", rsp_pkt.data[5], 0); + } + } + + + if (thres_mask & 0x08) { + log_printf("High Warning Threshold %11s", PD_BLANK, 0); + if (rsp_pkt.data[4] & 0x80) { + log_printf("-%d\n", + (int)((uint8_t)~rsp_pkt.data[4] + 1), 0); + } else { + log_printf(" %d\n", rsp_pkt.data[4], 0); + } + } + + if (thres_mask & 0x04) { + log_printf("Low Power Off Threshold %10s", PD_BLANK, 0); + if (rsp_pkt.data[3] & 0x80) { + log_printf("-%d\n", + (int)((uint8_t)~rsp_pkt.data[3] + 1), 0); + } else { + log_printf(" %d\n", rsp_pkt.data[3], 0); + } + } + + if (thres_mask & 0x02) { + log_printf("Low Shutdown Threshold %11s", PD_BLANK, 0); + if (rsp_pkt.data[2] & 0x80) { + log_printf("-%d\n", + (int)((uint8_t)~rsp_pkt.data[2] + 1), 0); + } else { + log_printf(" %d\n", rsp_pkt.data[2], 0); + } + } + + if (thres_mask & 0x01) { + log_printf("Low Warning Threshold %12s", PD_BLANK, 0); + if (rsp_pkt.data[1] & 0x80) { + log_printf("-%d\n", + (int)((uint8_t)~rsp_pkt.data[1] + 1), 0); + } else { + log_printf(" %d\n", rsp_pkt.data[1], 0); + } + } + + return (PD_SUCCESS); + +} + + + +static uint32_t +pd_prt_cpci_condition(picl_nodehdl_t nodeh) +{ + + picl_propinfo_t propinfo; + picl_prophdl_t proph; + char *valbuf; + + + if (picl_get_prop_by_name(nodeh, PICL_PROP_CONDITION, + &proph) != PICL_SUCCESS) { + return (PD_FAILURE); + } + + if (picl_get_propinfo(proph, &propinfo) != PICL_SUCCESS) { + return (PD_FAILURE); + } + + valbuf = (char *) malloc(sizeof (char) * (propinfo.size)); + if (valbuf == NULL) { + return (PD_FAILURE); + } + + if (picl_get_propval(proph, (void *)valbuf, + propinfo.size) != PICL_SUCCESS) { + free(valbuf); + return (PD_FAILURE); + } + + + log_printf("%29s Condition : %s\n", PD_BLANK, valbuf, 0); + + free(valbuf); + return (PD_SUCCESS); + + +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/starcat/Makefile b/usr/src/lib/libprtdiag_psr/sparc/starcat/Makefile new file mode 100644 index 0000000000..a94e6cfc06 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/starcat/Makefile @@ -0,0 +1,74 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#pragma ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/starcat/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= starcat.o + +include ../Makefile.com + +IFLAGS = -I$(USR_PLAT_DIR)/sun4u/include -I ../../../libprtdiag/inc +LINTFLAGS += $(IFLAGS) + +PLATFORM=SUNW,Sun-Fire-15000 + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/starcat; $(MAKE) $(USR_PSM_LIB_DIR) + +# +# install rule +# +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +# +# used for message files +# +POFILE= libprtdiag_psr_starcat.po +POFILES= starcat.po + + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/starcat.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/sparc/starcat/common/starcat.c b/usr/src/lib/libprtdiag_psr/sparc/starcat/common/starcat.c new file mode 100644 index 0000000000..2d33aa2fbe --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/starcat/common/starcat.c @@ -0,0 +1,1169 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Starcat Platform specific functions. + * + * called when : + * machine_type == MTYPE_STARCAT + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <assert.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include <pdevinfo.h> +#include <display.h> +#include <pdevinfo_sun4u.h> +#include <display_sun4u.h> +#include <libprtdiag.h> + +#define HZ_TO_MHZ(x) (((x) + 500000) / 1000000) +#define PORTID_TO_EXPANDER(p) (((p) >> 5) & 0x1f) +#define PORTID_TO_SLOT(p) (((p) >> 3) & 0x1) +#define PORTID_TO_INSTANCE(p) ((p) & 0x3) +#define SCHIZO_COMPATIBLE "pci108e,8001" +#define XMITS_COMPATIBLE "pci108e,8002" +#define SC_BOARD_TYPE(id) (PORTID_TO_SLOT(id) ? "IO" : "SB") + +#ifndef TEXT_DOMAIN +#define TEXT_DOMAIN "SYS_TEST" +#endif /* TEXT_DOMAIN */ + +#define DEFAULT_MAX_FREQ 66 /* 66 MHz */ +#define PCIX_MAX_FREQ 90 /* 90 MHz */ + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (Starcat systems only) + */ + +int do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag); +void *get_prop_val(Prop *prop); +Prop *find_prop(Prom_node *pnode, char *name); +char *get_node_name(Prom_node *pnode); +char *get_node_type(Prom_node *pnode); +void add_node(Sys_tree *, Prom_node *); +void display_pci(Board_node *); +void display_ffb(Board_node *, int); +void display_io_cards(struct io_card *list); +void display_cpu_devices(Sys_tree *tree); +void display_cpus(Board_node *board); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); +void print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, + char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); + +/* Local Functions */ +static void starcat_disp_hw_revisions(Prom_node *root); +static void display_io_max_bus_speed(struct io_card *p); +static void display_io_slot_info(struct io_card *p); + +/* The bus max freq is determined based on board level in use */ +int board_bus_max_freq = DEFAULT_MAX_FREQ; /* 66MHz default */ + +/* + * display_pci + * Display all the PCI IO cards on this board. + */ +void +display_pci(Board_node *board) +{ + struct io_card *card_list = NULL; + struct io_card card; + void *value; + Prom_node *pci; + Prom_node *card_node; + Prom_node *pci_bridge_node = NULL; + char *slot_name_arr[MAX_SLOTS_PER_IO_BD] = {NULL}; + char *slot_name = NULL; + int slot_name_bits; + int slot_name_offset = 0; + char *child_name; + char *name, *type; + char buf[MAXSTRLEN]; + int *int_val; + int pci_bus; + int pci_bridge = 0; + int pci_bridge_dev_no; + int child_dev_no; + int i; + int portid; + int version, *pversion; + + if (board == NULL) + return; + + /* Initialize all the common information */ + card.display = TRUE; + card.board = board->board_num; + card.node_id = board->node_id; + + /* + * Search for each schizo, then find/display all nodes under + * each schizo node found. Since the model property "SUNW,schizo" + * is not supported on Starcat, we must match on the compatible + * property "pci108e,8001". + */ + for (pci = dev_find_node_by_compatible(board->nodes, SCHIZO_COMPATIBLE); + pci != NULL; + pci = dev_next_node_by_compatible(pci, SCHIZO_COMPATIBLE)) { + + /* set max freq for this board */ + board_bus_max_freq = DEFAULT_MAX_FREQ; + /* + * Find out if this is a PCI or cPCI IO Board. + * If "enum-impl" property exists in pci node => cPCI. + */ + value = get_prop_val(find_prop(pci, "enum-impl")); + if (value == NULL) { + (void) sprintf(card.bus_type, "PCI"); + } else { + (void) sprintf(card.bus_type, "cPCI"); + } + + if (strstr((char *)get_prop_val( + find_prop(pci, "compatible")), XMITS_COMPATIBLE)) { + sprintf(card.notes, "%s", XMITS_COMPATIBLE); + /* + * With XMITS 3.X and PCI-X mode, the bus speed + * can be higher than 66MHZ. + */ + value = (int *)get_prop_val + (find_prop(pci, "module-revision#")); + if (value) { + pversion = (int *)value; + version = *pversion; + if (version >= 4) + board_bus_max_freq = PCIX_MAX_FREQ; + } + } else if (strstr((char *)get_prop_val( + find_prop(pci, "compatible")), SCHIZO_COMPATIBLE)) + sprintf(card.notes, "%s", SCHIZO_COMPATIBLE); + else + sprintf(card.notes, " "); + + /* + * Get slot-names property from parent node and + * store the individual slot names in an array. + * This is more general than Starcat requires, but + * it is correct, according to the slot-names property. + */ + value = (char *)get_prop_val(find_prop(pci, "slot-names")); + if (value == NULL) { + /* + * No slot_names property. This could be an Xmits + * card, so check the child node for slot-names property + */ + value = (char *)get_prop_val( + find_prop(pci->child, "slot-names")); + } + + if (value != NULL) { + /* Get the 4 byte bitmask and pointer to first name */ + slot_name_bits = *(int *)value; + if (slot_name_bits > 0) + slot_name_offset = slot_name_bits - 1; + slot_name = (char *)value + sizeof (int); + + for (i = 0; i < MAX_SLOTS_PER_IO_BD; i++) { + if (! (slot_name_bits & (1 << i))) { + slot_name_arr[i] = (char *)NULL; + continue; + } + + /* + * Save the name pointer into the array + * and advance it past the end of this + * slot name + */ + slot_name_arr[i] = slot_name; + slot_name += strlen(slot_name) + 1; + } + slot_name = (char *)NULL; + } + + /* + * Search for Children of this node ie. Cards. + * Note: any of these cards can be a pci-bridge + * that itself has children. If we find a + * pci-bridge we need to handle it specially. + */ + card_node = pci->child; + while (card_node != NULL) { + pci_bridge = 0; + + /* If it doesn't have a name, skip it */ + name = (char *)get_prop_val( + find_prop(card_node, "name")); + if (name == NULL) { + card_node = card_node->sibling; + continue; + } + + /* + * get dev# and func# for this card from the + * 'reg' property. + */ + int_val = (int *)get_prop_val( + find_prop(card_node, "reg")); + if (int_val != NULL) { + card.dev_no = (((*int_val) & 0xF800) >> 11); + card.func_no = (((*int_val) & 0x700) >> 8); + } else { + card.dev_no = -1; + card.func_no = -1; + } + + /* + * If this is a pci-bridge, then store it's dev# + * as its children nodes need this to get their slot#. + * We set the pci_bridge flag so that we know we are + * looking at a pci-bridge node. This flag gets reset + * every time we enter this while loop. + */ + + /* + * Check for a PCI-PCI Bridge for PCI and cPCI + * IO Boards using the name and type properties. + */ + type = (char *)get_prop_val( + find_prop(card_node, "device_type")); + if ((type != NULL) && + (strncmp(name, "pci", 3) == 0) && + (strcmp(type, "pci") == 0)) { + pci_bridge_dev_no = card.dev_no; + pci_bridge_node = card_node; + pci_bridge = TRUE; + } + + /* + * Get slot-names property from slot_names_arr. + * If we are the child of a pci_bridge we use the + * dev# of the pci_bridge as an index to get + * the slot number. We know that we are a child of + * a pci-bridge if our parent is the same as the last + * pci_bridge node found above. + */ + if (card.dev_no != -1) { + /* + * We compare this card's parent node with the + * pci_bridge_node to see if it's a child. + */ + if (card_node->parent == pci_bridge_node) { + /* use dev_no of pci_bridge */ + child_dev_no = pci_bridge_dev_no - 1; + } else { + /* use card's own dev_no */ + child_dev_no = card.dev_no - 1; + } + + if (child_dev_no < MAX_SLOTS_PER_IO_BD && + child_dev_no >= 0 && + slot_name_arr + [child_dev_no + slot_name_offset] != NULL) { + + slot_name = slot_name_arr[ + child_dev_no + slot_name_offset]; + } else + slot_name = (char *)NULL; + + if (slot_name != NULL && slot_name[0] != '\0') { + (void) sprintf(card.slot_str, "%s", + slot_name); + } else { + (void) sprintf(card.slot_str, "-"); + } + } else { + (void) sprintf(card.slot_str, "%c", '-'); + } + + /* + * Get the portid of the schizo that this card + * lives under. + */ + portid = -1; + value = get_prop_val(find_prop(pci, "portid")); + if (value != NULL) { + portid = *(int *)value; + } + card.schizo_portid = portid; + +#ifdef DEBUG + (void) sprintf(card.notes, "%s portid [%d]" + " dev_no [%d] slot_name[%s] name_bits[%#x]", + card.notes, portid, card.dev_no, + ((slot_name != NULL) ? slot_name : "NULL"), + slot_name_bits); +#endif /* DEBUG */ + + /* + * Find out whether this is PCI bus A or B + * using the 'reg' property. + */ + int_val = (int *)get_prop_val + (find_prop(pci, "reg")); + + if (int_val != NULL) { + int_val ++; /* skip over first integer */ + pci_bus = ((*int_val) & 0x7f0000); + if (pci_bus == 0x600000) + card.pci_bus = 'A'; + else if (pci_bus == 0x700000) + card.pci_bus = 'B'; + else + card.pci_bus = '-'; + } else { + card.pci_bus = '-'; + } + + + /* + * Check for failed status. + */ + if (node_failed(card_node)) + strcpy(card.status, "fail"); + else + strcpy(card.status, "ok"); + + /* Get the model of this card */ + value = get_prop_val(find_prop(card_node, "model")); + if (value == NULL) + card.model[0] = '\0'; + else { + (void) sprintf(card.model, "%s", (char *)value); + /* + * If we wish to exclude onboard devices + * (such as SBBC) then this is the place + * and here is how to do it: + * + * if (strcmp(card.model, "SUNW,sbbc") == 0) { + * card_node = card_node->sibling; + * continue; + * } + */ + } + + /* + * The card may have a "clock-frequency" but we + * are not interested in that. Instead we get the + * "clock-frequency" of the PCI Bus that the card + * resides on. PCI-A can operate at 33Mhz or 66Mhz + * depending on what card is plugged into the Bus. + * PCI-B always operates at 33Mhz. + * + */ + int_val = get_prop_val(find_prop(pci, + "clock-frequency")); + if (int_val != NULL) { + card.freq = HZ_TO_MHZ(*int_val); + } else { + card.freq = -1; + } + + /* + * Figure out how we want to display the name + */ + value = get_prop_val(find_prop(card_node, + "compatible")); + if (value != NULL) { + /* use 'name'-'compatible' */ + (void) sprintf(buf, "%s-%s", name, + (char *)value); + } else { + /* just use 'name' */ + (void) sprintf(buf, "%s", name); + } + name = buf; + + /* + * If this node has children, add the device_type + * of the child to the name value of this card. + */ + child_name = (char *)get_node_name(card_node->child); + if ((card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(card_node->child, + "device_type")); + if (value != NULL) { + /* add device_type of child to name */ + (void) sprintf(card.name, "%s/%s (%s)", + name, child_name, + (char *)value); + } else { + /* just add child's name */ + (void) sprintf(card.name, "%s/%s", + name, child_name); + } + } else { + /* childless, just the card's name */ + (void) sprintf(card.name, "%s", (char *)name); + } + + /* + * If this is a pci-bridge, then add the word + * 'pci-bridge' to its model. + */ + if (pci_bridge) { + if (card.model[0] == '\0') + (void) sprintf(card.model, + "%s", "pci-bridge"); + else + (void) strcat(card.model, + "/pci-bridge"); + } + + /* insert this card in the list to be displayed later */ + card_list = insert_io_card(card_list, &card); + + /* + * If we are dealing with a pci-bridge, we need to move + * down to the children of this bridge, if there are + * any, otherwise its siblings. + * + * If not a bridge, we are either dealing with a regular + * card (in which case we move onto the sibling of this + * card) or we are dealing with a child of a pci-bridge + * (in which case we move onto the child's siblings or + * if there are no more siblings for this child, we + * move onto the parent's siblings). I hope you're + * getting all this, there will be an exam later. + */ + if (pci_bridge) { + if (card_node->child != NULL) + card_node = card_node->child; + else + card_node = card_node->sibling; + } else { + /* + * If our parent is a pci-bridge but there + * are no more of its children to process we + * move back up to our parent's sibling, + * otherwise we move onto our own sibling. + */ + if ((card_node->parent == pci_bridge_node) && + (card_node->sibling == NULL)) + card_node = + pci_bridge_node->sibling; + else + card_node = card_node->sibling; + } + + } /* end while (card_node ...) loop */ + + } /* end for (pci ...) loop */ + + display_io_cards(card_list); + free_io_cards(card_list); +} + +/* + * display_ffb + * + * There are no FFB's on a Starcat, however in the generic library, + * the display_ffb() function is implemented so we have to define an + * empty function here. + */ +/*ARGSUSED0*/ +void +display_ffb(Board_node *board, int table) +{ +} + +/* + * add_node + * + * This function adds a board node to the board structure where that + * that node's physical component lives. + */ +void +add_node(Sys_tree *root, Prom_node *pnode) +{ + int portid = -1; + int nodeid = -1; + void *value; + Board_node *bnode; + Prom_node *p; + char *type; + + /* Get the board number of this board from the portid prop */ + if ((value = get_prop_val(find_prop(pnode, "portid"))) == NULL) { + if (type = get_node_type(pnode)) + if (strcmp(type, "cpu") == 0) + value = get_prop_val(find_prop(pnode->parent, + "portid")); + } + if (value != NULL) { + portid = *(int *)value; + nodeid = PORTID_TO_EXPANDER(portid); + } + + /* find the board node with the same board number */ + if ((bnode = find_board(root, portid)) == NULL) { + bnode = insert_board(root, portid); + bnode->board_type = UNKNOWN_BOARD; + bnode->node_id = nodeid; + } + + /* now attach this prom node to the board list */ + /* Insert this node at the end of the list */ + pnode->sibling = NULL; + if (bnode->nodes == NULL) + bnode->nodes = pnode; + else { + p = bnode->nodes; + while (p->sibling != NULL) + p = p->sibling; + p->sibling = pnode; + } +} + + + +/* + * Print out all the io cards in the list. Also print the column + * headers if told to do so. + */ +void +display_io_cards(struct io_card *list) +{ + char *hdrfmt = "%-10.10s %-4.4s %-4.4s %-4.4s %-4.4s %-4.4s" + " %-4.4s %-5.5s %-32.32s %-22.22s" +#ifdef DEBUG + " %-22.22s" +#endif /* DEBUG */ + "\n"; + + static int banner = FALSE; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + (void) textdomain(TEXT_DOMAIN); + + if (banner == FALSE) { + log_printf(hdrfmt, + "", "", "", "", + gettext("Bus"), + gettext("Max"), + "", "", "", "", +#ifdef DEBUG + "", +#endif /* DEBUG */ + 0); + + log_printf(hdrfmt, + "", + gettext("IO"), + gettext("Port"), + gettext("Bus"), + gettext("Freq"), + gettext("Bus"), + gettext("Dev,"), + "", "", "", +#ifdef DEBUG + "", +#endif /* DEBUG */ + 0); + + log_printf(hdrfmt, + gettext("Slot ID"), + gettext("Type"), + gettext(" ID"), + gettext("Side"), + gettext("MHz"), + gettext("Freq"), + gettext("Func"), + gettext("State"), + gettext("Name"), + gettext("Model"), +#ifdef DEBUG + gettext("Notes"), +#endif /* DEBUG */ + 0); + + log_printf(hdrfmt, + "----------", "----", "----", "----", "----", "----", + "----", "-----", "--------------------------------", + "----------------------", +#ifdef DEBUG + "----------------------", +#endif /* DEBUG */ + 0); + + banner = TRUE; + } + + for (p = list; p != NULL; p = p -> next) { + + display_io_slot_info(p); + + display_io_max_bus_speed(p); + + log_printf("\n", 0); + } +} + + +static void +display_io_slot_info(struct io_card *p) +{ + /* + * Onboard devices are distinguished by Slot IDs that + * indicate only the I/O board. Plug-in cards indicate + * their leaf and Schizo. + */ + + if (p->slot_str[0] == '-') { + log_printf("/%-2s%02d ", + SC_BOARD_TYPE(p->board), + PORTID_TO_EXPANDER(p->board), 0); + } else { + char c; + if (strcmp(p->notes, XMITS_COMPATIBLE) == 0) { + log_printf("/%-2s%02d/%s ", + SC_BOARD_TYPE(p->board), + PORTID_TO_EXPANDER(p->board), + p->slot_str, 0); + } else { + if (p->pci_bus == 'A') + c = '3'; + else if (p->pci_bus == 'B') { + c = '5'; + } else + c = '-'; + log_printf("/%-2s%02d/C%cV%1d ", + SC_BOARD_TYPE(p->board), + PORTID_TO_EXPANDER(p->board), c, + PORTID_TO_INSTANCE(p->schizo_portid), + 0); + } + } + log_printf("%-4.4s ", gettext(p->bus_type), 0); + log_printf("%3d ", p->schizo_portid, 0); + log_printf(" %c ", p->pci_bus, 0); + log_printf(" %3d ", p->freq, 0); +} + +#define BUS_SPEED_PRINT(speed) log_printf(" %d ", speed, 0) + +static void +display_io_max_bus_speed(struct io_card *p) +{ + int speed = board_bus_max_freq; + + switch (p->pci_bus) { + case 'A': + BUS_SPEED_PRINT(speed); + break; + case 'B': + if (strcmp(p->notes, XMITS_COMPATIBLE) == 0) { + if (PORTID_TO_INSTANCE(p->schizo_portid) == 0) + BUS_SPEED_PRINT(33); + else + BUS_SPEED_PRINT(speed); + } else + BUS_SPEED_PRINT(33); + break; + default: + log_printf(" - ", 0); + break; + } + + log_printf("%-1d,%-1d ", p->dev_no, p->func_no, 0); + log_printf("%-5.5s ", gettext(p->status), 0); + log_printf("%-32.32s%c ", p->name, + ((strlen(p->name) > 32) ? '+' : ' '), 0); + log_printf("%-22.22s%c", p->model, + ((strlen(p->model) > 22) ? '+' : ' '), 0); +#ifdef DEBUG + log_printf(" %s", p->notes, 0); +#endif /* DEBUG */ +} + +void +display_cpu_devices(Sys_tree *tree) +{ + Board_node *bnode; + char *hdrfmt = "%-8.8s %-7.7s %-4.4s %-4.4s %-7.7s %-4.4s\n"; + + (void) textdomain(TEXT_DOMAIN); + + /* + * Display the table header for CPUs . Then display the CPU + * frequency, cache size, and processor revision of all cpus. + */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(gettext(" CPUs "), 0); + log_printf("=========================", 0); + log_printf("\n\n", 0); + + log_printf(hdrfmt, + "", + gettext("CPU "), + gettext("Run"), + gettext(" E$"), + gettext(" CPU"), + gettext("CPU"), 0); + + log_printf(hdrfmt, + gettext("Slot ID"), + gettext("ID "), + gettext("MHz"), + gettext(" MB"), + gettext("Impl."), + gettext("Mask"), 0); + + log_printf(hdrfmt, + "--------", "-------", "----", "----", "-------", "----", 0); + + /* Now display all of the cpus on each board */ + bnode = tree->bd_list; + while (bnode != NULL) { + display_cpus(bnode); + bnode = bnode->next; + } + + log_printf("\n", 0); +} + +/* + * Display the CPUs present on this board. + */ +void +display_cpus(Board_node *board) +{ + Prom_node *cpu; + int freq; /* CPU clock frequency */ + int ecache_size; /* External cache size */ + int *impl; + int *mask; + int decoded_mask; + int *cpuid; + int *coreid; + int cpuid_prev = -1; + int ecache_size_prev = 0; + + (void) textdomain(TEXT_DOMAIN); + /* + * display the CPUs' operating frequency, cache size, impl. field + * and mask revision. + */ + for (cpu = dev_find_type(board->nodes, "cpu"); cpu != NULL; + cpu = dev_next_type(cpu, "cpu")) { + + freq = HZ_TO_MHZ(get_cpu_freq(cpu)); + ecache_size = get_ecache_size(cpu); + impl = (int *)get_prop_val(find_prop(cpu, "implementation#")); + mask = (int *)get_prop_val(find_prop(cpu, "mask#")); + cpuid = (int *)get_prop_val(find_prop(cpu, "cpuid")); + if (cpuid == NULL) + cpuid = &board->board_num; + + /* Do not display a failed CPU node */ + if ((freq == 0) || (impl == 0) || (node_failed(cpu))) + continue; + + if (CPU_IMPL_IS_CMP(*impl)) { + coreid = (int *)get_prop_val(find_prop(cpu, + "reg")); + if (coreid == NULL) { + continue; + } + + /* + * The assumption is made that 2 cores will always be + * listed together in the device tree. If either core + * is "bad" then the FRU will not be listed. + */ + if (cpuid_prev == -1) { + cpuid_prev = *cpuid; + ecache_size_prev = ecache_size; + continue; + } else { + /* + * Jaguar has a split E$, so the size for both + * cores must be added together to get the total + * size for the entire chip. + * + * Panther E$ (L3) is logically shared, so the + * total size is equal to the core size. + */ + if (IS_JAGUAR(*impl)) { + ecache_size += ecache_size_prev; + } + + ecache_size_prev = 0; + } + } + + /* + * Print out cpu data. + * + * Slot ID + */ + log_printf("/%-2s%02d/P%1d ", + SC_BOARD_TYPE(*cpuid), + PORTID_TO_EXPANDER(*cpuid), + PORTID_TO_INSTANCE(*cpuid), 0); + + /* CPU ID */ + if (CPU_IMPL_IS_CMP(*impl)) { + log_printf("%3d,%3d ", cpuid_prev, + *cpuid, 0); + cpuid_prev = -1; + } else + log_printf("%3d ", *cpuid, 0); + + /* Running frequency */ + log_printf("%4d ", freq, 0); + + /* Ecache size */ + if (ecache_size == 0) + log_printf("%-4.4s ", gettext("N/A"), 0); + else + log_printf("%4.1f ", + (float)ecache_size / (float)(1<<20), + 0); + + /* Implementation */ + switch (*impl) { + case CHEETAH_IMPL: + log_printf("%-7.7s ", + gettext("US-III"), 0); + break; + case CHEETAH_PLUS_IMPL: + log_printf("%-7.7s ", + gettext("US-III+"), 0); + break; + case JAGUAR_IMPL: + log_printf("%-7.7s ", + gettext("US-IV"), 0); + break; + case PANTHER_IMPL: + log_printf("%-7.7s ", + gettext("US-IV+"), 0); + break; + default: + log_printf("%-7x ", *impl, 0); + break; + } + + /* CPU Mask */ + if (mask == NULL) { + log_printf("%-4.4s", gettext("N/A"), 0); + } else { + if (IS_CHEETAH(*impl)) + decoded_mask = REMAP_CHEETAH_MASK(*mask); + else + decoded_mask = *mask; + + log_printf("%d.%d", + (decoded_mask >> 4) & 0xf, + decoded_mask & 0xf, 0); + } + + log_printf("\n", 0); + } +} + + +/*ARGSUSED1*/ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + Board_node *bnode = tree->bd_list; + char *hdrfmt = "\n%-11.11s %-4.4s %-7.7s %-7.7s %-8.8s %-6.6s" + " %-10.10s %-10.10s"; + + (void) textdomain(TEXT_DOMAIN); + + log_printf("=========================", 0); + log_printf(gettext(" Memory Configuration "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + + log_printf(hdrfmt, + "", "", + gettext("Logical"), + gettext("Logical"), + gettext("Logical"), + "", "", "", 0); + + log_printf(hdrfmt, + "", + gettext("Port"), + gettext("Bank"), + gettext("Bank"), + gettext("Bank"), + gettext(" DIMM"), + gettext("Interleave"), + gettext("Interleave"), 0); + + log_printf(hdrfmt, + gettext("Slot ID"), + gettext(" ID"), + gettext("Number"), + gettext("Size"), + gettext("Status"), + gettext(" Size"), + gettext("Factor"), + gettext("Segment"), 0); + + log_printf(hdrfmt, + "-----------", "----", "-------", "-------", "--------", + "------", "----------", "----------", 0); + + while (bnode != NULL) { + if (get_us3_mem_regs(bnode)) { + log_printf( + gettext( + "\nFailed to get memory information.\n"), + 0); + return; + } + bnode = bnode->next; + } + + /* Display what we have found */ + display_us3_banks(); +} + + +/* + * This function provides Starcat's formatting of the memory config + * information that get_us3_mem_regs() and display_us3_banks() code has + * gathered. It overrides the generic print_us3_memory_line() code + * which prints an error message. + */ +void +print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, + char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id) +{ + (void) textdomain(TEXT_DOMAIN); + + /* Slot ID */ + log_printf("\n/%-2s%02d/P%1d/B%1d ", + SC_BOARD_TYPE(portid), PORTID_TO_EXPANDER(portid), + PORTID_TO_INSTANCE(portid), (bank_id & 0x1), 0); + + /* Port ID */ + log_printf("%3d ", portid, 0); + + /* Logical Bank Number */ + log_printf(" %1d ", (bank_id & 0x3), 0); + + /* Logical Bank Size */ + log_printf("%4lldMB ", bank_size, 0); + + /* Logical Bank Status */ + log_printf("%-8.8s ", gettext(bank_status), 0); + + /* DIMM Size */ + log_printf("%4lldMB ", dimm_size, 0); + + /* Interleave Factor */ + log_printf(" %2d-%-3.3s ", intlv, gettext("way"), 0); + + /* Interleave Segment */ + log_printf(" %3d", seg_id, 0); +} + +/*ARGSUSED2*/ +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + if (flag) { + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + (void) textdomain(TEXT_DOMAIN); + + /* Print the header */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(gettext(" Diagnostic Information "), 0); + log_printf("=========================", 0); + log_printf("\n\n", 0); + log_printf(gettext("For diagnostic information,"), 0); + log_printf("\n", 0); + log_printf(gettext( + "see /var/opt/SUNWSMS/adm/[A-R]/messages on the SC."), + 0); + log_printf("\n", 0); + + /* Print the PROM revisions here */ + starcat_disp_hw_revisions(root); + } +} + +/* + * local functions - functions that are only needed inside this library + */ + +static void +starcat_disp_hw_revisions(Prom_node *root) +{ + Prom_node *pnode; + char *version; + + (void) textdomain(TEXT_DOMAIN); + + /* Print the header */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(gettext(" Hardware Revisions "), 0); + log_printf("=========================", 0); + log_printf("\n\n", 0); + + /* Display Prom revision header */ + log_printf(gettext("OpenBoot firmware revision:"), 0); + log_printf("\n---------------------------\n", 0); + + /* + * Display OBP version info + */ + pnode = dev_find_node(root, "openprom"); + if (pnode != NULL) { + version = (char *)get_prop_val(find_prop(pnode, "version")); + log_printf("%s\n\n", version, 0); + } +} + +/* + * We call do_devinfo() in order to use the libdevinfo device tree + * instead of OBP's device tree. + */ +int +do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag) +{ + + return (do_devinfo(syserrlog, pgname, log_flag, prt_flag)); + +} + +/* + * return the property value for the Prop + * passed in. (When using libdevinfo) + */ +void * +get_prop_val(Prop *prop) +{ + if (prop == NULL) + return (NULL); + + return ((void *)(prop->value.val_ptr)); +} + +/* + * Search a Prom node and retrieve the property with the correct + * name. (When using libdevinfo) + */ +Prop * +find_prop(Prom_node *pnode, char *name) +{ + Prop *prop; + + if (pnode == NULL) + return (NULL); + + for (prop = pnode->props; prop != NULL; prop = prop->next) { + if (prop->name.val_ptr != NULL && + strcmp((char *)(prop->name.val_ptr), name) == 0) + break; + } + + return (prop); +} + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the name property. + * (When using libdevinfo) + */ +char * +get_node_name(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("name", (char *)prop->name.val_ptr) == 0) + return (prop->value.val_ptr); + prop = prop->next; + } + return (NULL); +} + +/* + * This function searches through the properties of the node passed in + * and returns a pointer to the value of the device_type property. + * (When using libdevinfo) + */ +char * +get_node_type(Prom_node *pnode) +{ + Prop *prop; + + if (pnode == NULL) { + return (NULL); + } + + prop = pnode->props; + while (prop != NULL) { + if (strcmp("device_type", (char *)prop->name.val_ptr) == 0) + return (prop->value.val_ptr); + prop = prop->next; + } + return (NULL); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/starfire/Makefile b/usr/src/lib/libprtdiag_psr/sparc/starfire/Makefile new file mode 100644 index 0000000000..11714b079e --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/starfire/Makefile @@ -0,0 +1,70 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/starfire/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= starfire.o + +include ../Makefile.com + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../libprtdiag/inc +LINTFLAGS += $(IFLAGS) + +PLATFORM=SUNW,Ultra-Enterprise-10000 + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/starfire; $(MAKE) $(USR_PSM_LIB_DIR) + +# +# install rule +# +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +POFILE= libprtdiag_psr_starfire.po +POFILES= starfire.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/starfire.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/sparc/starfire/common/starfire.c b/usr/src/lib/libprtdiag_psr/sparc/starfire/common/starfire.c new file mode 100644 index 0000000000..6620ebbb89 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/starfire/common/starfire.c @@ -0,0 +1,224 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999-2001 by Sun Microsystems, Inc. + * All rights reserved. + * + * Starfire Platform specific functions. + * + * called when : + * machine_type == MTYPE_STARFIRE + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (starfire systems only) + */ +int error_check(Sys_tree *tree, struct system_kstat_data *kstats); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); +void display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void display_mid(int mid); +void display_pci(Board_node *); +Prom_node *find_device(Board_node *, int, char *); + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + + +int +error_check(Sys_tree *tree, struct system_kstat_data *kstats) +{ +#ifdef lint + tree = tree; + kstats = kstats; +#endif + return (0); +} + +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + Board_node *bnode; + char indent_str[] = " "; + +#ifdef lint + grps = grps; +#endif + + /* Print the header for the memory section. */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " Memory "), 0); + log_printf("=========================", 0); + log_printf("\n\n", 0); + + /* Print the header for the memory section. */ + log_printf(indent_str, 0); + log_printf("Memory Units: Size \n", 0); + log_printf(indent_str, 0); + log_printf("0: MB 1: MB 2: MB 3: MB\n", 0); + log_printf(indent_str, 0); + log_printf("----- ----- ----- ----- \n", 0); + + /* Run thru the board and display its memory if any */ + bnode = tree->bd_list; + while (bnode != NULL) { + Prom_node *pnode; + unsigned int *memsize; + unsigned int mbyte = 1024*1024; + + /* + * Find the mem-unit of the board. + * If the board has memory, a mem-unit pnode should + * be there. + */ + pnode = dev_find_node(bnode->nodes, "mem-unit"); + + if (pnode != NULL) { + /* there is a mem-unit in the board */ + + /* Print the board header */ + log_printf("Board%2d ", bnode->board_num, 0); + + memsize = get_prop_val(find_prop(pnode, "size")); + + log_printf(" %4d %4d %4d %4d \n", + memsize[0]/mbyte, memsize[1]/mbyte, + memsize[2]/mbyte, memsize[3]/mbyte, 0); + } + bnode = bnode->next; + } + log_printf("\n", 0); +} + +void +display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) +{ +#ifdef lint + tree = tree; + kstats = kstats; +#endif +} + +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + + char hostname[128]; /* used for starfire output */ + struct utsname uts_buf; + +#ifdef lint + flag = flag; + root = root; + tree = tree; + kstats = kstats; +#endif + + /* + * Get hostname from system Banner + */ + (void) uname(&uts_buf); + strcpy(hostname, uts_buf.nodename); + + /* + * We can't display diagnostic/env information for starfire. + * The diagnostic information may be displayed through + * commands in ssp. + */ + log_printf(dgettext(TEXT_DOMAIN, + "\nFor diagnostic information,"), 0); + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, "see /var/opt/SUNWssp/adm/%s/messages " + "on the SSP."), hostname, 0); + log_printf("\n", 0); +} + +void +display_mid(int mid) +{ + log_printf(" %2d ", mid % 4, 0); +} + +/* + * display_pci + * Call the generic psycho version of this function. + */ +void +display_pci(Board_node *board) +{ + display_psycho_pci(board); +} + +/* + * Find the device on the current board with the requested device ID + * and name. If this rountine is passed a NULL pointer, it simply returns + * NULL. + */ +Prom_node * +find_device(Board_node *board, int id, char *name) +{ + Prom_node *pnode; + int mask; + + /* find the first cpu node */ + pnode = dev_find_node(board->nodes, name); + + mask = 0x7F; + while (pnode != NULL) { + if ((get_id(pnode) & mask) == id) + return (pnode); + + pnode = dev_next_node(pnode, name); + } + return (NULL); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/sunfire/Makefile b/usr/src/lib/libprtdiag_psr/sparc/sunfire/Makefile new file mode 100644 index 0000000000..4cc1ed5bfd --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/sunfire/Makefile @@ -0,0 +1,68 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/sunfire/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= sunfire.o + +include ../Makefile.com + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../libprtdiag/inc +LINTFLAGS += $(IFLAGS) + +PLATFORM=SUNW,Ultra-Enterprise + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) + +# +# install rule +# +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + + +POFILE= libprtdiag_psr_sunfire.po +POFILES= sunfire.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/sunfire.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/sparc/sunfire/common/sunfire.c b/usr/src/lib/libprtdiag_psr/sparc/sunfire/common/sunfire.c new file mode 100644 index 0000000000..cfb83f25a0 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/sunfire/common/sunfire.c @@ -0,0 +1,2284 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Sunfire Platform specific functions. + * + * called when : + * machine_type == MTYPE_SUNFIRE + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +/* Macros for manipulating UPA IDs and board numbers on Sunfire. */ +#define bd_to_upa(bd) ((bd) << 1) +#define upa_to_bd(upa) ((upa) >> 1) + +#define MAX_MSGS 64 + +extern int print_flag; + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (sunfire systems only) + */ +int error_check(Sys_tree *tree, struct system_kstat_data *kstats); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); +int disp_fail_parts(Sys_tree *tree); +void display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats, + struct grp_info *grps, struct mem_total *memory_total); +void display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void display_mid(int mid); +void display_pci(Board_node *); +void display_ffb(Board_node *, int); +void add_node(Sys_tree *, Prom_node *); +void resolve_board_types(Sys_tree *); + +/* local functions */ +static void build_mem_tables(Sys_tree *, struct system_kstat_data *, + struct grp_info *); +static void get_mem_total(struct mem_total *, struct grp_info *); +static int disp_fault_list(Sys_tree *, struct system_kstat_data *); +static int disp_err_log(struct system_kstat_data *); +static int disp_env_status(struct system_kstat_data *); +static int disp_keysw_and_leds(struct system_kstat_data *); +static void sunfire_disp_prom_versions(Sys_tree *); +static void erase_msgs(char **); +static void display_msgs(char **msgs, int board); +static void sunfire_disp_asic_revs(Sys_tree *, struct system_kstat_data *); +static void display_hp_boards(struct system_kstat_data *); +static int disp_parts(char **, u_longlong_t, int); +/* + * Error analysis routines. These routines decode data from specified + * error registers. They are meant to be used for decoding the fatal + * hardware reset data passed to the kernel by sun4u POST. + */ +static int analyze_cpu(char **, int, u_longlong_t); +static int analyze_ac(char **, u_longlong_t); +static int analyze_dc(int, char **, u_longlong_t); + +#define RESERVED_STR "Reserved" + +#define MAX_PARTS 5 +#define MAX_FRUS 5 + +#define MAXSTRLEN 256 + +/* Define special bits */ +#define UPA_PORT_A 0x1 +#define UPA_PORT_B 0x2 + + +/* + * These defines comne from async.h, but it does not get exported from + * uts/sun4u/sys, so they must be redefined. + */ +#define P_AFSR_ISAP 0x0000000040000000ULL /* incoming addr. parity err */ +#define P_AFSR_ETP 0x0000000020000000ULL /* ecache tag parity */ +#define P_AFSR_ETS 0x00000000000F0000ULL /* cache tag parity syndrome */ +#define ETS_SHIFT 16 + +/* List of parts possible */ +#define RSVD_PART 1 +#define UPA_PART 2 +#define UPA_A_PART 3 +#define UPA_B_PART 4 +#define SOFTWARE_PART 5 +#define AC_PART 6 +#define AC_ANY_PART 7 +#define DTAG_PART 8 +#define DTAG_A_PART 9 +#define DTAG_B_PART 10 +#define FHC_PART 11 +#define BOARD_PART 12 +#define BOARD_ANY_PART 13 +#define BOARD_CONN_PART 14 +#define BACK_PIN_PART 15 +#define BACK_TERM_PART 16 +#define CPU_PART 17 + +/* List of possible parts */ +static char *part_str[] = { + "", /* 0, a placeholder for indexing */ + "", /* 1, reserved strings shouldn't be printed */ + "UPA devices", /* 2 */ + "UPA Port A device", /* 3 */ + "UPA Port B device", /* 4 */ + "Software error", /* 5 */ + "Address Controller", /* 6 */ + "Undetermined Address Controller in system", /* 7 */ + "Data Tags", /* 8 */ + "Data Tags for UPA Port A", /* 9 */ + "Data Tags for UPA Port B", /* 10 */ + "Firehose Controller", /* 11 */ + "This Board", /* 12 */ + "Undetermined Board in system", /* 13 */ + "Board Connector", /* 14 */ + "Centerplane pins ", /* 15 */ + "Centerplane terminators", /* 16 */ + "CPU", /* 17 */ +}; + +/* Ecache parity error messages. Tells which bits are bad. */ +static char *ecache_parity[] = { + "Bits 7:0 ", + "Bits 15:8 ", + "Bits 21:16 ", + "Bits 24:22 " +}; + + +struct ac_error { + char *error; + int part[MAX_PARTS]; +}; + +typedef struct ac_error ac_err; + +/* + * Hardware error register meanings, failed parts and FRUs. The + * following strings are indexed for the bit positions of the + * corresponding bits in the hardware. The code checks bit x of + * the hardware error register and prints out string[x] if the bit + * is turned on. + * + * This database of parts which are probably failed and which FRU's + * to replace was based on knowledge of the Sunfire Programmers Spec. + * and discussions with the hardware designers. The order of the part + * lists and consequently the FRU lists are in the order of most + * likely cause first. + */ +static ac_err ac_errors[] = { + { /* 0 */ + "UPA Port A Error", + { UPA_A_PART, 0, 0, 0, 0 }, + }, + { /* 1 */ + "UPA Port B Error", + { UPA_B_PART, 0, 0, 0, 0 }, + }, + { /* 2 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 3 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 4 */ + "UPA Interrupt to unmapped destination", + { BOARD_PART, 0, 0, 0, 0 }, + }, + { /* 5 */ + "UPA Non-cacheable write to unmapped destination", + { BOARD_PART, 0, 0, 0, 0 }, + }, + { /* 6 */ + "UPA Cacheable write to unmapped destination", + { BOARD_PART, 0, 0, 0, 0 }, + }, + { /* 7 */ + "Illegal Write Received", + { BOARD_PART, 0, 0, 0, 0 }, + }, + { /* 8 */ + "Local Writeback match with line in state S", + { AC_PART, DTAG_PART, 0, 0, 0 }, + }, + { /* 9 */ + "Local Read match with valid line in Tags", + { AC_PART, DTAG_PART, 0, 0, 0 }, + }, + { /* 10 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 11 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 12 */ + "Tag and Victim were valid during lookup", + { AC_PART, DTAG_PART, 0, 0, 0 }, + }, + { /* 13 */ + "Local Writeback matches a victim in state S", + { AC_PART, CPU_PART, 0, 0, 0 }, + }, + { /* 14 */ + "Local Read matches valid line in victim buffer", + { AC_PART, CPU_PART, 0, 0, 0 }, + }, + { /* 15 */ + "Local Read victim bit set and victim is S state", + { AC_PART, CPU_PART, 0, 0, 0 }, + }, + { /* 16 */ + "Local Read Victim bit set and Valid Victim Buffer", + { AC_PART, CPU_PART, 0, 0, 0 }, + }, + { /* 17 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 18 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 19 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 20 */ + "UPA Transaction received in Sleep mode", + { AC_PART, 0, 0, 0, 0 }, + }, + { /* 21 */ + "P_FERR error P_REPLY received from UPA Port", + { CPU_PART, AC_PART, 0, 0, 0 }, + }, + { /* 22 */ + "Illegal P_REPLY received from UPA Port", + { CPU_PART, AC_PART, 0, 0, 0 }, + }, + { /* 23 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 24 */ + "Timeout on a UPA Master Port", + { AC_ANY_PART, BOARD_ANY_PART, 0, 0, 0 }, + }, + { /* 25 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 26 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 27 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 28 */ + "Coherent Transactions Queue Overflow Error", + { BACK_PIN_PART, BOARD_CONN_PART, AC_PART, AC_ANY_PART, 0 }, + }, + { /* 29 */ + "Non-cacheable Request Queue Overflow Error", + { AC_PART, AC_ANY_PART, 0, 0, 0 }, + }, + { /* 30 */ + "Non-cacheable Reply Queue Overflow Error", + { AC_PART, 0, 0, 0, 0 }, + }, + { /* 31 */ + "PREQ Queue Overflow Error", + { CPU_PART, AC_PART, 0, 0, 0 }, + }, + { /* 32 */ + "Foreign DID CAM Overflow Error", + { AC_PART, AC_ANY_PART, 0, 0, 0 }, + }, + { /* 33 */ + "FT->UPA Queue Overflow Error", + { BACK_PIN_PART, BOARD_CONN_PART, AC_PART, AC_ANY_PART, 0 }, + }, + { /* 34 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 35 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 36 */ + "UPA Port B Dtag Parity Error", + { DTAG_B_PART, AC_PART, 0, 0, 0 }, + }, + { /* 37 */ + "UPA Port A Dtag Parity Error", + { DTAG_A_PART, AC_PART, 0, 0, 0 }, + }, + { /* 38 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 39 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 40 */ + "UPA Bus Parity Error", + { UPA_PART, AC_PART, 0, 0, 0 }, + }, + { /* 41 */ + "Data ID Line Mismatch", + { BACK_PIN_PART, BOARD_CONN_PART, AC_PART, 0, 0 }, + }, + { /* 42 */ + "Arbitration Line Mismatch", + { BACK_PIN_PART, BOARD_CONN_PART, AC_PART, 0, 0 }, + }, + { /* 43 */ + "Shared Line Parity Mismatch", + { BACK_PIN_PART, BOARD_CONN_PART, AC_PART, 0, 0 }, + }, + { /* 44 */ + "FireTruck Control Line Parity Error", + { AC_PART, BACK_PIN_PART, 0, 0, 0 }, + }, + { /* 45 */ + "FireTruck Address Bus Parity Error", + { AC_PART, BACK_PIN_PART, 0, 0, 0 }, + }, + { /* 46 */ + "Internal RAM Parity Error", + { AC_PART, 0, 0, 0, 0 }, + }, + { /* 47 */ + NULL, + { RSVD_PART, 0, 0, 0, 0 }, + }, + { /* 48 */ + "Internal Hardware Error", + { AC_PART, 0, 0, 0, 0 }, + }, + { /* 49 */ + "FHC Communications Error", + { FHC_PART, AC_PART, 0, 0, 0 }, + }, + /* Bits 50-63 are reserved in this implementation. */ +}; + + +#define MAX_BITS (sizeof (ac_errors)/ sizeof (ac_err)) + +/* + * There are only two error bits in the DC shadow chain that are + * important. They indicate an overflow error and a parity error, + * respectively. The other bits are not error bits and should not + * be checked for. + */ +#define DC_OVERFLOW 0x2 +#define DC_PARITY 0x4 + +static char dc_overflow_txt[] = "Board %d DC %d Overflow Error"; +static char dc_parity_txt[] = "Board %d DC %d Parity Error"; + +/* defines for the sysio */ +#define UPA_APERR 0x4 + +int +error_check(Sys_tree *tree, struct system_kstat_data *kstats) +{ + int exit_code = 0; /* init to all OK */ + + /* + * silently check for any types of machine errors + */ + print_flag = 0; + if (disp_fail_parts(tree) || disp_fault_list(tree, kstats) || + disp_err_log(kstats) || disp_env_status(kstats)) { + /* set exit_code to show failures */ + exit_code = 1; + } + print_flag = 1; + + return (exit_code); +} + +/* + * disp_fail_parts + * + * Display the failed parts in the system. This function looks for + * the status property in all PROM nodes. On systems where + * the PROM does not supports passing diagnostic information + * thruogh the device tree, this routine will be silent. + */ +int +disp_fail_parts(Sys_tree *tree) +{ + int exit_code; + int system_failed = 0; + Board_node *bnode = tree->bd_list; + Prom_node *pnode; + + exit_code = 0; + + /* go through all of the boards looking for failed units. */ + while (bnode != NULL) { + /* find failed chips */ + pnode = find_failed_node(bnode->nodes); + if ((pnode != NULL) && !system_failed) { + system_failed = 1; + exit_code = 1; + if (print_flag == 0) { + return (exit_code); + } + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, + "Failed Field Replaceable Units (FRU) " + "in System:\n"), 0); + log_printf("==========================" + "====================\n", 0); + } + + while (pnode != NULL) { + void *value; + char *name; /* node name string */ + char *type; /* node type string */ + char *board_type = NULL; + + value = get_prop_val(find_prop(pnode, "status")); + name = get_node_name(pnode); + + /* sanity check of data retreived from PROM */ + if ((value == NULL) || (name == NULL)) { + pnode = next_failed_node(pnode); + continue; + } + + /* Find the board type of this board */ + if (bnode->board_type == CPU_BOARD) { + board_type = "CPU"; + } else { + board_type = "IO"; + } + + log_printf(dgettext(TEXT_DOMAIN, + "%s unavailable on %s Board #%d\n"), + name, board_type, bnode->board_num, 0); + + log_printf(dgettext(TEXT_DOMAIN, + "\tPROM fault string: %s\n"), value, 0); + + log_printf(dgettext(TEXT_DOMAIN, + "\tFailed Field Replaceable Unit is "), 0); + + /* + * Determine whether FRU is CPU module, system + * board, or SBus card. + */ + if ((name != NULL) && (strstr(name, "sbus"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "SBus Card %d\n"), + get_sbus_slot(pnode), 0); + + } else if (((name = get_node_name(pnode->parent)) != + NULL) && (strstr(name, "pci"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "PCI Card %d"), + get_pci_device(pnode), 0); + + } else if (((type = get_node_type(pnode)) != NULL) && + (strstr(type, "cpu"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "UltraSPARC module " + "Board %d Module %d\n"), + get_id(pnode) >> 1, + get_id(pnode) & 0x1); + + } else { + log_printf(dgettext(TEXT_DOMAIN, + "%s board %d\n"), board_type, + bnode->board_num, 0); + } + pnode = next_failed_node(pnode); + } + bnode = bnode->next; + } + + if (!system_failed) { + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, + "No failures found in System\n"), 0); + log_printf("===========================\n", 0); + } + + if (system_failed) + return (1); + else + return (0); +} + +void +display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats, + struct grp_info *grps, struct mem_total *memory_total) { + + /* Build the memory group tables and interleave data */ + build_mem_tables(tree, kstats, grps); + + /* display total usable installed memory */ + get_mem_total(memory_total, grps); + (void) log_printf(dgettext(TEXT_DOMAIN, + "Memory size: %4dMb\n"), memory_total->dram, 0); + + /* We display the NVSIMM size totals separately. */ + if (memory_total->nvsimm != 0) { + (void) log_printf(dgettext(TEXT_DOMAIN, + "NVSIMM size: %4dMb\n"), memory_total->nvsimm); + } +} + +/* + * This routine displays the memory configuration for all boards in the + * system. + */ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + int group; + char *status_str[] = { "Unknown", " Empty ", " Failed", " Active", + " Spare " }; + char *cond_str[] = { " Unknown ", " OK ", " Failing ", + " Failed ", " Uninit. " }; + +#ifdef lint + tree = tree; +#endif + /* Print the header for the memory section. */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " Memory "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + log_printf(" Intrlv. " + "Intrlv.\n", 0); + log_printf("Brd Bank MB Status Condition Speed Factor " + " With\n", 0); + log_printf("--- ----- ---- ------- ---------- ----- ------- " + "-------\n", 0); + + /* Print the Memory groups information. */ + for (group = 0; group < MAX_GROUPS; group++) { + struct grp *grp; + + grp = &grps->grp[group]; + + /* If this board is not a CPU or MEM board, skip it. */ + if ((grp->type != MEM_BOARD) && (grp->type != CPU_BOARD)) { + continue; + } + + if (grp->valid) { + log_printf("%2d ", grp->board, 0); + log_printf(" %1d ", grp->group, 0); + log_printf("%4d ", grp->size, 0); + log_printf("%7s ", status_str[grp->status], 0); + log_printf("%10s ", cond_str[grp->condition], 0); + log_printf("%3dns ", grp->speed, 0); + log_printf("%3d-way ", grp->factor, 0); + if (grp->factor > 1) { + log_printf("%4c", grp->groupid, 0); + } + log_printf("\n", 0); + } + } + +} + + +void +display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) +{ + /* Display Hot plugged, disabled and failed boards */ + (void) display_hp_boards(kstats); + + /* Display failed units */ + (void) disp_fail_parts(tree); + + /* Display fault info */ + (void) disp_fault_list(tree, kstats); +} + +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + /* + * Now display the last powerfail time and the fatal hardware + * reset information. We do this under a couple of conditions. + * First if the user asks for it. The second is iof the user + * told us to do logging, and we found a system failure. + */ + if (flag) { + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + /* Display system environmental conditions. */ + (void) disp_env_status(kstats); + + /* Display ASIC Chip revs for all boards. */ + sunfire_disp_asic_revs(tree, kstats); + + /* Print the PROM revisions here */ + sunfire_disp_prom_versions(tree); + + /* + * Display the latest system fatal hardware + * error data, if any. The system holds this + * data in SRAM, so it does not persist + * across power-on resets. + */ + (void) disp_err_log(kstats); + } +} + +void +display_mid(int mid) +{ + log_printf(" %2d ", mid % 2, 0); +} + +/* + * display_pci + * Call the generic psycho version of this function. + */ +void +display_pci(Board_node *board) +{ + display_psycho_pci(board); +} + +/* + * display_ffb + * Display all FFBs on this board. It can either be in tabular format, + * or a more verbose format. + */ +void +display_ffb(Board_node *board, int table) +{ + Prom_node *ffb; + void *value; + struct io_card *card_list = NULL; + struct io_card card; + + if (board == NULL) + return; + + /* Fill in common information */ + card.display = 1; + card.board = board->board_num; + (void) sprintf(card.bus_type, "UPA"); + card.freq = sys_clk; + + for (ffb = dev_find_node(board->nodes, FFB_NAME); ffb != NULL; + ffb = dev_next_node(ffb, FFB_NAME)) { + if (table == 1) { + /* Print out in table format */ + + /* XXX - Get the slot number (hack) */ + card.slot = get_id(ffb); + + /* Find out if it's single or double buffered */ + (void) sprintf(card.name, "FFB"); + value = get_prop_val(find_prop(ffb, "board_type")); + if (value != NULL) + if ((*(int *)value) & FFB_B_BUFF) + (void) sprintf(card.name, "FFB, " + "Double Buffered"); + else + (void) sprintf(card.name, "FFB, " + "Single Buffered"); + + /* Print model number */ + card.model[0] = '\0'; + value = get_prop_val(find_prop(ffb, "model")); + if (value != NULL) + (void) sprintf(card.model, "%s", + (char *)value); + + card_list = insert_io_card(card_list, &card); + } else { + /* print in long format */ + char device[MAXSTRLEN]; + int fd = -1; + struct dirent *direntp; + DIR *dirp; + union strap_un strap; + struct ffb_sys_info fsi; + + /* Find the device node using upa address */ + value = get_prop_val(find_prop(ffb, "upa-portid")); + if (value == NULL) + continue; + + (void) sprintf(device, "%s@%x", FFB_NAME, + *(int *)value); + if ((dirp = opendir("/devices")) == NULL) + continue; + + while ((direntp = readdir(dirp)) != NULL) { + if (strstr(direntp->d_name, device) != NULL) { + (void) sprintf(device, "/devices/%s", + direntp->d_name); + fd = open(device, O_RDWR, 0666); + break; + } + } + (void) closedir(dirp); + + if (fd == -1) + continue; + + if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0) + continue; + + log_printf("Board %d FFB Hardware Configuration:\n", + board->board_num, 0); + log_printf("-----------------------------------\n", 0); + + strap.ffb_strap_bits = fsi.ffb_strap_bits; + log_printf("\tBoard rev: %d\n", + (int)strap.fld.board_rev, 0); + log_printf("\tFBC version: 0x%x\n", fsi.fbc_version, 0); + log_printf("\tDAC: %s\n", + fmt_manf_id(fsi.dac_version, device), 0); + log_printf("\t3DRAM: %s\n", + fmt_manf_id(fsi.fbram_version, device), 0); + log_printf("\n", 0); + } + } + + display_io_cards(card_list); + free_io_cards(card_list); +} + +/* + * add_node + * + * This function adds a board node to the board structure where that + * that node's physical component lives. + */ +void +add_node(Sys_tree *root, Prom_node *pnode) +{ + int board; + Board_node *bnode; + char *name = get_node_name(pnode); + Prom_node *p; + + /* add this node to the Board list of the appropriate board */ + if ((board = get_board_num(pnode)) == -1) { + void *value; + + /* + * if it is a server, pci nodes and ffb nodes never have + * board number properties and software can find the board + * number from the reg property. It is derived from the + * high word of the 'reg' property, which contains the + * mid. + */ + if ((name != NULL) && + ((strcmp(name, FFB_NAME) == 0) || + (strcmp(name, "pci") == 0) || + (strcmp(name, "counter-timer") == 0))) { + /* extract the board number from the 'reg' prop. */ + if ((value = get_prop_val(find_prop(pnode, + "reg"))) == NULL) { + (void) printf("add_node() no reg property\n"); + exit(2); + } + board = (*(int *)value - 0x1c0) / 4; + } + } + + /* find the node with the same board number */ + if ((bnode = find_board(root, board)) == NULL) { + bnode = insert_board(root, board); + bnode->board_type = UNKNOWN_BOARD; + } + + /* now attach this prom node to the board list */ + /* Insert this node at the end of the list */ + pnode->sibling = NULL; + if (bnode->nodes == NULL) + bnode->nodes = pnode; + else { + p = bnode->nodes; + while (p->sibling != NULL) + p = p->sibling; + p->sibling = pnode; + } + +} + +/* + * Function resolve_board_types + * + * After the tree is walked and all the information is gathered, this + * function is called to resolve the type of each board. + */ +void +resolve_board_types(Sys_tree *tree) +{ + Board_node *bnode; + Prom_node *pnode; + char *type; + + bnode = tree->bd_list; + while (bnode != NULL) { + bnode->board_type = UNKNOWN_BOARD; + + pnode = dev_find_node(bnode->nodes, "fhc"); + type = get_prop_val(find_prop(pnode, "board-type")); + if (type == NULL) { + bnode = bnode->next; + continue; + } + + if (strcmp(type, CPU_BD_NAME) == 0) { + bnode->board_type = CPU_BOARD; + } else if (strcmp(type, MEM_BD_NAME) == 0) { + bnode->board_type = MEM_BOARD; + } else if (strcmp(type, DISK_BD_NAME) == 0) { + bnode->board_type = DISK_BOARD; + } else if (strcmp(type, IO_SBUS_FFB_BD_NAME) == 0) { + bnode->board_type = IO_SBUS_FFB_BOARD; + } else if (strcmp(type, IO_2SBUS_BD_NAME) == 0) { + bnode->board_type = IO_2SBUS_BOARD; + } else if (strcmp(type, IO_PCI_BD_NAME) == 0) { + bnode->board_type = IO_PCI_BOARD; + } else if (strcmp(type, IO_2SBUS_SOCPLUS_BD_NAME) == 0) { + bnode->board_type = IO_2SBUS_SOCPLUS_BOARD; + } else if (strcmp(type, IO_SBUS_FFB_SOCPLUS_BD_NAME) == 0) { + bnode->board_type = IO_SBUS_FFB_SOCPLUS_BOARD; + } + + bnode = bnode->next; + } + +} + +/* + * local functions + */ + +static void +sunfire_disp_prom_versions(Sys_tree *tree) +{ + Board_node *bnode; + + /* Display Prom revision header */ + log_printf("System Board PROM revisions:\n", 0); + log_printf("----------------------------\n", 0); + + /* For each board, print the POST and OBP versions */ + for (bnode = tree->bd_list; bnode != NULL; bnode = bnode->next) { + Prom_node *flashprom; /* flashprom device node */ + + /* find a flashprom node for this board */ + flashprom = dev_find_node(bnode->nodes, "flashprom"); + + /* If no flashprom node found, continue */ + if (flashprom == NULL) + continue; + + /* flashprom node found, display board# */ + log_printf("Board %2d: ", bnode->board_num, 0); + + disp_prom_version(flashprom); + } +} + + +/* + * functions that are only needed inside this library + */ + +/* + * build_mem_tables + * + * This routine builds the memory table which tells how much memory + * is present in each SIMM group of each board, what the interleave + * factors are, and the group ID of the interleave group. + * + * The algorithms used are: + * First fill in the sizes of groups. + * Next build lists of all groups with same physical base. + * From #of members in each list, interleave factor is + * determined. + * All members of a certain list get the same interleave + * group ID. + */ +static void +build_mem_tables(Sys_tree *tree, + struct system_kstat_data *kstats, + struct grp_info *grps) +{ + struct mem_inter inter_grps; /* temp structure for interleaves */ + struct inter_grp *intrp; + int group; + int i; + + /* initialize the interleave lists */ + for (i = 0, intrp = &inter_grps.i_grp[0]; i < MAX_GROUPS; i++, + intrp++) { + intrp->valid = 0; + intrp->count = 0; + intrp->groupid = '\0'; + intrp->base = 0; + } + + for (group = 0; group < MAX_GROUPS; group++) { + int found; + int board; + struct grp *grp; + struct bd_kstat_data *bksp; + uchar_t simm_reg; + Board_node *bnode; + + board = group/2; + bksp = &kstats->bd_ksp_list[board]; + grp = &grps->grp[group]; + grp->group = group % 2; + + /* + * Copy the board type field into the group record. + */ + if ((bnode = find_board(tree, board)) != NULL) { + grp->type = bnode->board_type; + } else { + grp->type = UNKNOWN_BOARD; + continue; + } + + /* Make sure we have kstats for this board */ + if (bksp->ac_kstats_ok == 0) { + /* Mark this group as invalid and move to next one */ + grp->valid = 0; + continue; + } + + /* Find the bank status property */ + if (bksp->ac_memstat_ok) { + grp->status = bksp->mem_stat[grp->group].status; + grp->condition = bksp->mem_stat[grp->group].condition; + } else { + grp->status = StUnknown; + grp->condition = ConUnknown; + } + + switch (grp->status) { + case StBad: + case StActive: + case StSpare: + break; + default: + grp->status = StUnknown; + break; + } + + switch (grp->condition) { + case ConOK: + case ConFailing: + case ConFailed: + case ConTest: + case ConBad: + break; + default: + grp->condition = ConUnknown; + break; + } + + /* base the group size off of the simmstat kstat. */ + if (bksp->simmstat_kstats_ok == 0) { + grp->valid = 0; + continue; + } + + /* Is it bank 0 or bank 1 */ + if (grp->group == 0) { + simm_reg = bksp->simm_status[0]; + } else { + simm_reg = bksp->simm_status[1]; + } + + /* Now decode the size field. */ + switch (simm_reg & 0x1f) { + case MEM_SIZE_64M: + grp->size = 64; + break; + case MEM_SIZE_256M: + grp->size = 256; + break; + case MEM_SIZE_1G: + grp->size = 1024; + break; + case MEM_SIZE_2G: + grp->size = 2048; + break; + default: + grp->valid = 0; + continue; + } + + /* Decode the speed field */ + switch ((simm_reg & 0x60) >> 5) { + case MEM_SPEED_50ns: + grp->speed = 50; + break; + case MEM_SPEED_60ns: + grp->speed = 60; + break; + case MEM_SPEED_70ns: + grp->speed = 70; + break; + case MEM_SPEED_80ns: + grp->speed = 80; + break; + } + + grp->valid = 1; + grp->base = GRP_BASE(bksp->ac_memdecode[grp->group]); + grp->board = board; + if (grp->group == 0) { + grp->factor = INTLV0(bksp->ac_memctl); + } else { /* assume it is group 1 */ + grp->factor = INTLV1(bksp->ac_memctl); + } + grp->groupid = '\0'; /* Not in a group yet */ + + /* + * find the interleave list this group belongs on. If the + * interleave list corresponding to this base address is + * not found, then create a new one. + */ + + i = 0; + intrp = &inter_grps.i_grp[0]; + found = 0; + while ((i < MAX_GROUPS) && !found && (intrp->valid != 0)) { + if ((intrp->valid != 0) && + (intrp->base == grp->base)) { + grp->groupid = intrp->groupid; + intrp->count++; + found = 1; + } + i++; + intrp++; + } + /* + * We did not find a matching base. So now i and intrp + * now point to the next interleave group in the list. + */ + if (!found) { + intrp->count++; + intrp->valid = 1; + intrp->groupid = 'A' + (char)i; + intrp->base = grp->base; + grp->groupid = intrp->groupid; + } + } +} + + +static void +get_mem_total(struct mem_total *mem_total, struct grp_info *grps) +{ + struct grp *grp; + int i; + + /* Start with total of zero */ + mem_total->dram = 0; + mem_total->nvsimm = 0; + + /* For now we ignore NVSIMMs. We might want to fix this later. */ + for (i = 0, grp = &grps->grp[0]; i < MAX_GROUPS; i++, grp++) { + if (grp->valid == 1 && grp->status == StActive) { + mem_total->dram += grp->size; + } + } +} + +static int +disp_fault_list(Sys_tree *tree, struct system_kstat_data *kstats) +{ + struct ft_list *ftp; + int i; + int result = 0; + time_t t; + + if (!kstats->ft_kstat_ok) { + return (result); + } + + for (i = 0, ftp = kstats->ft_array; i < kstats->nfaults; i++, ftp++) { + if (!result) { + log_printf("\n", 0); + log_printf("Detected System Faults\n", 0); + log_printf("======================\n", 0); + } + result = 1; + if (ftp->fclass == FT_BOARD) { + log_printf("Board %d fault: %s\n", ftp->unit, + ftp->msg, 0); + + /* + * If the fault on this board is PROM inherited, see + * if we can find some failed component information + * in the PROM device tree. The general solution + * would be to fix the fhc driver and have it put in + * more descriptive messages, but that's for another + * day. + */ + + if (ftp->type == FT_PROM) { + Board_node *bn; + Prom_node *pn; + char *str; + + bn = find_board(tree, ftp->unit); + /* + * If any nodes under this board have a + * status containing "fail", print it out. + */ + pn = find_failed_node(bn->nodes); + while (pn) { + str = get_prop_val(find_prop(pn, + "status")); + if (str != NULL) { + log_printf("Fault: %s\n", str, + 0); + } + + pn = next_failed_node(pn); + } + } + } else if ((ftp->type == FT_CORE_PS) || (ftp->type == FT_PPS)) { + log_printf("Unit %d %s failure\n", ftp->unit, + ftp->msg, 0); + } else if ((ftp->type == FT_OVERTEMP) && + (ftp->fclass == FT_SYSTEM)) { + log_printf("Clock board %s\n", ftp->msg, 0); + } else { + log_printf("%s failure\n", ftp->msg, 0); + } + + t = (time_t)ftp->create_time; + log_printf("\tDetected %s", + asctime(localtime(&t)), 0); + } + + if (!result) { + log_printf("\n", 0); + log_printf("No System Faults found\n", 0); + log_printf("======================\n", 0); + } + + log_printf("\n", 0); + + return (result); +} + + +/* + * disp_err_log + * + * Display the fatal hardware reset system error logs. These logs are + * collected by POST and passed up through the kernel to userland. + * They will not necessarily be present in all systems. Their form + * might also be different in different systems. + * + * NOTE - We are comparing POST defined board types here. Do not confuse + * them with kernel board types. The structure being analyzed in this + * function is created by POST. All the defines for it are in reset_info.h, + * which was ported from POST header files. + */ +static int +disp_err_log(struct system_kstat_data *kstats) +{ + int exit_code = 0; + int i; + struct reset_info *rst_info; + struct board_info *bdp; + char *err_msgs[MAX_MSGS]; /* holds all messages for a system board */ + int msg_idx; /* current msg number */ + int count; /* number added by last analyze call */ + char **msgs; + + /* start by initializing the err_msgs array to all NULLs */ + for (i = 0; i < MAX_MSGS; i++) { + err_msgs[i] = NULL; + } + + /* First check to see that the reset-info kstats are present. */ + if (kstats->reset_kstats_ok == 0) { + return (exit_code); + } + + rst_info = &kstats->reset_info; + + /* Everything is OK, so print out time/date stamp first */ + log_printf("\n", 0); + log_printf( + dgettext(TEXT_DOMAIN, + "Analysis of most recent Fatal Hardware Watchdog:\n"), + 0); + log_printf("======================================================\n", + 0); + log_printf("Log Date: %s\n", + get_time(&kstats->reset_info.tod_timestamp[0]), 0); + + /* initialize the vector and the message index. */ + msgs = err_msgs; + msg_idx = 0; + + /* Loop Through all of the boards. */ + bdp = &rst_info->bd_reset_info[0]; + for (i = 0; i < MAX_BOARDS; i++, bdp++) { + + /* Is there data for this board? */ + if ((bdp->board_desc & BD_STATE_MASK) == BD_NOT_PRESENT) { + continue; + } + + /* If it is a CPU Board, look for CPU data. */ + if (BOARD_TYPE(bdp->board_desc) == CPU_TYPE) { + /* analyze CPU 0 if present */ + if (bdp->board_desc & CPU0_OK) { + count = analyze_cpu(msgs, 0, + bdp->cpu[0].afsr); + msgs += count; + msg_idx += count; + } + + /* analyze CPU1 if present. */ + if (bdp->board_desc & CPU1_OK) { + count = analyze_cpu(msgs, 1, + bdp->cpu[1].afsr); + msgs += count; + msg_idx += count; + } + } + + /* Always Analyze the AC and the DCs on a board. */ + count = analyze_ac(msgs, bdp->ac_error_status); + msgs += count; + msg_idx += count; + + count = analyze_dc(i, msgs, bdp->dc_shadow_chain); + msgs += count; + msg_idx += count; + + if (msg_idx != 0) + display_msgs(err_msgs, i); + + erase_msgs(err_msgs); + + /* If any messages are logged, we have errors */ + if (msg_idx != 0) { + exit_code = 1; + } + + /* reset the vector and the message index */ + msg_idx = 0; + msgs = &err_msgs[0]; + } + + return (exit_code); +} + +static void +erase_msgs(char **msgs) +{ + int i; + + for (i = 0; (*msgs != NULL) && (i < MAX_MSGS); i++, msgs++) { + free(*msgs); + *msgs = NULL; + } +} + + +static void +display_msgs(char **msgs, int board) +{ + int i; + + /* display the header for this board */ + print_header(board); + + for (i = 0; (*msgs != NULL) && (i < MAX_MSGS); i++, msgs++) { + log_printf(*msgs, 0); + } +} + + + +/* + * disp_keysw_and_leds + * + * This routine displays the position of the keyswitch and the front panel + * system LEDs. The keyswitch can be in either normal, diagnostic, or + * secure position. The three front panel LEDs are of importance because + * the center LED indicates component failure on the system. + */ +static int +disp_keysw_and_leds(struct system_kstat_data *kstats) +{ + int board; + int diag_mode = 0; + int secure_mode = 0; + int result = 0; + + /* Check the first valid board to determeine the diag bit */ + /* Find the first valid board */ + for (board = 0; board < MAX_BOARDS; board++) { + if (kstats->bd_ksp_list[board].fhc_kstats_ok != 0) { + /* If this was successful, break out of loop */ + if ((kstats->bd_ksp_list[board].fhc_bsr & + FHC_DIAG_MODE) == 0) + diag_mode = 1; + break; + } + } + + /* + * Check the register on the clock-board to determine the + * secure bit. + */ + if (kstats->sys_kstats_ok) { + /* The secure bit is negative logic. */ + if (kstats->keysw_status == KEY_SECURE) { + secure_mode = 1; + } + } + + /* + * The system cannot be in diag and secure mode. This is + * illegal. + */ + if (secure_mode && diag_mode) { + result = 2; + return (result); + } + + /* Now print the keyswitch position. */ + log_printf("Keyswitch position is in ", 0); + + if (diag_mode) { + log_printf("Diagnostic Mode\n"); + } else if (secure_mode) { + log_printf("Secure Mode\n", 0); + } else { + log_printf("Normal Mode\n"); + } + + /* display the redundant power status */ + if (kstats->sys_kstats_ok) { + log_printf("System Power Status: ", 0); + + switch (kstats->power_state) { + case REDUNDANT: + log_printf("Redundant\n", 0); + break; + + case MINIMUM: + log_printf("Minimum Available\n", 0); + break; + + case BELOW_MINIMUM: + log_printf("Insufficient Power Available\n", 0); + break; + + default: + log_printf("Unknown\n", 0); + break; + } + } + + if (kstats->sys_kstats_ok) { + /* + * If the center LED is on, then we return a non-zero + * result. + */ + log_printf("System LED Status: GREEN YELLOW " + "GREEN\n", 0); + if ((kstats->sysctrl & SYS_LED_MID) != 0) { + log_printf("WARNING ", 0); + } else { + log_printf("Normal ", 0); + } + + /* + * Left LED is negative logic, center and right LEDs + * are positive logic. + */ + if ((kstats->sysctrl & SYS_LED_LEFT) == 0) { + log_printf("ON ", 0); + } else { + log_printf("OFF", 0); + } + + log_printf(" ", 0); + if ((kstats->sysctrl & SYS_LED_MID) != 0) { + log_printf("ON ", 0); + } else { + log_printf("OFF", 0); + } + + log_printf(" BLINKING", 0); + } + + log_printf("\n", 0); + return (result); +} + +/* + * disp_env_status + * + * This routine displays the environmental status passed up from + * device drivers via kstats. The kstat names are defined in + * kernel header files included by this module. + */ +static int +disp_env_status(struct system_kstat_data *kstats) +{ + struct bd_kstat_data *bksp; + int exit_code = 0; + int i; + uchar_t curr_temp; + int is4slot = 0; + + /* + * Define some message arrays to make life simpler. These + * messages correspond to definitions in <sys/fhc.c> for + * temperature trend (enum temp_trend) and temperature state + * (enum temp_state). + */ + static char *temp_trend_msg[] = { "unknown", + "rapidly falling", + "falling", + "stable", + "rising", + "rapidly rising", + "unknown (noisy)" + }; + static char *temp_state_msg[] = { " OK ", + "WARNING ", + " DANGER " + }; + + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " Environmental Status "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + + exit_code = disp_keysw_and_leds(kstats); + + if (!kstats->sys_kstats_ok) { + log_printf(dgettext(TEXT_DOMAIN, + "*** Error: Unavailable ***\n\n")); + return (1); + } + + /* + * for purposes within this routine, + * 5 slot behaves the same as a 4 slot + */ + if (SYS_TYPE(kstats->sysstat1) == SYS_4_SLOT) + is4slot = 1; + + log_printf("\n", 0); + log_printf("\nFans:\n", 0); + log_printf("-----\n", 0); + + log_printf("Unit Status\n", 0); + log_printf("---- ------\n", 0); + + log_printf("%-4s ", is4slot ? "Disk" : "Rack", 0); + /* Check the status of the Rack Fans */ + if ((kstats->fan_status & SYS_RACK_FANFAIL) == 0) { + log_printf("OK\n", 0); + } else { + log_printf("FAIL\n", 0); + exit_code = 1; + } + + if (!is4slot) { + /* + * keyswitch and ac box are on 8 & 16 slot only + */ + /* Check the status of the Keyswitch Fan assembly. */ + log_printf("%-4s ", "Key", 0); + if ((kstats->fan_status & SYS_KEYSW_FAN_OK) != 0) { + log_printf("OK\n", 0); + } else { + log_printf("FAIL\n", 0); + exit_code = 1; + } + + log_printf("%-4s ", "AC", 0); + if ((kstats->fan_status & SYS_AC_FAN_OK) != 0) { + log_printf("OK\n", 0); + } else { + log_printf("FAIL\n", 0); + exit_code = 1; + } + } else { + /* + * peripheral fan is on 4 slot only + * XXX might want to indicate transient states too + */ + if (kstats->psstat_kstat_ok) { + if (kstats->ps_shadow[SYS_P_FAN_INDEX] == PS_OK) { + log_printf("PPS OK\n", 0); + } else if (kstats->ps_shadow[SYS_P_FAN_INDEX] == + PS_FAIL) { + log_printf("PPS FAIL\n", 0); + exit_code = 1; + } + } + } + + log_printf("\n", 0); + + + log_printf("System Temperatures (Celsius):\n", 0); + log_printf("------------------------------\n", 0); + log_printf("Brd State Current Min Max Trend\n", 0); + log_printf("--- ------- ------- --- --- -----\n", 0); + + for (i = 0, bksp = &kstats->bd_ksp_list[0]; i < MAX_BOARDS; + i++, bksp++) { + + /* Make sure we have kstats for this board first */ + if (!bksp->temp_kstat_ok) { + continue; + } + log_printf("%2d ", i, 0); + + /* Print the current state of the temperature */ + log_printf("%s", temp_state_msg[bksp->tempstat.state], 0); + /* Set exit code for WARNING and DANGER */ + if (bksp->tempstat.state != 0) + exit_code = 1; + + /* Print the current temperature */ + curr_temp = bksp->tempstat.l1[bksp->tempstat.index % L1_SZ]; + log_printf(" %2d ", curr_temp, 0); + + /* Print the minimum recorded temperature */ + log_printf(" %2d ", bksp->tempstat.min, 0); + + /* Print the maximum recorded temperature */ + log_printf(" %2d ", bksp->tempstat.max, 0); + + /* Print the current trend in temperature (if available) */ + if (bksp->tempstat.version < 2) + log_printf("unknown\n", 0); + else + log_printf("%s\n", temp_trend_msg[bksp->tempstat.trend], 0); + } + if (kstats->temp_kstat_ok) { + log_printf("CLK ", 0); + + /* Print the current state of the temperature */ + log_printf("%s", temp_state_msg[kstats->tempstat.state], 0); + /* Set exit code for WARNING or DANGER */ + if (kstats->tempstat.state != 0) + exit_code = 1; + + /* Print the current temperature */ + curr_temp = kstats->tempstat.l1[kstats->tempstat.index % L1_SZ]; + log_printf(" %2d ", curr_temp, 0); + + /* Print the minimum recorded temperature */ + log_printf(" %2d ", kstats->tempstat.min, 0); + + /* Print the maximum recorded temperature */ + log_printf(" %2d ", kstats->tempstat.max, 0); + + /* Print the current trend in temperature (if available) */ + if (kstats->tempstat.version < 2) + log_printf("unknown\n\n", 0); + else + log_printf("%s\n\n", + temp_trend_msg[kstats->tempstat.trend], 0); + } else { + log_printf("\n"); + } + + log_printf("\n", 0); + log_printf("Power Supplies:\n", 0); + log_printf("---------------\n", 0); + log_printf("Supply Status\n", 0); + log_printf("--------- ------\n", 0); + if (kstats->psstat_kstat_ok) { + for (i = 0; i < SYS_PS_COUNT; i++) { + char *ps, *state; + + /* skip core power supplies that are not present */ + if (i <= SYS_PPS0_INDEX && kstats->ps_shadow[i] == + PS_OUT) + continue; + + /* Display the unit Number */ + switch (i) { + case 0: ps = "0"; break; + case 1: ps = "1"; break; + case 2: ps = "2"; break; + case 3: ps = "3"; break; + case 4: ps = "4"; break; + case 5: ps = "5"; break; + case 6: ps = "6"; break; + case 7: ps = is4slot ? "2nd PPS" : "7"; break; + + case SYS_PPS0_INDEX: ps = "PPS"; break; + case SYS_CLK_33_INDEX: ps = " System 3.3v"; break; + case SYS_CLK_50_INDEX: ps = " System 5.0v"; break; + case SYS_V5_P_INDEX: ps = " Peripheral 5.0v"; break; + case SYS_V12_P_INDEX: ps = " Peripheral 12v"; break; + case SYS_V5_AUX_INDEX: ps = " Auxiliary 5.0v"; break; + case SYS_V5_P_PCH_INDEX: ps = + " Peripheral 5.0v precharge"; + break; + case SYS_V12_P_PCH_INDEX: ps = + " Peripheral 12v precharge"; + break; + case SYS_V3_PCH_INDEX: ps = + " System 3.3v precharge"; break; + case SYS_V5_PCH_INDEX: ps = + " System 5.0v precharge"; break; + + /* skip the peripheral fan here */ + case SYS_P_FAN_INDEX: + continue; + } + + /* what is the state? */ + switch (kstats->ps_shadow[i]) { + case PS_OK: + state = "OK"; + break; + + case PS_FAIL: + state = "FAIL"; + exit_code = 1; + break; + + /* XXX is this an exit_code condition? */ + case PS_OUT: + state = "PPS Out"; + exit_code = 1; + break; + + case PS_UNKNOWN: + state = "Unknown"; + break; + + default: + state = "Illegal State"; + break; + } + + log_printf("%-32s %s\n", ps, state, 0); + } + } + + /* Check status of the system AC Power Source */ + log_printf("%-32s ", "AC Power", 0); + if ((kstats->sysstat2 & SYS_AC_FAIL) == 0) { + log_printf("OK\n", 0); + } else { + log_printf("failed\n", 0); + exit_code = 1; + } + log_printf("\n", 0); + + return (exit_code); +} + + +/* + * Many of the ASICs present in fusion machines have implementation and + * version numbers stored in the OBP device tree. These codes are displayed + * in this routine in an effort to aid Engineering and Field service + * in detecting old ASICs which may have bugs in them. + */ +static void +sunfire_disp_asic_revs(Sys_tree *tree, struct system_kstat_data *kstats) +{ + Board_node *bnode; + Prom_node *pnode; + int isplusbrd; + char *board_str[] = { "Uninitialized", "Unknown", "CPU", + "Memory", "Dual-SBus", "UPA-SBus", + "Dual-PCI", "Disk", "Clock", + "Dual-SBus-SOC+", "UPA-SBus-SOC+"}; + + /* Print the header */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(" HW Revisions ", 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + + /* Else this is a Sunfire or campfire */ + log_printf("ASIC Revisions:\n", 0); + log_printf("---------------\n", 0); + + /* Display Firetruck ASIC Revisions first */ + log_printf("Brd FHC AC SBus0 SBus1 PCI0 PCI1 FEPS", 0); + log_printf(" Board Type Attributes", 0); + log_printf("\n", 0); + log_printf("--- --- -- ----- ----- ---- ---- ----", 0); + log_printf(" ---------- ----------", 0); + log_printf("\n", 0); + + /* + * Display all of the FHC, AC, and chip revisions for the entire + * machine. The AC anf FHC chip revs are available from the device + * tree that was read out of the PROM, but the DC chip revs will be + * read via a kstat. The interfaces for this are not completely + * available at this time. + */ + bnode = tree->bd_list; + while (bnode != NULL) { + int *version; + int upa = bd_to_upa(bnode->board_num); + + /* Display the header with the board number */ + log_printf("%2d ", bnode->board_num, 0); + + /* display the FHC version */ + if ((pnode = dev_find_node(bnode->nodes, "fhc")) == NULL) { + log_printf(" ", 0); + } else { + if ((version = (int *)get_prop_val(find_prop(pnode, + "version#"))) == NULL) { + log_printf(" ", 0); + } else { + log_printf(" %d ", *version, 0); + } + } + + /* display the AC version */ + if ((pnode = dev_find_node(bnode->nodes, "ac")) == NULL) { + log_printf(" ", 0); + } else { + if ((version = (int *)get_prop_val(find_prop(pnode, + "version#"))) == NULL) { + log_printf(" ", 0); + } else { + log_printf(" %d ", *version, 0); + } + } + + /* Find sysio 0 on board and print rev */ + if ((pnode = find_device(bnode, upa, "sbus")) == NULL) { + log_printf(" ", 0); + } else { + if ((version = (int *)get_prop_val(find_prop(pnode, + "version#"))) == NULL) { + log_printf(" ", 0); + } else { + log_printf(" %d ", *version, 0); + } + } + + /* Find sysio 1 on board and print rev */ + if ((pnode = find_device(bnode, upa+1, "sbus")) == NULL) { + log_printf(" ", 0); + } else { + if ((version = (int *)get_prop_val(find_prop(pnode, + "version#"))) == NULL) { + log_printf(" ", 0); + } else { + log_printf(" %d ", *version, 0); + } + } + + /* Find Psycho 0 on board and print rev */ + if ((pnode = find_device(bnode, upa, "pci")) == NULL) { + log_printf(" ", 0); + } else { + if ((version = (int *)get_prop_val(find_prop(pnode, + "version#"))) == NULL) { + log_printf(" ", 0); + } else { + log_printf(" %d ", *version, 0); + } + } + + /* Find Psycho 1 on board and print rev */ + if ((pnode = find_device(bnode, upa+1, "pci")) == NULL) { + log_printf(" ", 0); + } else { + if ((version = (int *)get_prop_val(find_prop(pnode, + "version#"))) == NULL) { + log_printf(" ", 0); + } else { + log_printf(" %d ", *version, 0); + } + } + + /* Find the FEPS on board and print rev */ + if ((pnode = dev_find_node(bnode->nodes, "SUNW,hme")) != NULL) { + if ((version = (int *)get_prop_val(find_prop(pnode, + "hm-rev"))) != NULL) { + if (*version == 0xa0) { + log_printf(" 2.0 ", 0); + } else if (*version == 0x20) { + log_printf(" 2.1 ", 0); + } else { + log_printf(" %2x ", *version, 0); + } + } + } else + log_printf(" ", 0); + + /* print out the board type */ + isplusbrd = ISPLUSBRD(kstats->bd_ksp_list + [bnode->board_num].fhc_bsr); + + log_printf("%-16s", board_str[bnode->board_type], 0); + if (isplusbrd) + log_printf("100MHz Capable", 0); + else + log_printf("84MHz Capable", 0); + + log_printf("\n", 0); + bnode = bnode->next; + } + log_printf("\n", 0); + + /* Now display the FFB board component revisions */ + for (bnode = tree->bd_list; bnode != NULL; bnode = bnode->next) { + display_ffb(bnode, 0); + } +} + +static void +display_hp_boards(struct system_kstat_data *kstats) +{ + int i; + int j; + int hp_found = 0; + struct hp_info *hp; + char *state; + + for (i = 0, hp = &kstats->hp_info[0]; i < MAX_BOARDS; i++, hp++) { + if (!hp->kstat_ok) { + continue; + } + + hp_found = 1; + } + + /* return if there are no hotplug boards in the system. */ + if (!hp_found) { + return; + } + + if (hp_found != 0) { + log_printf("\n", 0); + log_printf("Detached Boards\n", 0); + log_printf("===============\n", 0); + log_printf(" Slot State Type Info\n", 0); + log_printf(" ---- --------- ------ ----" + "-------------------------------------\n", 0); + } + + /* Display all detached boards */ + for (i = 0, hp = &kstats->hp_info[0]; i < MAX_BOARDS; i++, hp++) { + struct cpu_info *cpu; + + if (hp->kstat_ok == 0) { + continue; + } + + + switch (hp->bd_info.state) { + case UNKNOWN_STATE: + state = "unknown"; + break; + + case ACTIVE_STATE: + state = "active"; + break; + + case LOWPOWER_STATE: + state = "low-power"; + break; + + case HOTPLUG_STATE: + state = "hot-plug"; + break; + + case DISABLED_STATE: + state = "disabled"; + break; + + case FAILED_STATE: + state = "failed"; + break; + + default: + state = "unknown"; + break; + } + + log_printf(" %2d %9s ", i, state, 0); + + switch (hp->bd_info.type) { + case MEM_BOARD: + log_printf("%-14s ", MEM_BD_NAME, 0); + break; + + case CPU_BOARD: + log_printf("%-14s ", CPU_BD_NAME, 0); + + /* Cannot display CPU info for disabled boards */ + if ((hp->bd_info.state == DISABLED_STATE) || + (hp->bd_info.state == FAILED_STATE)) { + break; + } + + /* Display both CPUs if present */ + cpu = &hp->bd_info.bd.cpu[0]; + for (j = 0; j < 2; j++, cpu++) { + log_printf("CPU %d: ", j, 0); + /* Print the rated speed of the CPU. */ + if (cpu->cpu_speed > 1) { + log_printf("%3d MHz", cpu->cpu_speed, + 0); + } else { + log_printf("no CPU ", 0); + continue; + } + + /* Display the size of the cache */ + if (cpu->cache_size != 0) { + log_printf(" %0.1fM ", + (float)cpu->cache_size / + (float)(1024*1024), 0); + } else { + log_printf(" ", 0); + } + } + break; + + case IO_2SBUS_BOARD: + log_printf("%-14s ", IO_2SBUS_BD_NAME, 0); + break; + + case IO_2SBUS_SOCPLUS_BOARD: + log_printf("%-14s ", IO_2SBUS_SOCPLUS_BD_NAME, 0); + break; + + case IO_SBUS_FFB_BOARD: + log_printf("%-14s ", IO_SBUS_FFB_BD_NAME, 0); + switch (hp->bd_info.bd.io2.ffb_size) { + case FFB_SINGLE: + log_printf("Single buffered FFB", 0); + break; + + case FFB_DOUBLE: + log_printf("Double buffered FFB", 0); + break; + + case FFB_NOT_FOUND: + log_printf("No FFB installed", 0); + break; + + default: + log_printf("Illegal FFB size", 0); + break; + } + break; + + case IO_SBUS_FFB_SOCPLUS_BOARD: + log_printf("%-14s ", IO_SBUS_FFB_SOCPLUS_BD_NAME, 0); + switch (hp->bd_info.bd.io2.ffb_size) { + case FFB_SINGLE: + log_printf("Single buffered FFB", 0); + break; + + case FFB_DOUBLE: + log_printf("Double buffered FFB", 0); + break; + + case FFB_NOT_FOUND: + log_printf("No FFB installed", 0); + break; + + default: + log_printf("Illegal FFB size", 0); + break; + } + break; + + case IO_PCI_BOARD: + log_printf("%-14s ", IO_PCI_BD_NAME, 0); + break; + + case DISK_BOARD: + log_printf("%-14s ", "disk", 0); + for (j = 0; j < 2; j++) { + log_printf("Disk %d:", j, 0); + if (hp->bd_info.bd.dsk.disk_pres[j]) { + log_printf(" Target: %2d ", + hp->bd_info.bd.dsk.disk_id[j], + 0); + } else { + log_printf(" no disk ", 0); + } + } + break; + + case UNKNOWN_BOARD: + case UNINIT_BOARD: + default: + log_printf("UNKNOWN ", 0); + break; + } + log_printf("\n"); + } +} + +/* + * Analysis functions: + * + * Most of the Fatal error data analyzed from error registers is not + * very complicated. This is because the FRUs for errors detected by + * most parts is either a CPU module, a FFB, or the system board + * itself. + * The analysis of the Address Controller errors is the most complicated. + * These errors can be caused by other boards as well as the local board. + */ + +/* + * analyze_cpu + * + * Analyze the CPU MFSR passed in and determine what type of fatal + * hardware errors occurred at the time of the crash. This function + * returns a pointer to a string to the calling routine. + */ +static int +analyze_cpu(char **msgs, int cpu_id, u_longlong_t afsr) +{ + int count = 0; + int i; + int syndrome; + char msgbuf[MAXSTRLEN]; + + if (msgs == NULL) { + return (count); + } + + if (afsr & P_AFSR_ETP) { + (void) sprintf(msgbuf, "CPU %d Ecache Tag Parity Error, ", + cpu_id); + + /* extract syndrome for afsr */ + syndrome = (afsr & P_AFSR_ETS) >> ETS_SHIFT; + + /* now concat the parity syndrome msg */ + for (i = 0; i < 4; i++) { + if ((0x1 << i) & syndrome) { + (void) strcat(msgbuf, ecache_parity[i]); + } + } + (void) strcat(msgbuf, "\n"); + *msgs++ = strdup(msgbuf); + count++; + } + + if (afsr & P_AFSR_ISAP) { + (void) sprintf(msgbuf, + "CPU %d Incoming System Address Parity Error\n", + cpu_id); + *msgs++ = strdup(msgbuf); + count++; + } + + return (count); +} + +/* + * analyze_ac + * + * This function checks the AC error register passed in and checks + * for any errors that occured during the fatal hardware reset. + */ +static int +analyze_ac(char **msgs, u_longlong_t ac_error) +{ + int i; + int count = 0; + char msgbuf[MAXSTRLEN]; + int tmp_cnt; + + if (msgs == NULL) { + return (count); + } + + for (i = 2; i < MAX_BITS; i++) { + if ((((u_longlong_t)0x1 << i) & ac_error) != 0) { + if (ac_errors[i].error != NULL) { + (void) sprintf(msgbuf, "AC: %s\n", + ac_errors[i].error); + *msgs++ = strdup(msgbuf); + count++; + + /* display the part that might cause this */ + tmp_cnt = disp_parts(msgs, ac_error, i); + count += tmp_cnt; + msgs += tmp_cnt; + } + } + } + + return (count); +} + +/* + * analyze_dc + * + * This routine checks the DC shdow chain and tries to determine + * what type of error might have caused the fatal hardware reset + * error. + */ +static int +analyze_dc(int board, char **msgs, u_longlong_t dc_error) +{ + int i; + int count = 0; + char msgbuf[MAXSTRLEN]; + + if (msgs == NULL) { + return (count); + } + + /* + * The DC scan data is contained in 8 bytes, one byte per + * DC. There are 8 DCs on a system board. + */ + + for (i = 0; i < 8; i++) { + if (dc_error & DC_OVERFLOW) { + (void) sprintf(msgbuf, dc_overflow_txt, board, i); + *msgs++ = strdup(msgbuf); + count++; + } + + if (dc_error & DC_PARITY) { + (void) sprintf(msgbuf, dc_parity_txt, board, i); + *msgs++ = strdup(msgbuf); + count++; + } + dc_error = dc_error >> 8; /* shift over to next byte */ + } + + return (count); +} + +static int +disp_parts(char **msgs, u_longlong_t ac_error, int type) +{ + int count = 0; + int part; + char msgbuf[MAXSTRLEN]; + int i; + + if (msgs == NULL) { + return (count); + } + + (void) sprintf(msgbuf, "\tThe error could be caused by:\n"); + *msgs++ = strdup(msgbuf); + count++; + + for (i = 0; (i < MAX_FRUS) && ac_errors[type].part[i]; i++) { + part = ac_errors[type].part[i]; + + if (part == UPA_PART) { + if (ac_error & UPA_PORT_A) { + part = UPA_A_PART; + } else if (ac_error & UPA_PORT_B) { + part = UPA_B_PART; + } + } + + if (part == DTAG_PART) { + if (ac_error & UPA_PORT_A) { + part = DTAG_A_PART; + } else if (ac_error & UPA_PORT_B) { + part = DTAG_B_PART; + } + } + + (void) sprintf(msgbuf, "\t\t%s\n", part_str[part]); + + *msgs++ = strdup(msgbuf); + count++; + } + + return (count); +} diff --git a/usr/src/lib/libprtdiag_psr/sparc/tazmo/Makefile b/usr/src/lib/libprtdiag_psr/sparc/tazmo/Makefile new file mode 100644 index 0000000000..4035434901 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/tazmo/Makefile @@ -0,0 +1,76 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libprtdiag_psr/sparc/tazmo/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= tazmo.o + +include ../Makefile.com + +IFLAGS += -I$(USR_PLAT_DIR)/sun4u/include -I ../../../libprtdiag/inc +LINTFLAGS += $(IFLAGS) +LDLIBS += -lkstat + +# +# Workgroup Server platform library should install into +# SUNW,Ultra-4. All other desktop platforms can +# link to /usr/platform/SUNW,Ultra-4/lib/libprtdiag_psr.so +# +PLATFORM=SUNW,Ultra-4 + +.KEEP_STATE: + +PLATLIBS= $(USR_PLAT_DIR)/$(PLATFORM)/lib/ + +install: all $(USR_PSM_LIBS) + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/tazmo; $(MAKE) $(USR_PSM_LIB_DIR) + +# +# install rule +# +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +POFILE= libprtdiag_psr_tazmo.po +POFILES= tazmo.po + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext common/tazmo.c` + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po diff --git a/usr/src/lib/libprtdiag_psr/sparc/tazmo/common/tazmo.c b/usr/src/lib/libprtdiag_psr/sparc/tazmo/common/tazmo.c new file mode 100644 index 0000000000..bbeadffea0 --- /dev/null +++ b/usr/src/lib/libprtdiag_psr/sparc/tazmo/common/tazmo.c @@ -0,0 +1,1577 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Tazmo Platform specific functions. + * + * called when : + * machine_type == MTYPE_TAZMO + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +extern int print_flag; + +/* + * these functions will overlay the symbol table of libprtdiag + * at runtime (workgroup server systems only) + */ +int error_check(Sys_tree *tree, struct system_kstat_data *kstats); +void display_memoryconf(Sys_tree *tree, struct grp_info *grps); +int disp_fail_parts(Sys_tree *tree); +void display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats); +void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats); +void display_boardnum(int num); +void display_pci(Board_node *); +void display_io_cards(struct io_card *list); +void display_ffb(Board_node *, int); +void read_platform_kstats(Sys_tree *tree, + struct system_kstat_data *sys_kstat, + struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep); + +/* local functions */ +static int disp_envctrl_status(Sys_tree *, struct system_kstat_data *); +static void check_disk_presence(Sys_tree *, int *, int *, int *); +static void modify_device_path(char *, char *); +static int disk_present(char *); +static void tazjav_disp_asic_revs(Sys_tree *); +static int tazmo_physical_slot(Prom_node *, Prom_node *, int, char *); +static Prom_node *dev_next_node_sibling(Prom_node *root, char *name); + + +int +error_check(Sys_tree *tree, struct system_kstat_data *kstats) +{ + int exit_code = 0; /* init to all OK */ + +#ifdef lint + kstats = kstats; +#endif + /* + * silently check for any types of machine errors + */ + print_flag = 0; + if (disp_fail_parts(tree) || disp_envctrl_status(tree, kstats)) { + /* set exit_code to show failures */ + exit_code = 1; + } + print_flag = 1; + + return (exit_code); +} + +/* Search for and return the node's sibling */ +static Prom_node * +dev_next_node_sibling(Prom_node *root, char *name) +{ + if (root == NULL) + return (NULL); + + /* look at your siblings */ + if (dev_find_node(root->sibling, name) != NULL) + return (root->sibling); + + return (NULL); /* not found */ +} + +/* + * This function displays memory configurations specific to Tazmo/Javelin. + * The PROM device tree is read to obtain this information. + * Some of the information obtained is memory interleave factor, + * DIMM sizes, DIMM socket names. + */ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + Board_node *bnode; + Prom_node *memory; + Prom_node *bank; + Prom_node *dimm; + uint_t *preg; + uint_t interlv; + unsigned long size = 0; + int bank_count = 0; + char *sock_name; + char *status; + Prop *status_prop; + char interleave[8]; + int total_size = 0; +#ifdef lint + grps = grps; +#endif + + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " Memory "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + bnode = tree->bd_list; + memory = dev_find_node(bnode->nodes, "memory"); + preg = (uint_t *)(get_prop_val(find_prop(memory, "interleave"))); + if (preg) { + interlv = preg[4]; + log_printf("Memory Interleave Factor = %d-way\n\n", interlv, 0); + } + log_printf(" Interlv. Socket Size\n", 0); + log_printf("Bank Group Name (MB) Status\n", 0); + log_printf("---- ----- ------ ---- ------\n", 0); + + dimm = bnode->nodes; + for (bank = dev_find_node(bnode->nodes, "bank"); bank != NULL; + bank = dev_next_node(bank, "bank")) { + int bank_size = 0; + uint_t *reg_prop; + + preg = (uint_t *)(get_prop_val( + find_prop(bank, "bank-interleave"))); + + reg_prop = (uint_t *)(get_prop_val( + find_prop(bank, "reg"))); + + /* + * Skip empty banks + */ + if (((reg_prop[2]<<12) + (reg_prop[3]>>20)) == 0) { + bank_count++; + continue; + } + + if (preg) { + interlv = preg[2]; + (void) sprintf(interleave, " %d ", interlv); + bank_size = (preg[0]<<12) + (preg[1]>>20); + } else { + (void) sprintf(interleave, "%s", "none"); + preg = (uint_t *)(get_prop_val(find_prop(bank, "reg"))); + if (preg) { + bank_size = (preg[2]<<12) + (preg[3]>>20); + } + } + for (dimm = dev_find_node(bank, "dimm"); dimm != NULL; + dimm = dev_next_node_sibling(dimm, "dimm")) { + char dimm_status[16]; + + sock_name = (char *)(get_prop_val( + find_prop(dimm, "socket-name"))); + preg = (uint_t *)(get_prop_val(find_prop(dimm, "reg"))); + size = (preg[2]<<12) + (preg[3]>>20); + if ((status_prop = find_prop(dimm, "status")) == NULL) { + (void) sprintf(dimm_status, "%s", "OK"); + } else { + status = (char *)(get_prop_val(status_prop)); + (void) sprintf(dimm_status, "%s", status); + } + log_printf("%3d %5s %6s %4d %6s\n", + bank_count, interleave, sock_name, + size, dimm_status, 0); + } + total_size += bank_size; + bank_count++; + } + log_printf("\n", 0); +} + +/* + * disp_fail_parts + * + * Display the failed parts in the system. This function looks for + * the status property in all PROM nodes. On systems where + * the PROM does not supports passing diagnostic information + * thruogh the device tree, this routine will be silent. + */ +int +disp_fail_parts(Sys_tree *tree) +{ + int exit_code; + int system_failed = 0; + Board_node *bnode = tree->bd_list; + Prom_node *pnode; + char *fru; + char *sock_name; + char slot_str[MAXSTRLEN]; + + exit_code = 0; + + /* go through all of the boards looking for failed units. */ + while (bnode != NULL) { + /* find failed chips */ + pnode = find_failed_node(bnode->nodes); + if ((pnode != NULL) && !system_failed) { + system_failed = 1; + exit_code = 1; + if (print_flag == 0) { + return (exit_code); + } + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, "Failed Field " + "Replaceable Units (FRU) in System:\n"), 0); + log_printf("==========================" + "====================\n", 0); + } + + while (pnode != NULL) { + void *value; + char *name; /* node name string */ + char *type; /* node type string */ + + value = get_prop_val(find_prop(pnode, "status")); + name = get_node_name(pnode); + + /* sanity check of data retreived from PROM */ + if ((value == NULL) || (name == NULL)) { + pnode = next_failed_node(pnode); + continue; + } + + + log_printf(dgettext(TEXT_DOMAIN, "%s unavailable :\n"), + name, 0); + + log_printf(dgettext(TEXT_DOMAIN, + "\tPROM fault string: %s\n"), + value, 0); + + log_printf(dgettext(TEXT_DOMAIN, + "\tFailed Field Replaceable Unit is "), 0); + + /* + * Determine whether FRU is CPU module, system + * board, or SBus card. + */ + if ((name != NULL) && (strstr(name, "sbus"))) { + + log_printf(dgettext(TEXT_DOMAIN, "SBus " + "Card %d\n"), get_sbus_slot(pnode), 0); + + } else if (((name = get_node_name(pnode)) != + NULL) && (strstr(name, "pci"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "system board\n"), 0); + + } else if (((name = get_node_name(pnode)) != + NULL) && (strstr(name, "ffb"))) { + + log_printf(dgettext(TEXT_DOMAIN, + "FFB Card %d\n"), + tazmo_physical_slot( + dev_find_node(bnode->nodes, "slot2dev"), + pnode, -1, slot_str), 0); + + } else if (((name = get_node_name(pnode->parent)) != + NULL) && (strstr(name, "pci"))) { + + (void) tazmo_physical_slot( + NULL, + pnode->parent, + get_pci_device(pnode), + slot_str); + log_printf(dgettext(TEXT_DOMAIN, + "PCI Card in %s\n"), slot_str, 0); + + } else if (((type = get_node_type(pnode)) != NULL) && + (strstr(type, "cpu"))) { + + log_printf( + dgettext(TEXT_DOMAIN, + "UltraSPARC module Module %d\n"), + get_id(pnode)); + + } else if (((type = get_node_type(pnode)) != NULL) && + (strstr(type, "memory-module"))) { + + fru = (char *)(get_prop_val( + find_prop(pnode, "fru"))); + sock_name = (char *)(get_prop_val( + find_prop(pnode, "socket-name"))); + log_printf( + dgettext(TEXT_DOMAIN, + "%s in socket %s\n"), + fru, sock_name, 0); + } + pnode = next_failed_node(pnode); + } + bnode = bnode->next; + } + + if (!system_failed) { + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, + "No failures found in System\n"), 0); + log_printf("===========================\n", 0); + } + + if (system_failed) + return (1); + else + return (0); +} + +void +display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) +{ +#ifdef lint + kstats = kstats; +#endif + /* Display failed units */ + (void) disp_fail_parts(tree); +} + + +void +display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, + struct system_kstat_data *kstats) +{ + /* + * Now display the last powerfail time and the fatal hardware + * reset information. We do this under a couple of conditions. + * First if the user asks for it. The second is iof the user + * told us to do logging, and we found a system failure. + */ + if (flag) { + /* + * display time of latest powerfail. Not all systems + * have this capability. For those that do not, this + * is just a no-op. + */ + disp_powerfail(root); + + (void) disp_envctrl_status(tree, kstats); + + tazjav_disp_asic_revs(tree); + + platform_disp_prom_version(tree); + } + return; + +} + +/* ARGSUSED */ +void +display_boardnum(int num) +{ + log_printf("SYS ", 0); +} + + + +/* + * display_pci + * Display all the PCI IO cards on this board. + */ + +/* ARGSUSED */ +void +display_pci(Board_node *board) +{ + struct io_card *card_list = NULL; + struct io_card card; + void *value; + Prom_node *pci; + Prom_node *card_node; + + if (board == NULL) + return; + + /* Initialize all the common information */ + card.display = 1; + card.board = board->board_num; + (void) sprintf(card.bus_type, "PCI"); + + for (pci = dev_find_node(board->nodes, PCI_NAME); pci != NULL; + pci = dev_next_node(pci, PCI_NAME)) { + char *name; + Prom_node *prev_parent = NULL; + int prev_device = -1; + int pci_pci_bridge = 0; + + /* + * If we have reached a pci-to-pci bridge node, + * we are one level below the 'pci' nodes level + * in the device tree. To get back to that level, + * the search should continue with the sibling of + * the parent or else the remaining 'pci' cards + * will not show up in the output. + */ + if (find_prop(pci, "upa-portid") == NULL) { + if ((pci->parent->sibling != NULL) && + (strcmp(get_prop_val( + find_prop(pci->parent->sibling, + "name")), PCI_NAME) == 0)) + pci = pci->parent->sibling; + else { + pci = pci->parent->sibling; + continue; + } + } + + /* Skip all failed nodes for now */ + if (node_failed(pci)) + continue; + + /* Fill in frequency */ + value = get_prop_val(find_prop(pci, "clock-frequency")); + if (value == NULL) + card.freq = -1; + else + card.freq = ((*(int *)value) + 500000) / 1000000; + + /* Walk through the PSYCHO children */ + card_node = pci->child; + while (card_node != NULL) { + Prop *compat = NULL; + + /* If it doesn't have a name, skip it */ + name = (char *)get_prop_val( + find_prop(card_node, "name")); + if (name == NULL) { + card_node = card_node->sibling; + continue; + } + + /* + * If this is a PCI bridge, then display its + * children. + */ + if (strcmp(name, "pci") == 0) { + card_node = card_node->child; + pci_pci_bridge = 1; + continue; + } + + /* Get the slot number for this card */ + if (pci_pci_bridge) { + card.slot = tazmo_physical_slot( + dev_find_node(board->nodes, "slot2dev"), + pci, + get_pci_to_pci_device( + card_node->parent), + card.slot_str); + } else + card.slot = tazmo_physical_slot( + dev_find_node(board->nodes, + "slot2dev"), + pci, + get_pci_device(card_node), + card.slot_str); + + /* + * Check that duplicate devices are not reported + * on Tazmo. + */ + if ((card_node->parent == prev_parent) && + (get_pci_device(card_node) == prev_device) && + (pci_pci_bridge == 0)) + card.slot = -1; + prev_parent = card_node->parent; + prev_device = get_pci_device(card_node); + + + if (card.slot == -1 || strstr(name, "ebus")) { + card_node = card_node->sibling; + continue; + } + + /* XXX - Don't know how to get status for PCI cards */ + card.status[0] = '\0'; + + /* Get the model of this card */ + value = get_prop_val(find_prop(card_node, "model")); + if (value == NULL) + card.model[0] = '\0'; + else + (void) sprintf(card.model, "%s", + (char *)value); + + /* + * Check if further processing is necessary to display + * this card uniquely. + */ + distinguish_identical_io_cards(name, card_node, &card); + + /* + * If we haven't figured out the frequency yet, + * try and get it from the card. + */ + value = get_prop_val(find_prop(pci, "clock-frequency")); + if (value != NULL && card.freq == -1) + card.freq = ((*(int *)value) + 500000) + / 1000000; + + + value = get_prop_val(find_prop(card_node, + "compatible")); + + /* + * On Tazmo, we would like to print out the last + * string of the "compatible" property if it exists. + * The IEEE 1275 spec. states that this last string + * will be the classcode name. + */ + if (value != NULL) { + char *tval; + int index; + const int always = 1; + + tval = (char *)value; + index = 0; + compat = find_prop(card_node, "compatible"); + while (always) { + if ((strlen(tval) + 1) == + (compat->size - index)) + break; + index += strlen(tval) + 1; + tval += strlen(tval) + 1; + } + value = (void *)tval; + } + + if (value != NULL) + (void) sprintf(card.name, "%s-%s", + (char *)name, (char *)value); + else + (void) sprintf(card.name, "%s", + (char *)name); + + if (card.freq != -1) + card_list = insert_io_card(card_list, &card); + + /* + * If we are done with the children of the pci bridge, + * we must continue with the remaining siblings of + * the pci-to-pci bridge. + */ + if ((card_node->sibling == NULL) && pci_pci_bridge) { + card_node = card_node->parent->sibling; + pci_pci_bridge = 0; + } else + card_node = card_node->sibling; + } + } + + display_io_cards(card_list); + free_io_cards(card_list); +} + + +/* + * Print out all the io cards in the list. Also print the column + * headers if told to do so. + */ +void +display_io_cards(struct io_card *list) +{ + static int banner = 0; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + if (banner == 0) { + log_printf(" Bus Freq\n", 0); + log_printf("Brd Type MHz Slot " + "Name Model", 0); + log_printf("\n", 0); + log_printf("--- ---- ---- ---- " + "-------------------------------- " + "----------------------", 0); + log_printf("\n", 0); + banner = 1; + } + + for (p = list; p != NULL; p = p -> next) { + log_printf("SYS ", p->board, 0); + log_printf("%-4s ", p->bus_type, 0); + log_printf("%3d ", p->freq, 0); + log_printf("%3d ", p->slot, 0); + log_printf("%-32.32s", p->name, 0); + if (strlen(p->name) > 32) + log_printf("+ ", 0); + else + log_printf(" ", 0); + log_printf("%-22.22s", p->model, 0); + if (strlen(p->model) > 22) + log_printf("+", 0); + log_printf("\n", 0); + } +} + +/* + * display_ffb + * Display all FFBs on this board. It can either be in tabular format, + * or a more verbose format. + */ +void +display_ffb(Board_node *board, int table) +{ + Prom_node *ffb; + void *value; + struct io_card *card_list = NULL; + struct io_card card; + + if (board == NULL) + return; + + /* Fill in common information */ + card.display = 1; + card.board = board->board_num; + (void) sprintf(card.bus_type, "UPA"); + card.freq = sys_clk; + + for (ffb = dev_find_node(board->nodes, FFB_NAME); ffb != NULL; + ffb = dev_next_node(ffb, FFB_NAME)) { + if (table == 1) { + /* Print out in table format */ + + /* XXX - Get the slot number (hack) */ + card.slot = tazmo_physical_slot( + dev_find_node(board->nodes, "slot2dev"), + ffb, + -1, + card.slot_str); + + /* Find out if it's single or double buffered */ + (void) sprintf(card.name, "FFB"); + value = get_prop_val(find_prop(ffb, "board_type")); + if (value != NULL) + if ((*(int *)value) & FFB_B_BUFF) + (void) sprintf(card.name, + "FFB, Double Buffered"); + else + (void) sprintf(card.name, + "FFB, Single Buffered"); + + /* Print model number */ + card.model[0] = '\0'; + value = get_prop_val(find_prop(ffb, "model")); + if (value != NULL) + (void) sprintf(card.model, "%s", + (char *)value); + + card_list = insert_io_card(card_list, &card); + } else { + /* print in long format */ + char device[MAXSTRLEN]; + int fd = -1; + struct dirent *direntp; + DIR *dirp; + union strap_un strap; + struct ffb_sys_info fsi; + + /* Find the device node using upa address */ + value = get_prop_val(find_prop(ffb, "upa-portid")); + if (value == NULL) + continue; + + (void) sprintf(device, "%s@%x", FFB_NAME, + *(int *)value); + if ((dirp = opendir("/devices")) == NULL) + continue; + + while ((direntp = readdir(dirp)) != NULL) { + if (strstr(direntp->d_name, device) != NULL) { + (void) sprintf(device, "/devices/%s", + direntp->d_name); + fd = open(device, O_RDWR, 0666); + break; + } + } + (void) closedir(dirp); + + if (fd == -1) + continue; + + if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0) + continue; + + log_printf("FFB Hardware Configuration:\n", 0); + log_printf("-----------------------------------\n", 0); + + strap.ffb_strap_bits = fsi.ffb_strap_bits; + log_printf("\tBoard rev: %d\n", + (int)strap.fld.board_rev, 0); + log_printf("\tFBC version: 0x%x\n", fsi.fbc_version, 0); + log_printf("\tDAC: %s\n", + fmt_manf_id(fsi.dac_version, device), 0); + log_printf("\t3DRAM: %s\n", + fmt_manf_id(fsi.fbram_version, device), 0); + log_printf("\n", 0); + } + } + + display_io_cards(card_list); + free_io_cards(card_list); +} + +/* + * This module does the reading and interpreting of tazmo system + * kstats. These kstats are created by the environ driver: + */ +void +read_platform_kstats(Sys_tree *tree, struct system_kstat_data *sys_kstat, + struct bd_kstat_data *bdp, struct envctrl_kstat_data *ep) +{ + kstat_ctl_t *kc; + kstat_t *ksp; + + if ((kc = kstat_open()) == NULL) { + return; + } +#ifdef lint + tree = tree; + bdp = bdp; +#endif + + ep = &sys_kstat->env_data; + + /* Read the power supply kstats */ + ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0, + ENVCTRL_KSTAT_PSNAME); + + if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) { + (void) memcpy(ep->ps_kstats, ksp->ks_data, + MAX_DEVS * sizeof (envctrl_ps_t)); + } else { + sys_kstat->envctrl_kstat_ok = B_FALSE; + return; + } + + /* Read the fan status kstats */ + ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0, + ENVCTRL_KSTAT_FANSTAT); + + if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) { + (void) memcpy(ep->fan_kstats, ksp->ks_data, + ksp->ks_ndata * sizeof (envctrl_fan_t)); + } else { + sys_kstat->envctrl_kstat_ok = B_FALSE; + return; + } + + /* Read the enclosure kstats */ + ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0, + ENVCTRL_KSTAT_ENCL); + + if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) { + (void) memcpy(ep->encl_kstats, ksp->ks_data, + ksp->ks_ndata * sizeof (envctrl_encl_t)); + } else { + sys_kstat->envctrl_kstat_ok = B_FALSE; + return; + } + + sys_kstat->envctrl_kstat_ok = B_TRUE; +} + +/* + * Walk the PROM device tree and build the system tree and root tree. + * Nodes that have a board number property are placed in the board + * structures for easier processing later. Child nodes are placed + * under their parents. ffb (Fusion Frame Buffer) nodes are handled + * specially, because they do not contain board number properties. + * This was requested from OBP, but was not granted. So this code + * must parse the MID of the FFB to find the board#. + */ +Prom_node * +walk(Sys_tree *tree, Prom_node *root, int id) +{ + register int curnode; + Prom_node *pnode; + char *name; + char *type; + char *model; + int board_node = 0; + + /* allocate a node for this level */ + if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) == + NULL) { + perror("malloc"); + exit(2); /* program errors cause exit 2 */ + } + + /* assign parent Prom_node */ + pnode->parent = root; + pnode->sibling = NULL; + pnode->child = NULL; + + /* read properties for this node */ + dump_node(pnode); + + /* + * Place a node in a 'board' if it has 'board'-ness. The definition + * is that all nodes that are children of root should have a + * board# property. But the PROM tree does not exactly follow + * this. This is where we start hacking. The name 'ffb' can + * change, so watch out for this. + * + * The UltraSPARC, sbus, pci and ffb nodes will exit in + * the desktops and will not have board# properties. These + * cases must be handled here. + * + * PCI to PCI bridges also have the name "pci", but with different + * model property values. They should not be put under 'board'. + */ + name = get_node_name(pnode); + type = get_node_type(pnode); + model = (char *)get_prop_val(find_prop(pnode, "model")); +#ifdef DEBUG + if (name != NULL) + printf("name=%s ", name); + if (type != NULL) + printf("type=%s ", type); + if (model != NULL) + printf("model=%s", model); + printf("\n"); + + if (model == NULL) + model = ""; +#endif + if (type == NULL) + type = ""; + if (name != NULL) { + if (has_board_num(pnode)) { + add_node(tree, pnode); + board_node = 1; +#ifdef DEBUG + printf("ADDED BOARD name=%s type=%s model=%s\n", + name, type, model); +#endif + } else if ((strcmp(name, FFB_NAME) == 0) || + (strcmp(type, "cpu") == 0) || + + ((strcmp(name, "pci") == 0) && (model != NULL) && + (strcmp(model, "SUNW,psycho") == 0)) || + + ((strcmp(name, "pci") == 0) && (model != NULL) && + (strcmp(model, "SUNW,sabre") == 0)) || + + (strcmp(name, "counter-timer") == 0) || + (strcmp(name, "sbus") == 0) || + (strcmp(name, "memory") == 0) || + (strcmp(name, "mc") == 0) || + (strcmp(name, "associations") == 0)) { + add_node(tree, pnode); + board_node = 1; +#ifdef DEBUG + printf("ADDED BOARD name=%s type=%s model=%s\n", + name, type, model); +#endif + } +#ifdef DEBUG + else + printf("node not added: name=%s type=%s\n", name, type); +#endif + } + + if (curnode = child(id)) { + pnode->child = walk(tree, pnode, curnode); + } + + if (curnode = next(id)) { + if (board_node) { + return (walk(tree, root, curnode)); + } else { + pnode->sibling = walk(tree, root, curnode); + } + } + + if (board_node) { + return (NULL); + } else { + return (pnode); + } +} + +/* + * local functions + */ + +/* + * disp_envctrl_status + * + * This routine displays the environmental status passed up from + * device drivers via kstats. The kstat names are defined in + * kernel header files included by this module. + */ +static int +disp_envctrl_status(Sys_tree *tree, struct system_kstat_data *sys_kstats) +{ + int exit_code = 0; + int possible_failure; + int i; + uchar_t val; + char fan_type[16]; + char state[48]; + char name[16]; + envctrl_ps_t ps; + envctrl_fan_t fan; + envctrl_encl_t encl; + struct envctrl_kstat_data *ep; + uchar_t fsp_value; + int i4slot_backplane_value = -1; + int i8slot_backplane_value = -1; + int j8slot_backplane_value = -1; + static int first_8disk_bp = 0; + static int second_8disk_bp = 0; + static int first_4disk_bp = 0; + + if (sys_kstats->envctrl_kstat_ok == 0) { + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, "Environmental information " + "is not available\n"), 0); + log_printf(dgettext(TEXT_DOMAIN, "Environmental driver may " + "not be installed\n"), 0); + log_printf("\n", 0); + return (1); + } + + ep = &sys_kstats->env_data; + + check_disk_presence(tree, &first_4disk_bp, &first_8disk_bp, + &second_8disk_bp); + + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " Environmental Status "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + + log_printf("System Temperatures (Celsius):\n", 0); + log_printf("------------------------------\n", 0); + for (i = 0; i < MAX_DEVS; i++) { + encl = ep->encl_kstats[i]; + switch (encl.type) { + case ENVCTRL_ENCL_AMBTEMPR: + if (encl.instance == I2C_NODEV) + continue; + (void) sprintf(name, "%s", "AMBIENT"); + log_printf("%s %d", name, encl.value); + if (encl.value > MAX_AMB_TEMP) { + log_printf(" WARNING\n", 0); + exit_code = 1; + } else + log_printf("\n", 0); + break; + case ENVCTRL_ENCL_CPUTEMPR: + if (encl.instance == I2C_NODEV) + continue; + (void) sprintf(name, "%s %d", "CPU", encl.instance); + log_printf("%s %d", name, encl.value); + if (encl.value > MAX_CPU_TEMP) { + log_printf(" WARNING\n", 0); + exit_code = 1; + } else + log_printf("\n", 0); + break; + case ENVCTRL_ENCL_FSP: + if (encl.instance == I2C_NODEV) + continue; + val = encl.value & ENVCTRL_FSP_KEYMASK; + fsp_value = encl.value; + switch (val) { + case ENVCTRL_FSP_KEYOFF: + (void) sprintf(state, "%s", "Off"); + break; + case ENVCTRL_FSP_KEYON: + (void) sprintf(state, "%s", "On"); + break; + case ENVCTRL_FSP_KEYDIAG: + (void) sprintf(state, "%s", "Diagnostic"); + break; + case ENVCTRL_FSP_KEYLOCKED: + (void) sprintf(state, "%s", "Secure"); + break; + default: + (void) sprintf(state, "%s", "Broken!"); + exit_code = 1; + break; + } + break; + case ENVCTRL_ENCL_BACKPLANE4: + case ENVCTRL_ENCL_BACKPLANE8: + if (encl.instance == I2C_NODEV) + continue; + switch (encl.instance) { + case 0: + i4slot_backplane_value = + encl.value & ENVCTRL_4SLOT_BACKPLANE; + break; + case 1: + i8slot_backplane_value = + encl.value & ENVCTRL_8SLOT_BACKPLANE; + break; + case 2: + j8slot_backplane_value = + encl.value & ENVCTRL_8SLOT_BACKPLANE; + break; + } + default: + break; + } + } + + log_printf("=================================\n\n", 0); + log_printf("Front Status Panel:\n", 0); + log_printf("-------------------\n", 0); + log_printf("Keyswitch position is in %s mode.\n", state); + log_printf("\n", 0); + val = fsp_value & (ENVCTRL_FSP_DISK_ERR | ENVCTRL_FSP_PS_ERR | + ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR | + ENVCTRL_FSP_ACTIVE); + log_printf("System LED Status: POWER GENERAL ERROR " + " ACTIVITY\n", 0); + log_printf(" [ ON] [%3s] " + " [%3s]\n", val & ENVCTRL_FSP_GEN_ERR ? "ON" : "OFF", + val & ENVCTRL_FSP_ACTIVE ? "ON" : "OFF"); + log_printf(" DISK ERROR " + "THERMAL ERROR POWER SUPPLY ERROR\n", 0); + log_printf(" [%3s] [%3s] " + " [%3s]\n", val & ENVCTRL_FSP_DISK_ERR ? "ON" : "OFF", + val & ENVCTRL_FSP_TEMP_ERR ? "ON" : "OFF", + val & ENVCTRL_FSP_PS_ERR ? "ON" : "OFF"); + log_printf("\n", 0); + /* record error conditions */ + if (val & (ENVCTRL_FSP_GEN_ERR | ENVCTRL_FSP_DISK_ERR | + ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_PS_ERR)) { + exit_code = 1; + } + + + log_printf("Disk LED Status: OK = GREEN ERROR = YELLOW\n", 0); + + if (j8slot_backplane_value != -1) { + log_printf(" DISK 18: %7s DISK 19: %7s\n", + second_8disk_bp & ENVCTRL_DISK_6 ? + j8slot_backplane_value & ENVCTRL_DISK_6 ? "[ERROR]" : "[OK]" + : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_7 ? + j8slot_backplane_value & ENVCTRL_DISK_7 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + log_printf(" DISK 16: %7s DISK 17: %7s\n", + second_8disk_bp & ENVCTRL_DISK_4 ? + j8slot_backplane_value & ENVCTRL_DISK_4 ? "[ERROR]" : "[OK]" + : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_5 ? + j8slot_backplane_value & ENVCTRL_DISK_5 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + log_printf(" DISK 14: %7s DISK 15: %7s\n", + second_8disk_bp & ENVCTRL_DISK_2 ? + j8slot_backplane_value & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]" + : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_3 ? + j8slot_backplane_value & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + log_printf(" DISK 12: %7s DISK 13: %7s\n", + second_8disk_bp & ENVCTRL_DISK_0 ? + j8slot_backplane_value & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]" + : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_1 ? + j8slot_backplane_value & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + } + if (i8slot_backplane_value != -1) { + log_printf(" DISK 10: %7s DISK 11: %7s\n", + first_8disk_bp & ENVCTRL_DISK_6 ? + i8slot_backplane_value & ENVCTRL_DISK_6 ? "[ERROR]" : "[OK]" + : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_7 ? + i8slot_backplane_value & ENVCTRL_DISK_7 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + log_printf(" DISK 8: %7s DISK 9: %7s\n", + first_8disk_bp & ENVCTRL_DISK_4 ? + i8slot_backplane_value & ENVCTRL_DISK_4 ? "[ERROR]" : "[OK]" + : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_5 ? + i8slot_backplane_value & ENVCTRL_DISK_5 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + log_printf(" DISK 6: %7s DISK 7: %7s\n", + first_8disk_bp & ENVCTRL_DISK_2 ? + i8slot_backplane_value & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]" + : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_3 ? + i8slot_backplane_value & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + log_printf(" DISK 4: %7s DISK 5: %7s\n", + first_8disk_bp & ENVCTRL_DISK_0 ? + i8slot_backplane_value & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]" + : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_1 ? + i8slot_backplane_value & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + } + if (i4slot_backplane_value != -1) { + log_printf(" DISK 2: %7s DISK 3: %7s\n", + first_4disk_bp & ENVCTRL_DISK_2 ? + i4slot_backplane_value & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]" + : "[EMPTY]", first_4disk_bp & ENVCTRL_DISK_3 ? + i4slot_backplane_value & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + log_printf(" DISK 0: %7s DISK 1: %7s\n", + first_4disk_bp & ENVCTRL_DISK_0 ? + i4slot_backplane_value & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]" + : "[EMPTY]", first_4disk_bp & ENVCTRL_DISK_1 ? + i4slot_backplane_value & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]" + : "[EMPTY]"); + } + + log_printf("=================================\n", 0); + log_printf("\n", 0); + + log_printf("Fans:\n", 0); + log_printf("-----\n", 0); + + log_printf("Fan Bank Speed Status\n", 0); + log_printf("-------- ----- ------\n", 0); + + for (i = 0; i < MAX_DEVS; i++) { + fan = ep->fan_kstats[i]; + + if (fan.instance == I2C_NODEV) + continue; + + switch (fan.type) { + case ENVCTRL_FAN_TYPE_CPU: + (void) sprintf(fan_type, "%s", "CPU"); + break; + case ENVCTRL_FAN_TYPE_PS: + (void) sprintf(fan_type, "%s", "PWR"); + break; + case ENVCTRL_FAN_TYPE_AFB: + (void) sprintf(fan_type, "%s", "AFB"); + break; + } + if (fan.fans_ok == B_TRUE) { + (void) sprintf(state, "%s", " OK "); + } else { + (void) sprintf(state, "FAILED (FAN# %d)", + fan.fanflt_num); + /* we know fan.instance != I2C_NODEV */ + exit_code = 1; + } + if (fan.instance != I2C_NODEV) + log_printf("%s %d %s\n", fan_type, + fan.fanspeed, state); + } + + + log_printf("\n", 0); + + log_printf("\n", 0); + log_printf("Power Supplies:\n", 0); + log_printf("---------------\n", 0); + log_printf("Supply Rating Temp Status\n", 0); + log_printf("------ ------ ---- ------\n", 0); + for (i = 0; i < MAX_DEVS; i++) { + ps = ep->ps_kstats[i]; + if (ps.curr_share_ok == B_TRUE && + ps.limit_ok == B_TRUE && ps.ps_ok == B_TRUE) { + (void) sprintf(state, "%s", " OK "); + possible_failure = 0; + } else { + if (ps.ps_ok != B_TRUE) { + (void) sprintf(state, "%s", + "FAILED: DC Power Failure"); + possible_failure = 1; + } else if (ps.curr_share_ok != B_TRUE) { + (void) sprintf(state, "%s", + "WARNING: Current Share Imbalance"); + possible_failure = 1; + } else if (ps.limit_ok != B_TRUE) { + (void) sprintf(state, "%s", + "WARNING: Current Overload"); + possible_failure = 1; + } + } + + if (ps.instance != I2C_NODEV && ps.ps_rating != 0) { + log_printf(" %2d %4d W %2d %s\n", + ps.instance, ps.ps_rating, ps.ps_tempr, state); + if (possible_failure) + exit_code = 1; + } + } + + return (exit_code); +} + +/* + * This function will return a bitmask for each of the 4 disk backplane + * and the two 8 disk backplanes. It creates this mask by first obtaining + * the PROM path of the controller for each slot using the "slot2dev" + * node in the PROM tree. It then modifies the PROM path to obtain a + * physical device path to the controller. The presence of the controller + * is determined by trying to open the controller device and reading + * some information from the device. Currently only supported on Tazmo. + */ +static void +check_disk_presence(Sys_tree *tree, int *i4disk, int *i8disk, int *j8disk) +{ + Board_node *bnode; + Prom_node *slot2disk = NULL; + Prop *slotprop; + char *devpath_p; + char devpath[MAXSTRLEN]; + char slotx[16] = ""; + int slot; + int slot_ptr = 0; + + bnode = tree->bd_list; + *i4disk = *i8disk, *j8disk = 0; + + slot2disk = dev_find_node(bnode->nodes, "slot2disk"); + + for (slot = 0; slot < 20; slot++) { + (void) sprintf(slotx, "slot#%d", slot); + if ((slotprop = find_prop(slot2disk, slotx)) != NULL) + if ((devpath_p = (char *)(get_prop_val(slotprop))) + != NULL) { + modify_device_path(devpath_p, devpath); + if (disk_present(devpath)) { + if (slot < 4) + *i4disk |= 1 << slot_ptr; + else if (slot < 12) + *i8disk |= 1 << slot_ptr; + else if (slot < 20) + *j8disk |= 1 << slot_ptr; + } + } + if ((slot == 3) || (slot == 11)) + slot_ptr = 0; + else + slot_ptr++; + } +} + + + +/* + * modify_device_path + * + * This function modifies a string from the slot2disk association + * PROM node to a physical device path name. For example if the + * slot2disk association value is "/pci@1f,4000/scsi@3/disk@1", + * the equivalent physical device path will be + * "/devices/pci@1f,4000/scsi@3/sd@1,0:c,raw". + * We use this path to attempt to probe the disk to check for its + * presence in the enclosure. We access the 'c' partition + * which represents the entire disk. + */ +static void +modify_device_path(char *oldpath, char *newpath) +{ + char *changeptr; + long target; + char targetstr[16]; + + (void) strcpy(newpath, "/devices"); + changeptr = strstr(oldpath, "disk@"); + /* + * The assumption here is that nothing but the + * target id follows the disk@ substring. + */ + target = strtol(changeptr+5, NULL, 16); + (void) strncat(newpath, oldpath, changeptr - oldpath); + (void) sprintf(targetstr, "sd@%ld,0:c,raw", target); + (void) strcat(newpath, targetstr); +} + +/* + * Returns 0 if the device at devpath is not *physically* present. If it is, + * then info on that device is placed in the dkinfop buffer, and 1 is returned. + * Keep in mind that ioctl(DKIOCINFO)'s CDROMs owned by vold fail, so only + * the dki_ctype field is set in that case. + */ +static int +disk_present(char *devpath) +{ + int search_file; + struct stat stbuf; + struct dk_cinfo dkinfo; + + /* + * Attempt to open the disk. If it fails, skip it. + */ + if ((search_file = open(devpath, O_RDONLY | O_NDELAY)) < 0) + return (0); + + /* + * Must be a character device + */ + if (fstat(search_file, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) { + (void) close(search_file); + return (0); + } + + /* + * Attempt to read the configuration info on the disk. + * If it fails, we assume the disk's not there. + * Note we must close the file for the disk before we + * continue. + */ + if (ioctl(search_file, DKIOCINFO, &dkinfo) < 0) { + (void) close(search_file); + return (0); + } + (void) close(search_file); + return (1); +} + +void +tazjav_disp_asic_revs(Sys_tree *tree) +{ + Board_node *bnode; + Prom_node *pnode; + char *name; + int *version; + char *model; + + /* Print the header */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " HW Revisions "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + + bnode = tree->bd_list; + + log_printf("ASIC Revisions:\n", 0); + log_printf("---------------\n", 0); + + /* Find sysio and print rev */ + for (pnode = dev_find_node(bnode->nodes, "sbus"); pnode != NULL; + pnode = dev_next_node(pnode, "sbus")) { + version = (int *)get_prop_val(find_prop(pnode, "version#")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) { + log_printf("SBus: %s Rev %d\n", + name, *version, 0); + } + } + + /* Find Psycho and print rev */ + for (pnode = dev_find_node(bnode->nodes, "pci"); pnode != NULL; + pnode = dev_next_node(pnode, "pci")) { + Prom_node *parsib = pnode->parent->sibling; + + if (find_prop(pnode, "upa-portid") == NULL) { + if ((parsib != NULL) && + (strcmp(get_prop_val( + find_prop(parsib, "name")), + PCI_NAME) == 0)) + pnode = parsib; + else { + pnode = parsib; + continue; + } + } + + version = (int *)get_prop_val(find_prop(pnode, "version#")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) + if (get_pci_bus(pnode) == 0) + log_printf("STP2223BGA: Rev %d\n", *version, 0); + } + + /* Find Cheerio and print rev */ + for (pnode = dev_find_node(bnode->nodes, "ebus"); pnode != NULL; + pnode = dev_next_node(pnode, "ebus")) { + version = (int *)get_prop_val(find_prop(pnode, "revision-id")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) + log_printf("STP2003QFP: Rev %d\n", *version, 0); + } + + /* Find System Controller and print rev */ + for (pnode = dev_find_node(bnode->nodes, "sc"); pnode != NULL; + pnode = dev_next_node(pnode, "sc")) { + version = (int *)get_prop_val(find_prop(pnode, "version#")); + model = (char *)get_prop_val(find_prop(pnode, "model")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) { + if ((strcmp(model, "SUNW,sc-marvin") == 0)) + log_printf("STP2205BGA: Rev %d\n", *version, 0); + } + } + + /* Find the FEPS and print rev */ + for (pnode = dev_find_node(bnode->nodes, "SUNW,hme"); pnode != NULL; + pnode = dev_next_node(pnode, "SUNW,hme")) { + version = (int *)get_prop_val(find_prop(pnode, "hm-rev")); + name = get_prop_val(find_prop(pnode, "name")); + + if ((version != NULL) && (name != NULL)) { + log_printf("FEPS: %s Rev ", name); + if (*version == 0xa0) { + log_printf("2.0\n", 0); + } else if (*version == 0x20) { + log_printf("2.1\n", 0); + } else { + log_printf("%x\n", *version, 0); + } + } + } + log_printf("\n", 0); + + if (dev_find_node(bnode->nodes, FFB_NAME) != NULL) { + display_ffb(bnode, 0); + } +} + + +/* + * Determine the physical PCI slot based on which Psycho is the parent + * of the PCI card. + */ +static int +tazmo_physical_slot(Prom_node *slotd, Prom_node *parent, int device, char *str) +{ + int *upa_id = NULL; + int *reg = NULL; + int offset; + char controller[MAXSTRLEN]; + char *name; + Prop *prop; + char *devpath_p; + char slotx[16] = ""; + int *slot_names_mask; + char *slot_names; + int shift = 0; + int slot; + int slots, start_slot; + + /* + * If slotd != NULL, then we must return the physical PCI slot + * number based on the information in the slot2dev associations + * node. This routine is called from display_pci() with slotd + * != NULL. If so, we return without obtaining the slot name. + * If slotd == NULL, we look for the slot name through the + * slot-names property in the bus node. + */ + + if (slotd != NULL) { + (void) strcpy(str, ""); + if ((prop = find_prop(parent, "upa-portid")) != NULL) + upa_id = (int *)(get_prop_val(prop)); + if ((prop = find_prop(parent, "reg")) != NULL) + reg = (int *)(get_prop_val(prop)); + if ((prop = find_prop(parent, "name")) != NULL) + name = (char *)(get_prop_val(prop)); + if ((upa_id == NULL) || (reg == NULL)) { + return (-1); + } + offset = reg[1]; + if (strcmp(name, "pci") == 0) { + (void) sprintf(controller, "/pci@%x,%x/*@%x,*", + *upa_id, offset, device); + slots = 20; + } else if (strcmp(name, "SUNW,ffb") == 0) { + (void) sprintf(controller, "/*@%x,0", *upa_id); + slots = 2; + } + + start_slot = 1; + for (slot = start_slot; slot <= slots; slot++) { + if (strcmp(name, "pci") == 0) + (void) sprintf(slotx, "pci-slot#%d", slot); + else if (strcmp(name, "SUNW,ffb") == 0) + (void) sprintf(slotx, "graphics#%d", slot); + if ((prop = find_prop(slotd, slotx)) != NULL) + if ((devpath_p = (char *)(get_prop_val + (prop))) != NULL) + if (strcmp(devpath_p, controller) == + NULL) + return (slot); + } + return (-1); + } + + /* + * Get slot-names property from parent node. + * This property consists of a 32 bit mask indicating which + * devices are relevant to this bus node. Following are a + * number of strings depending on how many bits are set in the + * bit mask; the first string gives the label that is printed + * on the chassis for the smallest device number, and so on. + */ + + prop = find_prop(parent, "slot-names"); + if (prop == NULL) { + (void) strcpy(str, ""); + return (-1); + } + slot_names_mask = (int *)(get_prop_val(prop)); + slot_names = (char *)slot_names_mask; + + slot = 1; + slot_names += 4; /* Skip the 4 byte bitmask */ + + while (shift < 32) { + /* + * Shift through the bitmask looking to see if the + * bit corresponding to "device" is set. If so, copy + * the correcsponding string to the provided pointer. + */ + if (*slot_names_mask & slot) { + if (shift == device) { + (void) strcpy(str, slot_names); + return (0); + } + slot_names += strlen(slot_names)+1; + } + shift++; + slot = slot << 1; + } + return (-1); +} diff --git a/usr/src/lib/librsc/Makefile b/usr/src/lib/librsc/Makefile new file mode 100644 index 0000000000..52b7a6ffec --- /dev/null +++ b/usr/src/lib/librsc/Makefile @@ -0,0 +1,45 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/librsc/Makefile + +SUBDIRS= $(MACH) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all clean clobber install lint : $(SUBDIRS) + +$(MACH): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/librsc/Makefile.com b/usr/src/lib/librsc/Makefile.com new file mode 100644 index 0000000000..f5f08298fd --- /dev/null +++ b/usr/src/lib/librsc/Makefile.com @@ -0,0 +1,97 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/librsc/Makefile.com +# + +LIBRARY= librsc.a +VERS= .1 + +# PLATFORM_OBJECTS is defined in platform Makefile +OBJECTS= $(PLATFORM_OBJECTS) + +include $(SRC)/lib/Makefile.lib +include $(SRC)/Makefile.psm + +CPPFLAGS += $(PLATINCS) + +LINKED_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%) +LINKED_LIB_DIRS = $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib) +LINKED_LIBRSC_DIR = \ + $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib/librsc.so) +LINKED_LIBRSC1_DIR = \ + $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib/librsc.so.1) +LINKED_LLIBLRSC_DIR = \ + $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/lib/llib-lrsc.ln) + +SRCDIR = common +LIBS = $(DYNLIB) $(LINTLIB) +CFLAGS += $(CCVERBOSE) +LDLIBS += -lc +PLATLIBS = $(USR_PLAT_DIR)/$(PLATFORM)/lib/ +INS.slink6= $(RM) -r $@; $(SYMLINK) ../../$(PLATFORM)/lib/librsc.so.1 $@ $(CHOWNLINK) $(CHGRPLINK) +INS.slink7= $(RM) -r $@; $(SYMLINK) ../../$(PLATFORM)/lib/librsc.so $@ $(CHOWNLINK) $(CHGRPLINK) +INS.slink8= $(RM) -r $@; $(SYMLINK) ../../$(PLATFORM)/lib/llib-lrsc.ln $@ $(CHOWNLINK) $(CHGRPLINK) + +.KEEP_STATE: + +# +# build/lint rules +# +all: $(LIBS) +lint: lintcheck + +# +# install rules +# +$(PLATLIBS)/librsc.so: + $(RM) -r $@; $(SYMLINK) librsc.so.1 $@ $(CHOWNLINK) $(CHGRPLINK) + +install: all $(USR_PSM_LIBS) $(PLATLIBS)/librsc.so \ + $(LINKED_DIRS) $(LINKED_LIB_DIRS) \ + $(LINKED_LIBRSC_DIR) $(LINKED_LIBRSC1_DIR) \ + $(LINKED_LLIBLRSC_DIR) + +$(USR_PSM_LIB_DIR)/%: % $(USR_PSM_LIB_DIR) + $(INS.file) + +$(LINKED_DIRS): $(USR_PLAT_DIR) + -$(INS.dir.root.sys) + +$(LINKED_LIB_DIRS): $(LINKED_DIRS) + -$(INS.dir.root.sys) + +$(LINKED_LIBRSC_DIR): $(USR_PLAT_DIR) + -$(INS.slink7) + +$(LINKED_LIBRSC1_DIR): $(USR_PLAT_DIR) + -$(INS.slink6) + +$(LINKED_LLIBLRSC_DIR): $(USR_PLAT_DIR) + -$(INS.slink8) + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/librsc/sparc/Makefile b/usr/src/lib/librsc/sparc/Makefile new file mode 100644 index 0000000000..a971eee1d6 --- /dev/null +++ b/usr/src/lib/librsc/sparc/Makefile @@ -0,0 +1,44 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/librsc/sparc/Makefile +# + +SUBDIRS = mpxu + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +all clean clobber install lint : $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/librsc/sparc/Makefile.com b/usr/src/lib/librsc/sparc/Makefile.com new file mode 100644 index 0000000000..bf0be53755 --- /dev/null +++ b/usr/src/lib/librsc/sparc/Makefile.com @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/librsc/sparc/Makefile.com +# + +PLATINCS += -I$(USR_PLAT_DIR)/sun4u/include -I$(UTSBASE)/sun4u + +include ../../Makefile.com diff --git a/usr/src/lib/librsc/sparc/mpxu/Makefile b/usr/src/lib/librsc/sparc/mpxu/Makefile new file mode 100644 index 0000000000..9860f91dd3 --- /dev/null +++ b/usr/src/lib/librsc/sparc/mpxu/Makefile @@ -0,0 +1,45 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/librsc/sparc/mpxu/Makefile + +UTSBASE = ../../../../uts + +PLATFORM_OBJECTS= librsc.o + +# +# platform library directory (/usr/platform/SUNW,Sun-Fire-V240/lib) +# +PLATFORM=SUNW,Sun-Fire-V240 +LINKED_PLATFORMS = SUNW,Sun-Fire-V250 +LINKED_PLATFORMS += SUNW,Sun-Fire-V440 +LINKED_PLATFORMS += SUNW,Sun-Fire-V445 +LINKED_PLATFORMS += SUNW,Sun-Fire-V215 + +include ../Makefile.com + +$(USR_PSM_LIB_DIR): + cd $(UTSBASE)/sun4u/mpxu; pwd; $(MAKE) $(USR_PSM_LIB_DIR) diff --git a/usr/src/lib/librsc/sparc/mpxu/common/librsc.c b/usr/src/lib/librsc/sparc/mpxu/common/librsc.c new file mode 100644 index 0000000000..863c304fcc --- /dev/null +++ b/usr/src/lib/librsc/sparc/mpxu/common/librsc.c @@ -0,0 +1,711 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * ENXS platform-specific functions + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "librsc.h" + +/* rmcadm driver file descriptor */ +static int rsc_fd = -1; + +/* + * librsc receive buffer - it is used as temporary buffer to store replies + * from the remote side + */ + +static uchar_t rsc_rx_buffer[RSC_MAX_RX_BUFFER]; +static int rsc_rx_resp_len = 0; +static int rsc_rx_error = 0; +static rsci8 rsc_rx_resp_type = 0; + +/* + * Registered boot-protocol message callback routine. This routine will be + * called whenever a boot protocol message is received. + */ +static rscp_bpmsg_cb_t *bpmsg_cb; + + + +/* lookup table to match request and response . This is in order to support */ +/* obsolete functions (rscp_send, rscp_recv) */ + +static req_resp_table_t rr_table[] = { + + { DP_GET_DATE_TIME, DP_GET_DATE_TIME_R, + sizeof (dp_get_date_time_r_t), RR_TIMEOUT }, + { DP_SET_DATE_TIME, DP_SET_DATE_TIME_R, + sizeof (dp_set_date_time_r_t), RR_TIMEOUT }, + { DP_GET_EVENT_LOG, DP_GET_EVENT_LOG_R, + sizeof (dp_get_event_log_r_t), RR_TIMEOUT }, + { DP_MODEM_CONNECT, DP_MODEM_CONNECT_R, + sizeof (dp_modem_connect_r_t), RR_TIMEOUT }, + { DP_MODEM_DISCONNECT, DP_MODEM_DISCONNECT_R, + sizeof (dp_modem_disconnect_r_t), RR_TIMEOUT }, + { DP_SEND_ALERT, DP_SEND_ALERT_R, + sizeof (dp_send_alert_r_t), RR_TIMEOUT }, + { DP_SET_CFGVAR, DP_SET_CFGVAR_R, + sizeof (dp_set_cfgvar_r_t), RR_TIMEOUT }, + { DP_GET_CFGVAR, DP_GET_CFGVAR_R, + sizeof (dp_get_cfgvar_r_t), RR_TIMEOUT }, + { DP_GET_CFGVAR_NAME, DP_GET_CFGVAR_NAME_R, + sizeof (dp_get_cfgvar_name_r_t), RR_TIMEOUT }, + { DP_GET_NETWORK_CFG, DP_GET_NETWORK_CFG_R, + sizeof (dp_get_network_cfg_r_t), RR_TIMEOUT }, + { DP_RSC_STATUS, DP_RSC_STATUS_R, + sizeof (dp_rsc_status_r_t), RR_TIMEOUT }, + { DP_USER_ADM, DP_USER_ADM_R, + sizeof (dp_user_adm_r_t), RR_SEPROM_TIMEOUT}, + { DP_RESET_RSC, DP_NULL_MSG, + 0, 1 }, + { DP_GET_CONSOLE_LOG, DP_GET_CONSOLE_LOG_R, + sizeof (dp_get_console_log_r_t), RR_TIMEOUT }, + { DP_GET_CONFIG_LOG, DP_GET_CONFIG_LOG_R, + sizeof (dp_get_config_log_r_t), RR_TIMEOUT }, + { DP_GET_EVENT_LOG2, DP_GET_EVENT_LOG2_R, + sizeof (dp_get_event_log2_r_t), RR_TIMEOUT }, +}; + +static const int rr_table_cnt = sizeof (rr_table) / sizeof (rr_table[0]); + + +/* lookup table to get timeout value for BP cmd reply. This is in order to */ +/* support obsolete functions (rscp_send_bpmsg, rsc_raw_write) */ + +static req_resp_table_t rr_bp_table[] = { + + { BP_OBP_BOOTINIT, NULL, sizeof (bp_msg_t), + RR_BOOT_INIT_TIMEOUT }, + { BP_OBP_RESET, NULL, sizeof (bp_msg_t), + RR_BOOT_RESET_TIMEOUT } +}; + +static const int rr_bp_table_cnt = + sizeof (rr_bp_table) / sizeof (rr_bp_table[0]); + +static rsci8 unsupported_cmds[] = { DP_SET_DATE_TIME }; + +static int unsupported_cmds_cnt = sizeof (unsupported_cmds) / + sizeof (unsupported_cmds[0]); + +/* + * Protocol version number, used to determine whether ALOM will + * time out on unknown commands. + */ +static int sdp_version = -1; + +/* function prototypes */ + +static req_resp_table_t *rsc_lookup_rr_table(req_resp_table_t *, int, rsci8); + +static int rsc_check_unsupported_cmd(rsci8); + +static int rsc_cmd_response_guaranteed(rsci8); + +/* + * Initialize the generic librsc data protocol routines. basically, it + * open the rmcadm (pseudo) device and initialize data + */ +int +rscp_init(void) +{ + rscp_msg_t request, response; + dp_get_sdp_version_r_t version_msg; + + /* + * 'erase' the rx buffer + */ + (void) memset(rsc_rx_buffer, 0, sizeof (RSC_MAX_RX_BUFFER)); + rsc_rx_resp_len = 0; + rsc_rx_error = 0; + rsc_rx_resp_type = DP_NULL_MSG; + + /* + * open rmcadm driver + */ + if ((rsc_fd = open(RSC_RMCADM_DRV, O_RDWR)) < 0) { +#ifdef DEBUG + printf("rscp_init: Error opening %s, error code = %d\n", + RSC_RMCADM_DRV, errno); +#endif + return (errno); + } + + /* + * Fetch the protocol version number in use between the host + * and ALOM. + */ + request.type = DP_GET_SDP_VERSION; + request.len = 0; + request.data = 0; + + response.type = DP_GET_SDP_VERSION_R; + response.len = sizeof (version_msg); + response.data = (caddr_t)&version_msg; + + if ((errno = rscp_send_recv(&request, &response, 0)) != 0) + return (errno); + + sdp_version = version_msg.version; + +#ifdef DEBUG + printf("rscp_init: sdp version number is %d\n", sdp_version); +#endif + + return (0); +} + +/* + * send/receive interface: this is the new interface where application + * (currently scadm, SunVTS) send a request and wait for a reply in a + * single call. If a response is not required (resp=NULL), the function + * will only return the status of the request (whether it has been successfully + * or not). + */ +int +rscp_send_recv(rscp_msg_t *req, rscp_msg_t *resp, struct timespec *timeout) +{ + rmcadm_request_response_t rr; + rmcadm_msg_t *rr_req = &rr.req; + rmcadm_msg_t *rr_resp = &rr.resp; + + if (rsc_fd < 0) + return (EBADF); + + /* + * the request is required, it should not be NULL! + */ + if (req == NULL) + return (EINVAL); + + /* + * Check if the command is actually supported + * if not, return an error + */ + if (rsc_check_unsupported_cmd(req->type) != 0) + return (ENOTSUP); + + /* + * Check if this command will generate a response and if it will not, + * return an error. + */ + if (!rsc_cmd_response_guaranteed(req->type)) + return (ENOTSUP); + + rr_req->msg_type = req->type; + rr_req->msg_len = req->len; + rr_req->msg_buf = (caddr_t)req->data; + + if (resp != NULL) { + rr_resp->msg_type = resp->type; + rr_resp->msg_len = resp->len; + rr_resp->msg_buf = (caddr_t)resp->data; + rr_resp->msg_bytes = 0; + } else { + rr_resp->msg_type = DP_NULL_MSG; + rr_resp->msg_buf = (caddr_t)NULL; + rr_resp->msg_len = 0; + rr_resp->msg_bytes = 0; + } + + if (timeout == NULL) { + rr.wait_time = RR_TIMEOUT; + } else { + rr.wait_time = timeout->tv_sec * 1000 + + timeout->tv_nsec / 1000000; + } + rr.status = 0; + + if (ioctl(rsc_fd, RMCADM_REQUEST_RESPONSE, &rr) < 0) { +#ifdef DEBUG + printf("rscp_send_recv: req. failed, status=%d errno=%d\n", + rr_req->msg_type, rr.status, errno); +#endif + return (errno); + } + + return (0); +} + +/* + * function used to look up at the request/response table. Given a request + * type, will return a record which provides the following information: + * response expected and a timeout value + */ +static req_resp_table_t * +rsc_lookup_rr_table(req_resp_table_t *rr_table, int cnt, rsci8 type) +{ + int i; + +#ifdef DEBUG + printf("lookup for type %x, count %d\n", type, cnt); +#endif + + for (i = 0; i < cnt; i++) + if (rr_table[i].req_type == type) { + return (rr_table + i); + } + + return (NULL); +} + +/* + * function to check if a message type is in the list of unsupported commands + * If so, will return 1. + */ +static int +rsc_check_unsupported_cmd(rsci8 type) +{ + int i; + + for (i = 0; i < unsupported_cmds_cnt; i++) + if (unsupported_cmds[i] == type) { + return (1); + } + + return (0); +} + +/* + * Returns 1 if ALOM will generate a response to the given command code, + * otherwise it returns 0. If a command is not in the following list, + * and the protocol version is 2 or less, then ALOM will not generate + * a response to the command. This causes the driver to time out, + * and we want to avoid that situation. + */ +static int +rsc_cmd_response_guaranteed(rsci8 type) +{ + switch (type) { + case DP_GET_ALARM_STATE: + case DP_GET_CFGVAR: + case DP_GET_CFGVAR_NAME: + case DP_GET_CIRCUIT_BRKS: + case DP_GET_DATE_TIME: + case DP_GET_DEVICE: + case DP_GET_EVENT_LOG: + case DP_GET_FAN_STATUS: + case DP_GET_FRU_STATUS: + case DP_GET_HANDLE: + case DP_GET_HANDLE_NAME: + case DP_GET_LED_STATE: + case DP_GET_NETWORK_CFG: + case DP_GET_PCMCIA_INFO: + case DP_GET_PSU_STATUS: + case DP_GET_SDP_VERSION: + case DP_GET_SYSINFO: + case DP_GET_TEMP: + case DP_GET_TEMPERATURES: + case DP_GET_TICKCNT: + case DP_GET_TOD_CLOCK: + case DP_GET_USER_WATCHDOG: + case DP_GET_VOLTS: + case DP_MODEM_CONNECT: + case DP_MODEM_DATA: + case DP_MODEM_DISCONNECT: + case DP_RESET_RSC: + case DP_RMC_EVENTS: + case DP_RSC_STATUS: + case DP_RUN_TEST: + case DP_SEND_ALERT: + case DP_SET_ALARM_STATE: + case DP_SET_CFGVAR: + case DP_SET_CPU_SIGNATURE: + case DP_SET_DATE_TIME: + case DP_SET_DEFAULT_CFG: + case DP_SET_HOST_WATCHDOG: + case DP_SET_LED_STATE: + case DP_SET_USER_WATCHDOG: + case DP_UPDATE_FLASH: + case DP_USER_ADM: + return (1); + default: + return (sdp_version >= SDP_RESPONDS_TO_ALL_CMDS); + } +} + +/* + * RSC hard reset. Returns 0 on success, non-zero on error. + */ +int +rsc_nmi(void) +{ + if (rsc_fd < 0) + return (EBADF); + + if (ioctl(rsc_fd, RMCADM_RESET_SP, NULL) < 0) + return (errno); + + return (0); +} + +/* + * functions used (exclusively) for the firmware download + */ + +/* + * Call this routine to register a callback that will be called by the + * generic data protocol routines when a boot protocol message is + * received. Only one of these routines may be registered at a time. + * Note that receiving a boot protocol message has the effect of + * re-initializing the data protocol. Returns 0 on success, or non- + * zero on failure. + */ +int +rscp_register_bpmsg_cb(rscp_bpmsg_cb_t *cb) +{ + if (rsc_fd < 0) + return (EBADF); + + if (bpmsg_cb == NULL) { + bpmsg_cb = cb; + return (0); + } else { + return (EALREADY); + } +} + +/* + * This routine un-registers a boot protocol message callback. + */ +int +rscp_unregister_bpmsg_cb(rscp_bpmsg_cb_t *cb) +{ + if (rsc_fd < 0) + return (EBADF); + + if (bpmsg_cb == cb) { + bpmsg_cb = NULL; + return (0); + } else { + return (EINPROGRESS); + } +} + +/* + * Call this routine to send a boot protocol message. + */ +void +rscp_send_bpmsg(bp_msg_t *bpmsg) +{ + rmcadm_request_response_t rr_bp; + rmcadm_msg_t *req_bp = &rr_bp.req; + rmcadm_msg_t *resp_bp = &rr_bp.resp; + req_resp_table_t *rr_bp_item; + bp_msg_t bpmsg_reply; + + if (rsc_fd < 0 || bpmsg == NULL) + return; + + /* + * get the timeout value + */ + if ((rr_bp_item = rsc_lookup_rr_table(rr_bp_table, rr_bp_table_cnt, + bpmsg->cmd)) != NULL) { + + rr_bp.wait_time = rr_bp_item->timeout; + + } else { + + rr_bp.wait_time = RR_BP_TIMEOUT; + } + + rr_bp.status = 0; + + req_bp->msg_len = sizeof (bp_msg_t); + req_bp->msg_buf = (caddr_t)bpmsg; + + if (rr_bp.wait_time == 0) { + resp_bp->msg_buf = (caddr_t)NULL; + } else { + resp_bp->msg_len = sizeof (bp_msg_t); + resp_bp->msg_buf = (caddr_t)&bpmsg_reply; + } + +#ifdef DEBUG + printf("send BP cmd %x, expect reply %x/%d\n", + bpmsg->cmd, resp_bp->msg_buf, resp_bp->msg_len); +#endif + if (ioctl(rsc_fd, RMCADM_REQUEST_RESPONSE_BP, &rr_bp) < 0) { +#ifdef DEBUG + printf("rscp_send_bpmsg: BP cmd %x failed status=%d " + "errno=%d\n", bpmsg->cmd, rr_bp.status, errno); +#endif + return; + } + +#ifdef DEBUG + printf("got BP reply type=%x,%x,%x\n", + bpmsg_reply.cmd, bpmsg_reply.dat1, bpmsg_reply.dat2); +#endif + + /* + * reply received. call the registered callback (if any) + */ + if (bpmsg_cb != NULL && resp_bp->msg_buf != NULL) + bpmsg_cb(&bpmsg_reply); +} + +/* + * Write raw characters to the RSC control device. Returns 0 on success, + * non-zero on error. + */ +int +rsc_raw_write(char *buf, int nbytes) +{ + rmcadm_send_srecord_bp_t srec_bp; + bp_msg_t bpmsg_reply; + + if (rsc_fd < 0) + return (EBADF); + + srec_bp.data_len = (uint_t)nbytes; + srec_bp.data_buf = (caddr_t)buf; + srec_bp.resp_bp.msg_len = sizeof (bp_msg_t); + srec_bp.resp_bp.msg_buf = (caddr_t)&bpmsg_reply; + srec_bp.wait_time = RR_BOOT_LOAD_TIMEOUT; + srec_bp.status = 0; + +#ifdef DEBUG + printf("send srecord BP len=%d\n", nbytes); +#endif + if (ioctl(rsc_fd, RMCADM_SEND_SRECORD_BP, &srec_bp) < 0) { +#ifdef DEBUG + printf("rsc_raw_write: failed. status=%d ioctl error=%d\n", + srec_bp.status, errno); +#endif + return (errno); + } + +#ifdef DEBUG + printf("got BP reply type=%x\n", bpmsg_reply.cmd); +#endif + + /* + * reply received. call the registered callback (if any) + */ + if (bpmsg_cb != NULL) + bpmsg_cb(&bpmsg_reply); + + return (0); +} + +/* + * obsolete functions provided for backward compatibility + */ + +/* + * This function is obsolete and it is provided for backward compatibility. + * (no-op function). It was used to start up the data protocol. low-level + * protocol has moved to the kernel and the rmc_comm driver is responsible + * for setting up the data protocol. + * (obsolete) + */ +int +rscp_start(void) +{ + if (rsc_fd < 0) + return (EBADF); + + return (0); +} + +/* + * This function is obsolete and it is provided for backward compatibility. + * Previously, rscp_send() and rscp_recv() where used to send a request and + * read a reply respectively. Now, rscp_send_recv() should be used instead + * (request/response in one call). + * + * This is used to send a message by making an RMCADM_REQUEST_RESPONSE ioctl + * call. A lookup table (rr_table) is used to find out the expected reply + * (if any) and the timeout value for a message to be sent. The reply is then + * stored in a buffer (rsc_rx_buffer) to be returned by calling rscp_recv() + */ +int +rscp_send(rscp_msg_t *msgp) +{ + rmcadm_request_response_t rr; + rmcadm_msg_t *req = &rr.req; + rmcadm_msg_t *resp = &rr.resp; + req_resp_table_t *rr_item; + + if (rsc_fd < 0) + return (EBADF); + + /* + * sanity check + */ + if (msgp == NULL) + return (EINVAL); + + /* + * Check if the command is actually supported + * if not, return an error + */ + if (rsc_check_unsupported_cmd(msgp->type) != 0) + return (ENOTSUP); + + /* + * Check if this command will generate a response and if it will not, + * return an error. + */ + if (!rsc_cmd_response_guaranteed(msgp->type)) + return (ENOTSUP); + + /* + * init rx buffer + */ + rsc_rx_resp_len = 0; + rsc_rx_error = 0; + + req->msg_type = msgp->type; + req->msg_len = msgp->len; + req->msg_buf = msgp->data; + + if ((rr_item = rsc_lookup_rr_table(rr_table, rr_table_cnt, + msgp->type)) != NULL) { + resp->msg_type = rr_item->resp_type; + if (rr_item->resp_type == DP_NULL_MSG) { + /* + * no reply expected. so, no reply buffer needed + * (set to NULL) + */ + resp->msg_len = 0; + resp->msg_buf = (caddr_t)NULL; + } else { + resp->msg_len = RSC_MAX_RX_BUFFER; + resp->msg_buf = (caddr_t)rsc_rx_buffer; + } + + rr.wait_time = rr_item->timeout; + rsc_rx_resp_type = rr_item->resp_type; + } else { + return (ENOTSUP); + } + rr.status = 0; + +#ifdef DEBUG + printf("request/response %x/%x\n", req->msg_type, resp->msg_type); +#endif + if (ioctl(rsc_fd, RMCADM_REQUEST_RESPONSE, &rr) < 0) { +#ifdef DEBUG + printf("rscp_send: req %x failed, status=%d errno=%d\n", + rr.req.msg_type, rr.status, errno); +#endif + rsc_rx_error = errno; + + return (errno); + } + + /* + * reply received. get the number of bytes effectively returned + */ + rsc_rx_resp_len = resp->msg_bytes; + rsc_rx_resp_type = resp->msg_type; + +#ifdef DEBUG + printf("got reply type=%x len=%d\n", rsc_rx_resp_type, rsc_rx_resp_len); +#endif + + return (0); +} + +/* + * This function is obsolete and it is provided for backward compatibility + * Previously, rscp_send() and rscp_recv() where used to send a request and + * read a reply repectively. Now, rscp_send_recv() should be used instead + * (request/response in one call). + * + * This function returns the reply received when a request was previously sent + * using the rscp_send() function (stored in the rsc_rx_buffer buffer). If a + * reply was not received, then an error is returned. + * + * timeout parameter is declared for backward compatibility but it is not used. + */ +/*ARGSUSED*/ +int +rscp_recv(rscp_msg_t *msgp, struct timespec *timeout) +{ + int err = 0; + + if (rsc_fd < 0) + return (EBADF); + + /* + * sanity check + */ + if (msgp == NULL) + return (EINVAL); + + if (rsc_rx_error < 0) { + msgp->type = DP_NULL_MSG; + msgp->len = 0; + msgp->data = NULL; + + err = rsc_rx_error; + + } else { + msgp->type = rsc_rx_resp_type; + msgp->len = rsc_rx_resp_len; + msgp->data = rsc_rx_buffer; + } + +#ifdef DEBUG + printf("read reply. type=%x, err=%d\n", msgp->type, err); +#endif + + rsc_rx_resp_len = 0; + rsc_rx_error = 0; + rsc_rx_resp_type = DP_NULL_MSG; + + return (err); +} + +/* + * used to free up a (received) message. no-op function + */ +/*ARGSUSED*/ +int +rscp_free_msg(rscp_msg_t *msgp) +{ + if (rsc_fd < 0) + return (EBADF); + + return (0); +} diff --git a/usr/src/lib/librsc/sparc/mpxu/common/librsc.h b/usr/src/lib/librsc/sparc/mpxu/common/librsc.h new file mode 100644 index 0000000000..05b92f62c9 --- /dev/null +++ b/usr/src/lib/librsc/sparc/mpxu/common/librsc.h @@ -0,0 +1,118 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBRSC_H +#define _LIBRSC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/rmc_comm_lproto.h> +#include <sys/rmc_comm_hproto.h> +#include <sys/rmc_comm_dp_boot.h> +#include <sys/rmcadm.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The structure used to pass messages into and out of this layer. + */ +typedef struct rscp_msg { + rsci8 type; + rsci32 len; + void *data; + void *private; +} rscp_msg_t; + +typedef void rscp_bpmsg_cb_t(bp_msg_t *msg); + +#define RSC_RMCADM_DRV "/devices/pseudo/rmcadm@0:rmcadm" + +#define RSC_MAX_RX_BUFFER DP_MAX_MSGLEN + + +/* + * this table is used to match request/response in order to provide + * backward compatibility to obsolete functions: rscp_send(), rscp_recv(), + * + * in the old way, send and receive were decoupled: applications sent a + * request (rscp_send) and waited for a reply (rscp_recv) using two different + * calls. + * As the ioctl to the communication driver is a single call, send and receive + * cannot be decoupled. So, when the rscp_send is called, this table will tell + * which reply is expected and in what time. The reply is then stored in a + * temporary buffer. When the rscp_recv is called, it will return the + * content of the temporary buffer (if a reply was received) or an error + */ +typedef struct req_resp_table { + + uint8_t req_type; + uint8_t resp_type; + uint16_t resp_size; + uint_t timeout; + +} req_resp_table_t; + + +/* timeout value (millisecs) for request/response sessions */ + +#define RR_TIMEOUT 10000 +#define RR_SEPROM_TIMEOUT 10000 + +#define RR_BOOT_INIT_TIMEOUT 1000 +#define RR_BOOT_LOAD_TIMEOUT 10000 +#define RR_BOOT_RESET_TIMEOUT 0 +#define RR_BP_TIMEOUT 1000 + + +/* function prototypes */ + +int rscp_init(void); +int rscp_send_recv(rscp_msg_t *, rscp_msg_t *, struct timespec *); +int rsc_nmi(void); + + +/* function prototypes for firmware download */ + +int rscp_register_bpmsg_cb(rscp_bpmsg_cb_t *); +int rscp_unregister_bpmsg_cb(rscp_bpmsg_cb_t *); +void rscp_send_bpmsg(bp_msg_t *); +int rsc_raw_write(char *, int); + + +/* prototypes of obsolete functions */ + +int rscp_send(rscp_msg_t *); +int rscp_recv(rscp_msg_t *, struct timespec *); +int rscp_start(void); +int rscp_free_msg(rscp_msg_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBRSC_H */ |
