103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License, Version 1.0 only
603831d35Sstevel  * (the "License").  You may not use this file except in compliance
703831d35Sstevel  * with the License.
803831d35Sstevel  *
903831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1003831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1103831d35Sstevel  * See the License for the specific language governing permissions
1203831d35Sstevel  * and limitations under the License.
1303831d35Sstevel  *
1403831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1503831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1603831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1703831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1803831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1903831d35Sstevel  *
2003831d35Sstevel  * CDDL HEADER END
2103831d35Sstevel  */
2203831d35Sstevel /*
2303831d35Sstevel  * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
2403831d35Sstevel  * All rights reserved.
25*2bcbf80cSPeter Tribble  * Copyright (c) 2020 Peter Tribble.
2603831d35Sstevel  */
2703831d35Sstevel 
2803831d35Sstevel #include <stdio.h>
2903831d35Sstevel #include <stdlib.h>
3003831d35Sstevel #include <unistd.h>
3103831d35Sstevel #include <ctype.h>
3203831d35Sstevel #include <string.h>
3303831d35Sstevel #include <kvm.h>
3403831d35Sstevel #include <varargs.h>
3503831d35Sstevel #include <errno.h>
3603831d35Sstevel #include <time.h>
3703831d35Sstevel #include <dirent.h>
3803831d35Sstevel #include <fcntl.h>
3903831d35Sstevel #include <sys/param.h>
4003831d35Sstevel #include <sys/stat.h>
4103831d35Sstevel #include <sys/types.h>
4203831d35Sstevel #include <sys/utsname.h>
4303831d35Sstevel #include <sys/openpromio.h>
4403831d35Sstevel #include <kstat.h>
4503831d35Sstevel #include <libintl.h>
4603831d35Sstevel #include <syslog.h>
4703831d35Sstevel #include <sys/dkio.h>
4803831d35Sstevel #include <sys/sbd_ioctl.h>
4903831d35Sstevel #include <sys/sbdp_mem.h>
5003831d35Sstevel #include <sys/serengeti.h>
5103831d35Sstevel #include <sys/mc.h>
5203831d35Sstevel #include "pdevinfo.h"
5303831d35Sstevel #include "display.h"
5403831d35Sstevel #include "pdevinfo_sun4u.h"
5503831d35Sstevel #include "display_sun4u.h"
5603831d35Sstevel #include "libprtdiag.h"
5703831d35Sstevel 
5803831d35Sstevel #if !defined(TEXT_DOMAIN)
5903831d35Sstevel #define	TEXT_DOMAIN	"SYS_TEST"
6003831d35Sstevel #endif
6103831d35Sstevel 
6203831d35Sstevel #define	KBYTE	1024
6303831d35Sstevel #define	MBYTE	(KBYTE * KBYTE)
6403831d35Sstevel 
6503831d35Sstevel #define	MEM_UK_SIZE_MASK	0x3FF
6603831d35Sstevel 
6703831d35Sstevel /*
6803831d35Sstevel  * Global variables.
6903831d35Sstevel  */
7003831d35Sstevel static memory_bank_t	*bank_head;
7103831d35Sstevel static memory_bank_t	*bank_tail;
7203831d35Sstevel static memory_seg_t	*seg_head;
7303831d35Sstevel 
7403831d35Sstevel /*
7503831d35Sstevel  * Local functions.
7603831d35Sstevel  */
7703831d35Sstevel static void add_bank_node(uint64_t mc_decode, int portid, char *bank_status);
7803831d35Sstevel static void add_seg_node(void);
7903831d35Sstevel static memory_seg_t *match_seg(uint64_t);
8003831d35Sstevel 
8103831d35Sstevel 
8203831d35Sstevel /*
8303831d35Sstevel  * Used for US-I and US-II systems
8403831d35Sstevel  */
8503831d35Sstevel /*ARGSUSED0*/
8603831d35Sstevel void
8703831d35Sstevel display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats,
88*2bcbf80cSPeter Tribble 	struct mem_total *memory_total)
8903831d35Sstevel {
9003831d35Sstevel 	log_printf(dgettext(TEXT_DOMAIN, "Memory size: "), 0);
9103831d35Sstevel 
9203831d35Sstevel 	if (sysconf(_SC_PAGESIZE) == -1 || sysconf(_SC_PHYS_PAGES) == -1)
9303831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN, "unable to determine\n"), 0);
9403831d35Sstevel 	else {
9503831d35Sstevel 		uint64_t	mem_size;
9603831d35Sstevel 
9703831d35Sstevel 		mem_size =
9803831d35Sstevel 		    (uint64_t)sysconf(_SC_PAGESIZE) * \
9903831d35Sstevel 			(uint64_t)sysconf(_SC_PHYS_PAGES);
10003831d35Sstevel 
10103831d35Sstevel 		if (mem_size >= MBYTE)
10203831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN, "%d Megabytes\n"),
10303831d35Sstevel 				(int)((mem_size+MBYTE-1) / MBYTE), 0);
10403831d35Sstevel 		else
10503831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN, "%d Kilobytes\n"),
10603831d35Sstevel 				(int)((mem_size+KBYTE-1) / KBYTE), 0);
10703831d35Sstevel 	}
10803831d35Sstevel }
10903831d35Sstevel 
11003831d35Sstevel /*ARGSUSED0*/
11103831d35Sstevel void
112*2bcbf80cSPeter Tribble display_memoryconf(Sys_tree *tree)
11303831d35Sstevel {
11403831d35Sstevel 	/*
11503831d35Sstevel 	 * This function is intentionally blank
11603831d35Sstevel 	 */
11703831d35Sstevel }
11803831d35Sstevel 
11903831d35Sstevel /*
12003831d35Sstevel  * The following functions are for use by any US-III based systems.
12103831d35Sstevel  * All they need to do is to call get_us3_mem_regs()
12203831d35Sstevel  * and then display_us3_banks(). Each platform then needs to decide how
12303831d35Sstevel  * to format this data by over-riding the generic function
12403831d35Sstevel  * print_us3_memory_line().
12503831d35Sstevel  */
12603831d35Sstevel int
12703831d35Sstevel get_us3_mem_regs(Board_node *bnode)
12803831d35Sstevel {
12903831d35Sstevel 	Prom_node	*pnode;
13003831d35Sstevel 	int		portid;
13103831d35Sstevel 	uint64_t	*ma_reg_arr;
13203831d35Sstevel 	uint64_t	madr[NUM_MBANKS_PER_MC];
13303831d35Sstevel 	void		*bank_status_array;
13403831d35Sstevel 	char		*bank_status;
13503831d35Sstevel 	int		i, status_offset;
13603831d35Sstevel 
13703831d35Sstevel 	for (pnode = dev_find_node(bnode->nodes, "memory-controller");
13803831d35Sstevel 		pnode != NULL;
13903831d35Sstevel 		pnode = dev_next_node(pnode, "memory-controller")) {
14003831d35Sstevel 
14103831d35Sstevel 		/* Get portid of this mc from libdevinfo. */
14203831d35Sstevel 		portid = (*(int *)get_prop_val(find_prop(pnode, "portid")));
14303831d35Sstevel 
14403831d35Sstevel 		/* read the logical_bank_ma_regs property for this mc node. */
14503831d35Sstevel 		ma_reg_arr = (uint64_t *)get_prop_val(
14603831d35Sstevel 				find_prop(pnode, MEM_CFG_PROP_NAME));
14703831d35Sstevel 
14803831d35Sstevel 		/*
14903831d35Sstevel 		 * There are situations where a memory-controller node
15003831d35Sstevel 		 * will not have the logical_bank_ma_regs property and
15103831d35Sstevel 		 * we need to allow for these cases. They include:
15203831d35Sstevel 		 *	- Excalibur/Littleneck systems that only
15303831d35Sstevel 		 *	  support memory on one of their CPUs.
15403831d35Sstevel 		 *	- Systems that support DR where a cpu board
15503831d35Sstevel 		 *	  can be unconfigured but still connected.
15603831d35Sstevel 		 * It is up to the caller of this function to ensure
15703831d35Sstevel 		 * that the bank_head and seg_head pointers are not
15803831d35Sstevel 		 * NULL after processing all memory-controllers in the
15903831d35Sstevel 		 * system. This would indicate a situation where no
16003831d35Sstevel 		 * memory-controllers in the system have a logical_bank_ma_regs
16103831d35Sstevel 		 * property which should never happen.
16203831d35Sstevel 		 */
16303831d35Sstevel 		if (ma_reg_arr == NULL)
16403831d35Sstevel 			continue;
16503831d35Sstevel 
16603831d35Sstevel 		/*
16703831d35Sstevel 		 * The first NUM_MBANKS_PER_MC of uint64_t's in the
16803831d35Sstevel 		 * logical_bank_ma_regs property are the madr values.
16903831d35Sstevel 		 */
17003831d35Sstevel 		for (i = 0; i < NUM_MBANKS_PER_MC; i++) {
17103831d35Sstevel 			madr[i] = *ma_reg_arr++;
17203831d35Sstevel 		}
17303831d35Sstevel 
17403831d35Sstevel 		/*
17503831d35Sstevel 		 * Get the bank_status property for this mem controller from
17603831d35Sstevel 		 * OBP. This contains the bank-status for each logical bank.
17703831d35Sstevel 		 */
17803831d35Sstevel 		bank_status_array = (void *)get_prop_val(
17903831d35Sstevel 				find_prop(pnode, "bank-status"));
18003831d35Sstevel 		status_offset = 0;
18103831d35Sstevel 
18203831d35Sstevel 		/*
18303831d35Sstevel 		 * process each logical bank
18403831d35Sstevel 		 */
18503831d35Sstevel 		for (i = 0; i < NUM_MBANKS_PER_MC; i++) {
18603831d35Sstevel 			/*
18703831d35Sstevel 			 * Get the bank-status string for this bank
18803831d35Sstevel 			 * from the bank_status_array we just retrieved
18903831d35Sstevel 			 * from OBP. If the prop was not found, we
19003831d35Sstevel 			 * malloc a bank_status and set it to "no_status".
19103831d35Sstevel 			 */
19203831d35Sstevel 			if (bank_status_array) {
19303831d35Sstevel 				bank_status = ((char *)bank_status_array +
19403831d35Sstevel 				    status_offset);
19503831d35Sstevel 
19603831d35Sstevel 				/* Move offset to next bank_status string */
19703831d35Sstevel 				status_offset += (strlen(bank_status) + 1);
19803831d35Sstevel 			} else {
19903831d35Sstevel 				bank_status = malloc(strlen("no_status"));
20003831d35Sstevel 				strcpy(bank_status, "no_status");
20103831d35Sstevel 			}
20203831d35Sstevel 
20303831d35Sstevel 			/*
20403831d35Sstevel 			 * create a bank_node for this bank
20503831d35Sstevel 			 * and add it to the list.
20603831d35Sstevel 			 */
20703831d35Sstevel 			add_bank_node(madr[i], portid, bank_status);
20803831d35Sstevel 
20903831d35Sstevel 			/*
21003831d35Sstevel 			 * find the segment to which this bank
21103831d35Sstevel 			 * belongs. If it doesn't already exist
21203831d35Sstevel 			 * then create it. If it exists, add to it.
21303831d35Sstevel 			 */
21403831d35Sstevel 			add_seg_node();
21503831d35Sstevel 		}
21603831d35Sstevel 	}
21703831d35Sstevel 	return (0);
21803831d35Sstevel }
21903831d35Sstevel 
22003831d35Sstevel static void
22103831d35Sstevel add_bank_node(uint64_t mc_decode, int portid, char *bank_status)
22203831d35Sstevel {
22303831d35Sstevel 	static int	id = 0;
22403831d35Sstevel 	memory_bank_t	*new, *bank;
22503831d35Sstevel 	uint32_t	ifactor = MC_INTLV(mc_decode);
22603831d35Sstevel 	uint64_t	seg_size;
22703831d35Sstevel 
22803831d35Sstevel 	if ((new = malloc(sizeof (memory_bank_t))) == NULL) {
22903831d35Sstevel 		perror("malloc");
23003831d35Sstevel 		exit(1);
23103831d35Sstevel 	}
23203831d35Sstevel 
23303831d35Sstevel 	new->portid = portid;
23403831d35Sstevel 	new->id = id++;
23503831d35Sstevel 	new->valid = (mc_decode >> 63);
23603831d35Sstevel 	new->uk = MC_UK(mc_decode);
23703831d35Sstevel 	new->um = MC_UM(mc_decode);
23803831d35Sstevel 	new->lk = MC_LK(mc_decode);
23903831d35Sstevel 	new->lm = MC_LM(mc_decode);
24003831d35Sstevel 
24103831d35Sstevel 	seg_size = ((((uint64_t)new->uk & MEM_UK_SIZE_MASK) + 1) << 26);
24203831d35Sstevel 	new->bank_size = seg_size / ifactor;
24303831d35Sstevel 	new->bank_status = bank_status;
24403831d35Sstevel 
24503831d35Sstevel 	new->next = NULL;
24603831d35Sstevel 	new->seg_next = NULL;
24703831d35Sstevel 
24803831d35Sstevel 	/* Handle the first bank found */
24903831d35Sstevel 	if (bank_head == NULL) {
25003831d35Sstevel 		bank_head = new;
25103831d35Sstevel 		bank_tail = new;
25203831d35Sstevel 		return;
25303831d35Sstevel 	}
25403831d35Sstevel 
25503831d35Sstevel 	/* find last bank in list */
25603831d35Sstevel 	bank = bank_head;
25703831d35Sstevel 	while (bank->next)
25803831d35Sstevel 		bank = bank->next;
25903831d35Sstevel 
26003831d35Sstevel 	/* insert this bank into the list */
26103831d35Sstevel 	bank->next = new;
26203831d35Sstevel 	bank_tail = new;
26303831d35Sstevel }
26403831d35Sstevel 
26503831d35Sstevel void
26603831d35Sstevel display_us3_banks(void)
26703831d35Sstevel {
26803831d35Sstevel 	uint64_t	base, bank_size;
26903831d35Sstevel 	uint32_t	intlv;
27003831d35Sstevel 	memory_bank_t	*bank, *tmp_bank;
27103831d35Sstevel 	memory_seg_t	*seg;
27203831d35Sstevel 	int		 mcid;
27303831d35Sstevel 	uint64_t	dimm_size;
27403831d35Sstevel 	uint64_t	total_bank_size = 0;
27503831d35Sstevel 	uint64_t	total_sys_mem;
27603831d35Sstevel 	static uint64_t	bank0_size, bank1_size, bank2_size, bank3_size;
27703831d35Sstevel 
27803831d35Sstevel 	if ((bank_head == NULL) || (seg_head == NULL)) {
27903831d35Sstevel 		log_printf("\nCannot find any memory bank/segment info.\n");
28003831d35Sstevel 		return;
28103831d35Sstevel 	}
28203831d35Sstevel 
28303831d35Sstevel 	for (bank = bank_head; bank; bank = bank->next) {
28403831d35Sstevel 		/*
28503831d35Sstevel 		 * Interleave factor is determined from the
28603831d35Sstevel 		 * lk bits in the Mem Addr Decode register.
28703831d35Sstevel 		 *
28803831d35Sstevel 		 * The Base Address of the memory segment in which this
28903831d35Sstevel 		 * bank belongs is determined from the um abd uk bits
29003831d35Sstevel 		 * of the Mem Addr Decode register.
29103831d35Sstevel 		 *
29203831d35Sstevel 		 * See section 9.1.5 of Cheetah Programmer's reference
29303831d35Sstevel 		 * manual.
29403831d35Sstevel 		 */
29503831d35Sstevel 		intlv 		= ((bank->lk ^ 0xF) + 1);
29603831d35Sstevel 		base 		= bank->um & ~(bank->uk);
29703831d35Sstevel 
29803831d35Sstevel 		mcid 		= SG_PORTID_TO_SAFARI_ID(bank->portid);
29903831d35Sstevel 
30003831d35Sstevel 		/* If bank is not valid, set size to zero incase it's garbage */
30103831d35Sstevel 		if (bank->valid)
30203831d35Sstevel 			bank_size = ((bank->bank_size) / MBYTE);
30303831d35Sstevel 		else
30403831d35Sstevel 			bank_size = 0;
30503831d35Sstevel 
30603831d35Sstevel 		/*
30703831d35Sstevel 		 * Keep track of all banks found so we can check later
30803831d35Sstevel 		 * that this value matches the total memory in the
30903831d35Sstevel 		 * system using the pagesize and number of pages.
31003831d35Sstevel 		 */
31103831d35Sstevel 		total_bank_size	+= bank_size;
31203831d35Sstevel 
31303831d35Sstevel 		/* Find the matching segment for this bank. */
31403831d35Sstevel 		seg = match_seg(base);
31503831d35Sstevel 
31603831d35Sstevel 		/*
31703831d35Sstevel 		 * Find the Dimm size by adding banks 0 + 2 and divide by 4
31803831d35Sstevel 		 * and then adding banks 1 + 3 and divide by 4. We divide
31903831d35Sstevel 		 * by 2 if one of the logical banks size is zero.
32003831d35Sstevel 		 */
32103831d35Sstevel 		switch ((bank->id) % 4) {
32203831d35Sstevel 		case 0:
32303831d35Sstevel 			/* have bank0_size, need bank2_size */
32403831d35Sstevel 			bank0_size = bank_size;
32503831d35Sstevel 			bank2_size = 0;
32603831d35Sstevel 
32703831d35Sstevel 			tmp_bank = bank->next;
32803831d35Sstevel 			while (tmp_bank) {
32903831d35Sstevel 				if (tmp_bank->valid == 0) {
33003831d35Sstevel 					tmp_bank = tmp_bank->next;
33103831d35Sstevel 					continue;
33203831d35Sstevel 				}
33303831d35Sstevel 				/* Is next bank on the same mc ? */
33403831d35Sstevel 				if (mcid != SG_PORTID_TO_SAFARI_ID(
33503831d35Sstevel 				    tmp_bank->portid)) {
33603831d35Sstevel 					break;
33703831d35Sstevel 				}
33803831d35Sstevel 				if ((tmp_bank->id) % 4 == 2) {
33903831d35Sstevel 					bank2_size =
34003831d35Sstevel 					    (tmp_bank->bank_size / MBYTE);
34103831d35Sstevel 					break;
34203831d35Sstevel 				}
34303831d35Sstevel 				tmp_bank = tmp_bank->next;
34403831d35Sstevel 			}
34503831d35Sstevel 			if (bank2_size)
34603831d35Sstevel 				dimm_size = (bank0_size + bank2_size) / 4;
34703831d35Sstevel 			else
34803831d35Sstevel 				dimm_size = bank0_size / 2;
34903831d35Sstevel 			break;
35003831d35Sstevel 		case 1:
35103831d35Sstevel 			/* have bank1_size, need bank3_size */
35203831d35Sstevel 			bank1_size = bank_size;
35303831d35Sstevel 			bank3_size = 0;
35403831d35Sstevel 
35503831d35Sstevel 			tmp_bank = bank->next;
35603831d35Sstevel 			while (tmp_bank) {
35703831d35Sstevel 				if (tmp_bank->valid == 0) {
35803831d35Sstevel 					tmp_bank = tmp_bank->next;
35903831d35Sstevel 					continue;
36003831d35Sstevel 				}
36103831d35Sstevel 				/* Is next bank on the same mc ? */
36203831d35Sstevel 				if (mcid != SG_PORTID_TO_SAFARI_ID(
36303831d35Sstevel 				    tmp_bank->portid)) {
36403831d35Sstevel 					break;
36503831d35Sstevel 				}
36603831d35Sstevel 				if ((tmp_bank->id) % 4 == 3) {
36703831d35Sstevel 					bank3_size =
36803831d35Sstevel 					    (tmp_bank->bank_size / MBYTE);
36903831d35Sstevel 					break;
37003831d35Sstevel 				}
37103831d35Sstevel 				tmp_bank = tmp_bank->next;
37203831d35Sstevel 			}
37303831d35Sstevel 			if (bank3_size)
37403831d35Sstevel 				dimm_size = (bank1_size + bank3_size) / 4;
37503831d35Sstevel 			else
37603831d35Sstevel 				dimm_size = bank1_size / 2;
37703831d35Sstevel 			break;
37803831d35Sstevel 		case 2:
37903831d35Sstevel 			/* have bank0_size and bank2_size */
38003831d35Sstevel 			bank2_size = bank_size;
38103831d35Sstevel 			if (bank0_size)
38203831d35Sstevel 				dimm_size = (bank0_size + bank2_size) / 4;
38303831d35Sstevel 			else
38403831d35Sstevel 				dimm_size = bank2_size / 2;
38503831d35Sstevel 			break;
38603831d35Sstevel 		case 3:
38703831d35Sstevel 			/* have bank1_size and bank3_size */
38803831d35Sstevel 			bank3_size = bank_size;
38903831d35Sstevel 			if (bank1_size)
39003831d35Sstevel 				dimm_size = (bank1_size + bank3_size) / 4;
39103831d35Sstevel 			else
39203831d35Sstevel 				dimm_size = bank3_size / 4;
39303831d35Sstevel 			break;
39403831d35Sstevel 		}
39503831d35Sstevel 
39603831d35Sstevel 		if (bank->valid == 0)
39703831d35Sstevel 			continue;
39803831d35Sstevel 
39903831d35Sstevel 		/*
40003831d35Sstevel 		 * Call platform specific code for formatting memory
40103831d35Sstevel 		 * information.
40203831d35Sstevel 		 */
40303831d35Sstevel 		print_us3_memory_line(bank->portid, bank->id, bank_size,
40403831d35Sstevel 		    bank->bank_status, dimm_size, intlv, seg->id);
40503831d35Sstevel 	}
40603831d35Sstevel 
40703831d35Sstevel 	printf("\n");
40803831d35Sstevel 
40903831d35Sstevel 	/*
41003831d35Sstevel 	 * Sanity check to ensure that the total amount of system
41103831d35Sstevel 	 * memory matches the total number of memory banks that
41203831d35Sstevel 	 * we find here. Scream if there is a mis-match.
41303831d35Sstevel 	 */
41403831d35Sstevel 	total_sys_mem = (((uint64_t)sysconf(_SC_PAGESIZE) * \
41503831d35Sstevel 		(uint64_t)sysconf(_SC_PHYS_PAGES)) / MBYTE);
41603831d35Sstevel 
41703831d35Sstevel 	if (total_bank_size != total_sys_mem) {
41803831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN,
41903831d35Sstevel 		    "\nError: total bank size [%lldMB] does not match total "
42003831d35Sstevel 			"system memory [%lldMB]\n"), total_bank_size,
42103831d35Sstevel 				total_sys_mem, 0);
42203831d35Sstevel 	}
42303831d35Sstevel 
42403831d35Sstevel }
42503831d35Sstevel 
42603831d35Sstevel static void
42703831d35Sstevel add_seg_node(void)
42803831d35Sstevel {
42903831d35Sstevel 	uint64_t	base;
43003831d35Sstevel 	memory_seg_t	*new;
43103831d35Sstevel 	static int	id = 0;
43203831d35Sstevel 	memory_bank_t	*bank = bank_tail;
43303831d35Sstevel 
43403831d35Sstevel 	if (bank->valid != 1)
43503831d35Sstevel 		return;
43603831d35Sstevel 
43703831d35Sstevel 	base = bank->um & ~(bank->uk);
43803831d35Sstevel 
43903831d35Sstevel 	if ((new = match_seg(base)) == NULL) {
44003831d35Sstevel 		/*
44103831d35Sstevel 		 * This bank is part of a new segment, so create
44203831d35Sstevel 		 * a struct for it and added to the list of segments
44303831d35Sstevel 		 */
44403831d35Sstevel 		if ((new = malloc(sizeof (memory_seg_t))) == NULL) {
44503831d35Sstevel 			perror("malloc");
44603831d35Sstevel 			exit(1);
44703831d35Sstevel 		}
44803831d35Sstevel 		new->id = id++;
44903831d35Sstevel 		new->base = base;
45003831d35Sstevel 		new->size = (((uint64_t)bank->uk +1) << 26);
45103831d35Sstevel 		new->intlv = ((bank->lk ^ 0xF) + 1);
45203831d35Sstevel 
45303831d35Sstevel 		/*
45403831d35Sstevel 		 * add to the seg list
45503831d35Sstevel 		 */
45603831d35Sstevel 		new->next = seg_head;
45703831d35Sstevel 		seg_head = new;
45803831d35Sstevel 	}
45903831d35Sstevel 
46003831d35Sstevel 	new->nbanks++;
46103831d35Sstevel 	/*
46203831d35Sstevel 	 * add bank into segs bank list.  Note we add at the head
46303831d35Sstevel 	 */
46403831d35Sstevel 	bank->seg_next = new->banks;
46503831d35Sstevel 	new->banks = bank;
46603831d35Sstevel }
46703831d35Sstevel 
46803831d35Sstevel static memory_seg_t *
46903831d35Sstevel match_seg(uint64_t base)
47003831d35Sstevel {
47103831d35Sstevel 	memory_seg_t	*cur_seg;
47203831d35Sstevel 
47303831d35Sstevel 	for (cur_seg = seg_head; cur_seg; cur_seg = cur_seg->next) {
47403831d35Sstevel 		if (cur_seg-> base == base)
47503831d35Sstevel 			break;
47603831d35Sstevel 	}
47703831d35Sstevel 	return (cur_seg);
47803831d35Sstevel }
47903831d35Sstevel 
48003831d35Sstevel /*ARGSUSED0*/
48103831d35Sstevel void
48203831d35Sstevel print_us3_memory_line(int portid, int bank_id, uint64_t bank_size,
48303831d35Sstevel     char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id)
48403831d35Sstevel {
48503831d35Sstevel 	log_printf(dgettext(TEXT_DOMAIN,
48603831d35Sstevel 	    "\n No print_us3_memory_line() function specified for"
48703831d35Sstevel 	    " this platform\n"), 0);
48803831d35Sstevel }
48903831d35Sstevel 
49003831d35Sstevel int
49103831d35Sstevel display_us3_failed_banks(int system_failed)
49203831d35Sstevel {
49303831d35Sstevel 	memory_bank_t	*bank;
49403831d35Sstevel 	int		found_failed_bank = 0;
49503831d35Sstevel 
49603831d35Sstevel 	if ((bank_head == NULL) || (seg_head == NULL)) {
49703831d35Sstevel 		log_printf("\nCannot find any memory bank/segment info.\n");
49803831d35Sstevel 		return (1);
49903831d35Sstevel 	}
50003831d35Sstevel 
50103831d35Sstevel 	for (bank = bank_head; bank; bank = bank->next) {
50203831d35Sstevel 		/*
50303831d35Sstevel 		 * check to see if the bank is invalid and also
50403831d35Sstevel 		 * check if the bank_status is unpopulated.  Unpopulated
50503831d35Sstevel 		 * means the bank is empty.
50603831d35Sstevel 		 */
50703831d35Sstevel 
50803831d35Sstevel 		if ((bank->valid == 0) &&
50903831d35Sstevel 		    (strcmp(bank->bank_status, "unpopulated"))) {
51003831d35Sstevel 			if (!system_failed && !found_failed_bank) {
51103831d35Sstevel 				found_failed_bank = TRUE;
51203831d35Sstevel 				log_printf("\n", 0);
51303831d35Sstevel 				log_printf(dgettext(TEXT_DOMAIN,
51403831d35Sstevel 				"Failed Field Replaceable Units (FRU) in "
51503831d35Sstevel 				    "System:\n"), 0);
51603831d35Sstevel 				log_printf("=========================="
51703831d35Sstevel 				    "====================\n", 0);
51803831d35Sstevel 			}
51903831d35Sstevel 			/*
52003831d35Sstevel 			 * Call platform specific code for formatting memory
52103831d35Sstevel 			 * information.
52203831d35Sstevel 			 */
52303831d35Sstevel 			print_us3_failed_memory_line(bank->portid, bank->id,
52403831d35Sstevel 			    bank->bank_status);
52503831d35Sstevel 		}
52603831d35Sstevel 	}
52703831d35Sstevel 	if (found_failed_bank)
52803831d35Sstevel 		return (1);
52903831d35Sstevel 	else
53003831d35Sstevel 		return (0);
53103831d35Sstevel }
53203831d35Sstevel 
53303831d35Sstevel /*ARGSUSED0*/
53403831d35Sstevel void
53503831d35Sstevel print_us3_failed_memory_line(int portid, int bank_id, char *bank_status)
53603831d35Sstevel {
53703831d35Sstevel 	log_printf(dgettext(TEXT_DOMAIN,
53803831d35Sstevel 	    "\n No print_us3_failed_memory_line() function specified for"
53903831d35Sstevel 	    " this platform\n"), 0);
54003831d35Sstevel }
541