4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
40 #include "libfrureg.h"
43 #define NUM_ITER_BYTES 4
46 #define TAIL_ITER 1 /* not used */
51 #define TIMESTRINGLEN 128
52 #define TEMPERATURE_OFFSET 73
53 #define MIN_VERSION 17
54 #define GMT "%a, %b %d %Y %H:%M:%S GMT"
62 Status_CurrentR Status_CurrentR_status
[] = {
64 { 0x04, "DEEMED FAULTY"},
65 { 0x08, "FRU DETECTED"},
66 { 0x0c, "FRU DETECTED, DEEMED FAULTY"},
67 { 0x10, "PROXIED FAULT"},
68 { 0x14, "DEEMED FAULTY. Also PROXIED FAULT"},
69 { 0x18, "FRU DETECTED. Also PROXIED FAULT"},
70 { 0x1c, "FRU DETECTED, DEEMED FAULTY. Also PROXIED FAULT"},
72 { 0x24, "SUSPECT, DEEMED FAULTY"},
73 { 0x28, "SUSPECT, FRU DETECTED"},
74 { 0x2c, "SUSPECT, FRU DETECTED, DEEMED FAULTY"},
75 { 0x30, "SUSPECT. Also PROXIED FAULT"},
76 { 0x34, "SUSPECT, DEEMED FAULTY. Also PROXIED FAULT"},
77 { 0x38, "SUSPECT, FRU DETECTED. Also PROXIED FAULT"},
78 { 0x3c, "SUSPECT, FRU DETECTED, DEEMED FAULTY. Also PROXIED FAULT"},
79 { 0x40, "MAINTENANCE REQUIRED"},
80 { 0x44, "MAINTENANCE REQUIRED, DEEMED FAULTY"},
81 { 0x48, "MAINTENANCE REQUIRED, FRU DETECTED"},
82 { 0x4c, "MAINTENANCE REQUIRED, FRU DETECTED, DEEMED FAULTY"},
83 { 0x50, "MAINTENANCE REQUIRED. Also PROXIED FAULT"},
84 { 0x54, "MAINTENANCE REQUIRED, DEEMED FAULTY. Also PROXIED FAULT"},
85 { 0x58, "MAINTENANCE REQUIRED, FRU DETECTED. Also PROXIED FAULT"},
86 { 0x5c, "MAINTENANCE REQUIRED, FRU DETECTED, DEEMED FAULTY. \
88 { 0x60, "MAINTENANCE REQUIRED, SUSPECT"},
89 { 0x64, "MAINTENANCE REQUIRED, SUSPECT, DEEMED FAULTY"},
90 { 0x68, "MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED"},
91 { 0x6c, "MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED, DEEMED FAULTY"},
92 { 0x70, "MAINTENANCE REQUIRED, SUSPECT. Also PROXIED FAULT"},
93 { 0x74, "MAINTENANCE REQUIRED, SUSPECT, DEEMED FAULTY.\
95 { 0x78, "MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED. \
97 { 0x7c, "MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED, \
98 DEEMED FAULTY. Also PROXIED FAULT"},
100 { 0x84, "DISABLED, DEEMED FAULTY"},
101 { 0x88, "DISABLED, FRU DETECTED"},
102 { 0x8c, "DISABLED, FRU DETECTED, DEEMED FAULTY"},
103 { 0x90, "DISABLED. Also PROXIED FAULT"},
104 { 0x94, "DISABLED, DEEMED FAULTY. Also PROXIED FAULT"},
105 { 0x98, "DISABLED, FRU DETECTED. Also PROXIED FAULT"},
106 { 0x9c, "DISABLED, FRU DETECTED, DEEMED FAULTY. Also PROXIED FAULT"},
107 { 0xa0, "DISABLED, SUSPECT"},
108 { 0xa4, "DISABLED, SUSPECT, DEEMED FAULTY"},
109 { 0xa8, "DISABLED, SUSPECT, FRU DETECTED"},
110 { 0xac, "DISABLED, SUSPECT, FRU DETECTED, DEEMED FAULTY"},
111 { 0xb0, "DISABLED, SUSPECT. Also PROXIED FAULT"},
112 { 0xb4, "DISABLED, SUSPECT, DEEMED FAULTY. Also PROXIED FAULT"},
113 { 0xb8, "DISABLED, SUSPECT, FRU DETECTED. Also PROXIED FAULT"},
114 { 0xbc, "DISABLED, SUSPECT, FRU DETECTED, \
115 DEEMED FAULTY. Also PROXIED FAULT"},
116 { 0xc0, "DISABLED, MAINTENANCE REQUIRED"},
117 { 0xc4, "DISABLED, MAINTENANCE REQUIRED, DEEMED FAULTY"},
118 { 0xc8, "DISABLED, MAINTENANCE REQUIRED, FRU DETECTED"},
119 { 0xcc, "DISABLED, MAINTENANCE REQUIRED, FRU DETECTED, DEEMED FAULTY"},
120 { 0xd0, "DISABLED, MAINTENANCE REQUIRED. Also PROXIED FAULT"},
121 { 0xd4, "DISABLED, MAINTENANCE REQUIRED, \
122 DEEMED FAULTY. Also PROXIED FAULT"},
123 { 0xd8, "DISABLED, MAINTENANCE REQUIRED, \
124 FRU DETECTED. Also PROXIED FAULT"},
125 { 0xdc, "DISABLED, MAINTENANCE REQUIRED, FRU DETECTED, \
126 DEEMED FAULTY. Also PROXIED FAULT"},
127 { 0xe0, "DISABLED, MAINTENANCE REQUIRED, SUSPECT"},
128 { 0xe4, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, DEEMED FAULTY"},
129 { 0xe8, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED"},
130 { 0xec, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, \
131 FRU DETECTED, DEEMED FAULTY"},
132 { 0xf0, "DISABLED, MAINTENANCE REQUIRED, SUSPECT. Also PROXIED FAULT"},
133 { 0xf4, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, \
134 DEEMED FAULTY. Also PROXIED FAULT"},
135 { 0xf8, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, \
136 FRU DETECTED. Also PROXIED FAULT"},
137 { 0xfc, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, \
138 FRU DETECTED, DEEMED FAULTY. Also PROXIED FAULT"},
142 static void (*print_node
)(fru_node_t fru_type
, const char *path
,
143 const char *name
, end_node_fp_t
*end_node
,
146 static void print_element(const uint8_t *data
, const fru_regdef_t
*def
,
147 const char *parent_path
, int indent
);
149 static char tagname
[sizeof ("?_0123456789_0123456789_0123456789")];
151 static int containers_only
= 0, list_only
= 0, saved_status
= 0, xml
= 0;
156 int FMAmessageR
= -1;
157 int Fault_Install_DataR_flag
= 0;
158 int Power_On_DataR_flag
= 0;
160 int spd_revision
= 0;
162 * Definition for data elements found in devices but not found in
163 * the system's version of libfrureg
165 static fru_regdef_t unknown
= {
185 * Write message to standard error and possibly the error log buffer
188 error(const char *format
, ...)
193 /* make relevant output appear before error message */
194 if (fflush(stdout
) == EOF
) {
195 (void) fprintf(stderr
, "Error flushing output: %s\n",
200 va_start(args
, format
);
201 if (vfprintf(stderr
, format
, args
) < 0) exit(1);
202 if (errlog
&& (vfprintf(errlog
, format
, args
) < 0)) exit(1);
206 * Write message to standard output
209 output(const char *format
, ...)
214 va_start(args
, format
);
215 if (vfprintf(stdout
, format
, args
) < 0) {
216 error(gettext("Error writing output: %s\n"),
223 * Safe wrapper for putchar()
228 if (putchar(c
) == EOF
) {
229 error(gettext("Error writing output: %s\n"),
235 static void (*safeputchar
)(int c
) = voidputchar
;
238 * Safe wrapper for puts()
241 voidputs(const char *s
)
243 if (fputs(s
, stdout
) == EOF
) {
244 error(gettext("Error writing output: %s\n"),
250 static void (*safeputs
)(const char *s
) = voidputs
;
253 * XML-safe wrapper for putchar(): quotes XML-special characters
260 c
= fputs("<", stdout
);
263 c
= fputs(">", stdout
);
266 c
= fputs("&", stdout
);
269 c
= fputs(""", stdout
);
277 error(gettext("Error writing output: %s\n"),
284 * XML-safe analog of puts(): quotes XML-special characters
291 for (/* */; ((c
= *s
) != 0); s
++)
296 * Output the XML DTD derived from the registry provided by libfrureg
303 unsigned int i
, j
, num_elements
= 0;
307 const fru_regdef_t
*def
;
310 if (((element
= fru_reg_list_entries(&num_elements
)) == NULL
) ||
311 (num_elements
== 0)) {
312 error(gettext("No FRU ID Registry elements"));
316 if ((tagged
= calloc(num_elements
, sizeof (*tagged
))) == NULL
) {
317 error(gettext("Unable to get memory for tagged element list"),
323 * Output the DTD preamble
325 output("<!ELEMENT FRUID_XML_Tree (Parameter*, "
326 "(Fru | Location | Container)*,\n"
327 " Parameter*, ErrorLog?, Parameter*)>\n"
328 "<!ATTLIST FRUID_XML_Tree>\n"
330 "<!ELEMENT Parameter EMPTY>\n"
331 "<!ATTLIST Parameter type CDATA #REQUIRED>\n"
332 "<!ATTLIST Parameter name CDATA #REQUIRED>\n"
333 "<!ATTLIST Parameter value CDATA #REQUIRED>\n"
335 "<!ELEMENT Fru (Fru | Location | Container)*>\n"
336 "<!ATTLIST Fru name CDATA #REQUIRED>\n"
338 "<!ELEMENT Location (Fru | Location | Container)*>\n"
339 "<!ATTLIST Location\n"
340 " name CDATA #IMPLIED\n"
341 " value CDATA #IMPLIED\n"
344 "<!ELEMENT Container (ContainerData?, "
345 "(Fru | Location | Container)*)>\n"
346 "<!ATTLIST Container name CDATA #REQUIRED>\n"
347 "<!ATTLIST Container imagefile CDATA #IMPLIED>\n"
349 "<!ELEMENT ContainerData (Segment*)>\n"
350 "<!ATTLIST ContainerData>\n"
352 "<!ATTLIST Segment name CDATA #REQUIRED>\n"
354 "<!ELEMENT Index EMPTY>\n"
355 "<!ATTLIST Index value CDATA #REQUIRED>\n"
357 "<!ELEMENT ErrorLog (#PCDATA)>\n"
358 "<!ATTLIST ErrorLog>\n"
362 * Output the definition for each element
364 for (i
= 0; i
< num_elements
; i
++) {
365 assert(element
[i
] != NULL
);
366 /* Prevent incompatible duplicate defn. from FRUID Registry. */
367 if ((strcmp("Location", element
[i
])) == 0) continue;
368 if ((def
= fru_reg_lookup_def_by_name(element
[i
])) == NULL
) {
369 error(gettext("Error looking up registry "
370 "definition for \"%s\"\n"),
375 if (def
->tagType
!= FRU_X
) tagged
[i
] = 1;
377 if (def
->dataType
== FDTYPE_Record
) {
378 if (def
->iterationType
== FRU_NOT_ITERATED
)
379 output("<!ELEMENT %s (%s", element
[i
],
380 def
->enumTable
[0].text
);
382 output("<!ELEMENT %s (Index_%s*)>\n"
383 "<!ATTLIST Index_%s>\n"
384 "<!ELEMENT Index_%s (%s",
385 element
[i
], element
[i
], element
[i
],
386 element
[i
], def
->enumTable
[0].text
);
388 for (j
= 1; j
< def
->enumCount
; j
++)
389 output(",\n\t%s", def
->enumTable
[j
].text
);
392 } else if (def
->iterationType
== FRU_NOT_ITERATED
) {
393 output("<!ELEMENT %s EMPTY>\n"
394 "<!ATTLIST %s value CDATA #REQUIRED>\n",
395 element
[i
], element
[i
]);
397 if (def
->dataType
== FDTYPE_Enumeration
) {
398 output("<!-- %s valid enumeration values\n");
399 for (j
= 0; j
< def
->enumCount
; j
++) {
401 xputs(def
->enumTable
[j
].text
);
408 output("<!ELEMENT %s (Index*)>\n", element
[i
]);
413 /* Provide for returning the tag for an "unknown" element */
414 output("<!ATTLIST UNKNOWN tag CDATA \"UNKNOWN\">\n\n");
418 * List all data elements as possible members of "Segment"
420 output("<!ELEMENT Segment ((UNKNOWN");
421 for (i
= 0; i
< num_elements
; i
++) {
422 if (tagged
[i
]) output("\n\t| %s", element
[i
]);
432 * Function to convert bcd to binary to correct the SPD_Manufacturer_Week
435 static void convertbcdtobinary(int *val
)
437 unsigned int newval
= (unsigned int)*val
, tmpval
= 0;
439 tmpval
= (tmpval
<< 4) | (newval
& 0xF);
443 newval
= (newval
* 10) + (tmpval
& 0xF);
450 * Checking UTF-8 printable charecter
452 static int check_utf_char(const uint8_t *field
, int len
)
455 char tmp
[128], tmp1
[128], tmp2
[128];
456 (void) sprintf(tmp
, " (Invalid Data");
457 (void) sprintf(tmp2
, "0x");
458 for (i
= 0; i
< len
&& field
[i
]; i
++) {
459 (void) sprintf(tmp1
, "%2.2X", field
[i
]);
460 (void) strcat(tmp2
, tmp1
);
461 if (iswprint(field
[i
]) == 0) {
463 (void) sprintf(tmp1
, " : 0x%2.2X", field
[i
]);
464 (void) strcat(tmp
, tmp1
);
468 (void) sprintf(tmp1
, ")");
469 (void) strcat(tmp
, tmp1
);
470 (void) strcat(tmp2
, tmp
);
477 * Safely pretty-print the value of a field
480 print_field(const uint8_t *field
, const fru_regdef_t
*def
)
482 char *errmsg
= NULL
, timestring
[TIMESTRINGLEN
], path
[16384];
492 uchar_t first_byte
, data
[128];
494 const fru_regdef_t
*new_def
;
496 const char *elem_name
= NULL
;
497 const char *parent_path
;
498 switch (def
->dataType
) {
500 assert(def
->payloadLen
<= sizeof (value
));
501 switch (def
->dispType
) {
503 for (i
= 0; i
< def
->payloadLen
; i
++)
504 output("%c%c%c%c%c%c%c%c",
505 ((field
[i
] & 0x80) ? '1' : '0'),
506 ((field
[i
] & 0x40) ? '1' : '0'),
507 ((field
[i
] & 0x20) ? '1' : '0'),
508 ((field
[i
] & 0x10) ? '1' : '0'),
509 ((field
[i
] & 0x08) ? '1' : '0'),
510 ((field
[i
] & 0x04) ? '1' : '0'),
511 ((field
[i
] & 0x02) ? '1' : '0'),
512 ((field
[i
] & 0x01) ? '1' : '0'));
518 (void) memcpy((((uint8_t *)&value
) +
519 sizeof (value
) - def
->payloadLen
),
520 field
, def
->payloadLen
);
522 (strcmp(def
->name
, "SPD_Manufacture_Week") == 0)) {
523 valueint
= (int)value
;
524 if (spd_memtype
&& spd_revision
) {
525 convertbcdtobinary(&valueint
);
529 output("%d", valueint
);
533 ((strcmp(def
->name
, "Lowest") == 0) ||
534 (strcmp(def
->name
, "Highest") == 0) ||
535 (strcmp(def
->name
, "Latest") == 0)))
536 output((def
->dispType
== FDISP_Octal
) ?
537 "%llo" : "%lld (%lld degrees C)",
538 value
, (value
- TEMPERATURE_OFFSET
));
540 output((def
->dispType
== FDISP_Octal
) ?
541 "%llo" : "%lld", value
);
544 if (def
->payloadLen
> sizeof (timefield
)) {
545 errmsg
= "time value too large for formatting";
549 (void) memcpy((((uint8_t *)&timefield
) +
550 sizeof (timefield
) - def
->payloadLen
),
551 field
, def
->payloadLen
);
552 if (timefield
== 0) {
553 errmsg
= "No Value Recorded";
556 if ((tm
= gmtime(&timefield
)) == NULL
) {
557 errmsg
= "cannot convert time value";
560 if (strftime(timestring
, sizeof (timestring
), GMT
, tm
)
562 errmsg
= "formatted time would overflow buffer";
565 safeputs(timestring
);
571 if (strcmp(def
->name
, "Message") == 0) {
572 if (FMAmessageR
== 0)
573 elem_name
= "FMA_Event_DataR";
574 else if (FMAmessageR
== 1)
575 elem_name
= "FMA_MessageR";
576 if (elem_name
!= NULL
) {
577 (void) memcpy(data
, field
,
580 fru_reg_lookup_def_by_name
582 (void) snprintf(path
, sizeof (path
),
583 "/Status_EventsR[%d]/Message(FMA)",
587 print_element(data
, new_def
,
588 parent_path
, 2*INDENT
);
593 if (strcmp(def
->name
, "Fru_Path") == 0) {
594 if (check_utf_char(field
, def
->payloadLen
) == 1)
597 for (i
= 0; i
< def
->payloadLen
&& field
[i
]; i
++)
598 safeputchar(field
[i
]);
600 case FDTYPE_Enumeration
:
602 (void) memcpy((((uint8_t *)&value
) + sizeof (value
)
604 field
, def
->payloadLen
);
605 for (i
= 0; i
< def
->enumCount
; i
++)
606 if (def
->enumTable
[i
].value
== value
) {
607 if (strcmp(def
->name
, "Event_Code") == 0) {
608 if (strcmp(def
->enumTable
[i
].text
,
609 "FMA Message R") == 0)
612 if (strcmp(def
->enumTable
[i
].text
,
613 "FMA Event Data R") == 0)
616 if (strcmp(def
->name
,
617 "SPD_Fundamental_Memory_Type") == 0) {
618 if (strcmp(def
->enumTable
[i
].text
,
619 "DDR II SDRAM") == 0)
622 safeputs(def
->enumTable
[i
].text
);
626 errmsg
= "unrecognized value";
630 /* If nothing matched above, print the field in hex */
631 switch (def
->dispType
) {
633 (void) memcpy((uchar_t
*)&first_byte
, field
, 1);
634 if (isprint(first_byte
)) {
635 for (i
= 0; i
< def
->payloadLen
&& field
[i
];
637 safeputchar(field
[i
]);
641 for (i
= 0; i
< def
->payloadLen
; i
++) {
642 if ((i
== 4) || (i
== 6) ||
643 (i
== 8) || (i
== 10))
645 output("%2.2x", field
[i
]);
649 if ((strcmp(def
->name
, "Status") == 0) ||
650 (strcmp(def
->name
, "Old_Status") == 0) ||
651 (strcmp(def
->name
, "New_Status") == 0)) {
652 int status_length
= \
653 sizeof (Status_CurrentR_status
) / \
654 sizeof (*(Status_CurrentR_status
));
657 if (Status_CurrentR_status
[i
].value
== \
661 } while (i
< status_length
);
662 if (i
< status_length
)
663 output("0x%2.2X (%s)", *(field
),
664 Status_CurrentR_status
[i
].data
);
666 output("0x%2.2X (UNKNOWN)", *(field
));
669 if (strcmp(def
->name
,
670 "SPD_Data_Revision_Code") == 0) {
673 (void) memcpy((((uint8_t *)&value
)
674 + sizeof (value
) - def
->payloadLen
),
675 field
, def
->payloadLen
);
676 valueint
= (int)value
;
677 if ((valueint
>= MIN_VERSION
) && (spd_memtype
))
680 for (i
= 0; i
< def
->payloadLen
; i
++)
681 output("%2.2X", field
[i
]);
685 /* Safely print any error message associated with the field */
687 if (strcmp(def
->name
, "Fault_Diag_Secs") != 0) {
696 * Recursively print the contents of a data element
699 print_element(const uint8_t *data
, const fru_regdef_t
*def
,
700 const char *parent_path
, int indent
)
708 indent
= (xml
) ? (indent
+ INDENT
) : (2*INDENT
);
709 if (strcmp(def
->name
, "Sun_SPD_DataR") == 0) {
710 Fault_Install_DataR_flag
= indent
;
711 Power_On_DataR_flag
= indent
;
714 * Construct the path, or, for XML, the name, for the current
717 if ((def
->iterationCount
== 0) &&
718 (def
->iterationType
!= FRU_NOT_ITERATED
)) {
720 if (def
->dataType
== FDTYPE_Record
) {
721 len
= strlen("Index_") + strlen(def
->name
) + 1;
723 (void) snprintf(path
, len
,
724 "Index_%s", def
->name
);
730 path
= (char *)parent_path
;
733 path
= (char *)def
->name
;
735 len
= strlen(parent_path
) + sizeof ("/") +
737 (def
->iterationCount
? sizeof ("[255]") : 0);
739 bytes
= snprintf(path
, len
,
740 "%s/%s", parent_path
, def
->name
);
744 if ((Fault_Install_DataR_flag
) &&
745 (strcmp(path
, "E_1_46") == 0) || (strcmp(path
, "/E_1_46") == 0)) {
747 char timestring
[128];
748 time_t timefield
= 0;
750 indent
= Fault_Install_DataR_flag
;
751 (void) memcpy((uint8_t *)&timefield
, data
, 4);
752 if (timefield
== 0) {
753 (void) sprintf(timestring
,
754 "00000000 (No Value Recorded)\"");
756 if ((tm
= gmtime(&timefield
)) == NULL
)
757 (void) sprintf(timestring
,
758 "cannot convert time value");
759 if (strftime(timestring
,
760 sizeof (timestring
), GMT
, tm
) == 0)
761 (void) sprintf(timestring
,
762 "formatted time would overflow buffer");
765 (void) sprintf(path
, "Fault_Install_DataR");
766 output("%*s<%s>\n", indent
, "", path
);
767 indent
= Fault_Install_DataR_flag
+ INDENT
;
768 (void) sprintf(path
, "UNIX_Timestamp32");
769 output("%*s<%s value=\"", indent
, "", path
);
771 output("%s\"/>\n", timestring
);
772 (void) sprintf(path
, "MACADDR");
773 output("%*s<%s value=\"", indent
, "", path
);
774 for (cnt
= 4; cnt
< 4 + 6; cnt
++) {
775 output("%2.2x", data
[cnt
]);
781 (void) sprintf(path
, "Status");
782 output("%*s<%s value=\"", indent
, "", path
);
784 output("%2.2x\"/>\n", data
[10]);
785 (void) sprintf(path
, "Initiator");
786 output("%*s<%s value=\"", indent
, "", path
);
788 output("%2.2x\"/>\n", data
[11]);
789 (void) sprintf(path
, "Message_Type");
790 output("%*s<%s value=\"", indent
, "", path
);
792 output("%2.2x\"/>\n", data
[12]);
793 (void) sprintf(path
, "Message_32");
794 output("%*s<%s value=\"", indent
, "", path
);
795 for (cnt
= 13; cnt
< 13 + 32; cnt
++)
796 output("%2.2x", data
[cnt
]);
799 indent
= Fault_Install_DataR_flag
;
800 (void) sprintf(path
, "Fault_Install_DataR");
801 output("%*s</%s>\n", indent
, "", path
);
803 (void) sprintf(path
, "/Fault_Install_DataR");
804 output("%*s%s\n", indent
, "", path
);
806 "/Fault_Install_DataR/UNIX_Timestamp32");
807 output("%*s%s: ", indent
, "", path
);
808 output("%s\n", timestring
);
809 (void) sprintf(path
, "/Fault_Install_DataR/MACADDR");
810 output("%*s%s: ", indent
, "", path
);
811 for (cnt
= 4; cnt
< 4 + 6; cnt
++) {
812 output("%2.2x", data
[cnt
]);
817 (void) sprintf(path
, "/Fault_Install_DataR/Status");
818 output("%*s%s: ", indent
, "", path
);
819 output("%2.2x\n", data
[10]);
820 (void) sprintf(path
, "/Fault_Install_DataR/Initiator");
821 output("%*s%s: ", indent
, "", path
);
822 output("%2.2x\n", data
[11]);
824 "/Fault_Install_DataR/Message_Type");
825 output("%*s%s: ", indent
, "", path
);
826 output("%2.2x\n", data
[12]);
827 (void) sprintf(path
, "/Fault_Install_DataR/Message_32");
828 output("%*s%s: ", indent
, "", path
);
829 for (cnt
= 13; cnt
< 13 + 32; cnt
++)
830 output("%2.2x", data
[cnt
]);
833 Fault_Install_DataR_flag
= 0;
835 } else if ((Power_On_DataR_flag
) && (
836 strcmp(path
, "C_10_8") == 0 ||
837 (strcmp(path
, "/C_10_8") == 0))) {
839 char timestring
[128];
840 time_t timefield
= 0;
842 indent
= Power_On_DataR_flag
;
843 (void) memcpy((uint8_t *)&timefield
, data
, 4);
844 if (timefield
== 0) {
845 (void) sprintf(timestring
,
846 "00000000 (No Value Recorded)");
848 if ((tm
= gmtime(&timefield
)) == NULL
)
849 (void) sprintf(timestring
,
850 "cannot convert time value");
851 if (strftime(timestring
,
852 sizeof (timestring
), GMT
, tm
) == 0)
853 (void) sprintf(timestring
,
854 "formatted time would overflow buffer");
857 (void) sprintf(path
, "Power_On_DataR");
858 output("%*s<%s>\n", indent
, "", path
);
859 indent
= Power_On_DataR_flag
+ INDENT
;
860 (void) sprintf(path
, "UNIX_Timestamp32");
861 output("%*s<%s value=\"", indent
, "", path
);
863 output("%s\"/>\n", timestring
);
864 (void) sprintf(path
, "Power_On_Minutes");
865 output("%*s<%s value=\"", indent
, "", path
);
866 for (cnt
= 4; cnt
< 4 + 4; cnt
++)
867 output("%2.2x", data
[cnt
]);
870 indent
= Power_On_DataR_flag
;
871 (void) sprintf(path
, "Power_On_DataR");
872 output("%*s</%s>\n", indent
, "", path
);
874 (void) sprintf(path
, "/Power_On_DataR");
875 output("%*s%s\n", indent
, "", path
);
877 "/Power_On_DataR/UNIX_Timestamp32");
878 output("%*s%s: ", indent
, "", path
);
879 output("%s\n", timestring
);
881 "/Power_On_DataR/Power_On_Minutes");
882 output("%*s%s: ", indent
, "", path
);
883 for (cnt
= 4; cnt
< 4 + 4; cnt
++)
884 output("%2.2x", data
[cnt
]);
887 Power_On_DataR_flag
= 0;
891 * Handle the various categories of data elements: iteration,
894 if (def
->iterationCount
) {
895 int iterlen
= (def
->payloadLen
- NUM_ITER_BYTES
)/
905 * Make a new element definition to describe the components
908 (void) memcpy(&newdef
, def
, sizeof (newdef
));
909 newdef
.iterationCount
= 0;
910 newdef
.payloadLen
= iterlen
;
913 * Validate the contents of the iteration control bytes
915 if (data
[HEAD_ITER
] >= def
->iterationCount
) {
917 error(gettext("%s: Invalid iteration head: %d "
918 "(should be less than %d)\n"),
919 path
, data
[HEAD_ITER
], def
->iterationCount
);
922 if (data
[NUM_ITER
] > def
->iterationCount
) {
924 error(gettext("%s: Invalid iteration count: %d "
925 "(should not be greater than %d)\n"),
926 path
, data
[NUM_ITER
], def
->iterationCount
);
929 if (data
[MAX_ITER
] != def
->iterationCount
) {
931 error(gettext("%s: Invalid iteration maximum: %d "
932 "(should equal %d)\n"),
933 path
, data
[MAX_ITER
], def
->iterationCount
);
937 head
= data
[HEAD_ITER
];
938 num
= data
[NUM_ITER
];
941 num
= def
->iterationCount
;
942 error(gettext("%s: Showing all iterations\n"), path
);
946 output("%*s<%s>\n", indent
, "", path
);
948 output("%*s%s (%d iterations)\n", indent
, "", path
,
952 * Print each component of the iteration
954 for (i
= head
, n
= 0, data
+= 4;
956 i
= ((i
+ 1) % def
->iterationCount
), n
++) {
957 if (!xml
) (void) sprintf((path
+ bytes
), "[%d]", n
);
959 print_element((data
+ i
*iterlen
), &newdef
, path
,
963 if (xml
) output("%*s</%s>\n", indent
, "", path
);
965 } else if (def
->dataType
== FDTYPE_Record
) {
966 const fru_regdef_t
*component
;
969 output("%*s<%s>\n", indent
, "", path
);
971 output("%*s%s\n", indent
, "", path
);
974 * Print each component of the record
976 for (i
= 0; i
< def
->enumCount
;
977 i
++, data
+= component
->payloadLen
) {
978 component
= fru_reg_lookup_def_by_name(
979 def
->enumTable
[i
].text
);
980 assert(component
!= NULL
);
981 print_element(data
, component
, path
, indent
);
984 if (xml
) output("%*s</%s>\n", indent
, "", path
);
987 * Base case: print the field formatted for XML
989 char *format
= ((def
== &unknown
)
990 ? "%*s<UNKNOWN tag=\"%s\" value=\""
991 : "%*s<%s value=\"");
993 output(format
, indent
, "", path
);
994 print_field(data
, def
);
996 output("\"/>\n"); /* \" confuses cstyle */
998 if ((strcmp(def
->name
, "Message") == 0) &&
999 ((FMAmessageR
== 0) || (FMAmessageR
== 1))) {
1000 const char *elem_name
= NULL
;
1001 const char *parent_path
;
1002 uchar_t tmpdata
[128];
1004 const fru_regdef_t
*new_def
;
1006 if (FMAmessageR
== 0)
1007 elem_name
= "FMA_Event_DataR";
1008 else if (FMAmessageR
== 1)
1009 elem_name
= "FMA_MessageR";
1010 if (elem_name
!= NULL
) {
1011 (void) memcpy(tmpdata
, data
, def
->payloadLen
);
1012 new_def
= fru_reg_lookup_def_by_name(elem_name
);
1013 (void) snprintf(path
, sizeof (path
),
1014 "/Status_EventsR[%d]/Message(FMA)", iterglobal
);
1016 print_element(tmpdata
, new_def
,
1017 parent_path
, 2*INDENT
);
1024 * Base case: print the field
1026 output("%*s%s: ", indent
, "", path
);
1027 print_field(data
, def
);
1033 * Print the contents of a packet (i.e., a tagged data element)
1037 print_packet(fru_tag_t
*tag
, uint8_t *payload
, size_t length
, void *args
)
1039 int tag_type
= get_tag_type(tag
);
1041 size_t payload_length
= 0;
1043 const fru_regdef_t
*def
;
1046 * Build a definition for unrecognized tags (e.g., not in libfrureg)
1048 if ((tag_type
== -1) ||
1049 ((payload_length
= get_payload_length(tag
)) != length
)) {
1052 unknown
.tagType
= -1;
1053 unknown
.tagDense
= -1;
1054 unknown
.payloadLen
= length
;
1055 unknown
.dataLength
= unknown
.payloadLen
;
1058 (void) snprintf(tagname
, sizeof (tagname
), "INVALID");
1060 (void) snprintf(tagname
, sizeof (tagname
),
1061 "%s_%u_%u_%u", get_tagtype_str(tag_type
),
1062 get_tag_dense(tag
), payload_length
, length
);
1063 } else if ((def
= fru_reg_lookup_def_by_tag(*tag
)) == NULL
) {
1066 unknown
.tagType
= tag_type
;
1067 unknown
.tagDense
= get_tag_dense(tag
);
1068 unknown
.payloadLen
= payload_length
;
1069 unknown
.dataLength
= unknown
.payloadLen
;
1071 (void) snprintf(tagname
, sizeof (tagname
), "%s_%u_%u",
1072 get_tagtype_str(unknown
.tagType
),
1073 unknown
.tagDense
, payload_length
);
1078 * Print the defined element
1080 print_element(payload
, def
, "", INDENT
);
1082 return (FRU_SUCCESS
);
1086 * Print a segment's name and the contents of each data element in the segment
1089 print_packets_in_segment(fru_seghdl_t segment
, void *args
)
1096 if ((status
= fru_get_segment_name(segment
, &name
)) != FRU_SUCCESS
) {
1097 saved_status
= status
;
1099 error(gettext("Error getting segment name: %s\n"),
1100 fru_strerror(status
));
1105 output("%*s<Segment name=\"%s\">\n", INDENT
, "", name
);
1107 output("%*sSEGMENT: %s\n", INDENT
, "", name
);
1109 if (strcmp(name
, "ED") == 0) {
1110 if (xml
) output("%*s</Segment>\n", INDENT
, "");
1112 return (FRU_SUCCESS
);
1114 /* Iterate over the packets in the segment, printing the contents */
1115 if ((status
= fru_for_each_packet(segment
, print_packet
, args
))
1117 saved_status
= status
;
1118 error(gettext("Error processing data in segment \"%s\": %s\n"),
1119 name
, fru_strerror(status
));
1122 if (xml
) output("%*s</Segment>\n", INDENT
, "");
1126 return (FRU_SUCCESS
);
1131 print_node_path(fru_node_t fru_type
, const char *path
, const char *name
,
1132 end_node_fp_t
*end_node
, void **end_args
)
1134 output("%s%s\n", path
,
1135 ((fru_type
== FRU_NODE_CONTAINER
) ? " (container)"
1136 : ((fru_type
== FRU_NODE_FRU
) ? " (fru)" : "")));
1140 * Close the XML element for a "location" node
1144 end_location_xml(fru_nodehdl_t node
, const char *path
, const char *name
,
1147 assert(args
!= NULL
);
1148 output("</Location> <!-- %s -->\n", args
);
1152 * Close the XML element for a "fru" node
1156 end_fru_xml(fru_nodehdl_t node
, const char *path
, const char *name
, void *args
)
1158 assert(args
!= NULL
);
1159 output("</Fru> <!-- %s -->\n", args
);
1163 * Close the XML element for a "container" node
1167 end_container_xml(fru_nodehdl_t node
, const char *path
, const char *name
,
1170 assert(args
!= NULL
);
1171 output("</Container> <!-- %s -->\n", args
);
1175 * Introduce a node in XML and set the appropriate node-closing function
1179 print_node_xml(fru_node_t fru_type
, const char *path
, const char *name
,
1180 end_node_fp_t
*end_node
, void **end_args
)
1184 output("<Fru name=\"%s\">\n", name
);
1185 *end_node
= end_fru_xml
;
1187 case FRU_NODE_CONTAINER
:
1188 output("<Container name=\"%s\">\n", name
);
1189 *end_node
= end_container_xml
;
1192 output("<Location name=\"%s\">\n", name
);
1193 *end_node
= end_location_xml
;
1197 *end_args
= (void *) name
;
1201 * Print node info and, where appropriate, node contents
1205 process_node(fru_nodehdl_t node
, const char *path
, const char *name
,
1206 void *args
, end_node_fp_t
*end_node
, void **end_args
)
1210 fru_node_t fru_type
= FRU_NODE_UNKNOWN
;
1213 if ((status
= fru_get_node_type(node
, &fru_type
)) != FRU_SUCCESS
) {
1214 saved_status
= status
;
1215 error(gettext("Error getting node type: %s\n"),
1216 fru_strerror(status
));
1219 if (containers_only
) {
1220 if (fru_type
!= FRU_NODE_CONTAINER
)
1221 return (FRU_SUCCESS
);
1225 /* Introduce the node */
1226 assert(print_node
!= NULL
);
1227 print_node(fru_type
, path
, name
, end_node
, end_args
);
1230 return (FRU_SUCCESS
);
1232 /* Print the contents of each packet in each segment of a container */
1233 if (fru_type
== FRU_NODE_CONTAINER
) {
1234 if (xml
) output("<ContainerData>\n");
1236 fru_for_each_segment(node
, print_packets_in_segment
,
1239 saved_status
= status
;
1240 error(gettext("Error processing node \"%s\": %s\n"),
1241 name
, fru_strerror(status
));
1243 if (xml
) output("</ContainerData>\n");
1246 return (FRU_SUCCESS
);
1250 * Process the node if its path matches the search path in "args"
1254 process_matching_node(fru_nodehdl_t node
, const char *path
, const char *name
,
1255 void *args
, end_node_fp_t
*end_node
, void **end_args
)
1260 if (!fru_pathmatch(path
, args
))
1261 return (FRU_SUCCESS
);
1263 status
= process_node(node
, path
, path
, args
, end_node
, end_args
);
1265 return ((status
== FRU_SUCCESS
) ? FRU_WALK_TERMINATE
: status
);
1269 * Write the trailer required for well-formed DTD-compliant XML
1275 if (ftell(errlog
) > 0) {
1278 output("<ErrorLog>\n");
1281 while ((c
= getc(errlog
)) != EOF
)
1283 output("</ErrorLog>\n");
1289 error(gettext("Error copying error messages to \"ErrorLog\""),
1293 output("</FRUID_XML_Tree>\n");
1297 * Print available FRU ID information
1300 prtfru(const char *searchpath
, int containers_only_flag
, int list_only_flag
,
1305 fru_nodehdl_t frutree
= 0;
1308 /* Copy parameter flags to global flags */
1309 containers_only
= containers_only_flag
;
1310 list_only
= list_only_flag
;
1314 /* Help arrange for correct, efficient interleaving of output */
1315 (void) setvbuf(stderr
, NULL
, _IOLBF
, 0);
1318 /* Initialize for XML--or not */
1320 safeputchar
= xputchar
;
1323 print_node
= print_node_xml
;
1325 if ((errlog
= tmpfile()) == NULL
) {
1326 (void) fprintf(stderr
,
1327 "Error creating error log file: %s\n",
1332 /* Output the XML preamble */
1333 output("<?xml version=\"1.0\" ?>\n"
1335 " Copyright 2000-2002 Sun Microsystems, Inc. "
1336 "All rights reserved.\n"
1337 " Use is subject to license terms.\n"
1339 "<!DOCTYPE FRUID_XML_Tree SYSTEM \"prtfrureg.dtd\">\n\n"
1340 "<FRUID_XML_Tree>\n");
1342 /* Arrange to always properly terminate XML */
1343 if (atexit(terminate_xml
))
1344 error(gettext("Warning: XML will not be terminated: "
1345 "%s\n"), strerror(errno
));
1347 print_node
= print_node_path
;
1350 /* Get the root node */
1351 if ((status
= fru_get_root(&frutree
)) == FRU_NODENOTFOUND
) {
1352 error(gettext("This system does not support PICL "
1353 "infrastructure to provide FRUID data\n"
1354 "Please use the platform SP to access the FRUID "
1357 } else if (status
!= FRU_SUCCESS
) {
1358 error(gettext("Unable to access FRU ID data: %s\n"),
1359 fru_strerror(status
));
1363 /* Process the tree */
1364 if (searchpath
== NULL
) {
1365 status
= fru_walk_tree(frutree
, "", process_node
, NULL
);
1367 status
= fru_walk_tree(frutree
, "", process_matching_node
,
1368 (void *)searchpath
);
1369 if (status
== FRU_WALK_TERMINATE
) {
1370 status
= FRU_SUCCESS
;
1371 } else if (status
== FRU_SUCCESS
) {
1372 error(gettext("\"%s\" not found\n"), searchpath
);
1377 if (status
!= FRU_SUCCESS
)
1378 error(gettext("Error processing FRU tree: %s\n"),
1379 fru_strerror(status
));
1381 return (((status
== FRU_SUCCESS
) && (saved_status
== 0)) ? 0 : 1);