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.
252bcbf80cSPeter Tribble  * Copyright (c) 2020 Peter Tribble.
2603831d35Sstevel  */
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"
5803831d35Sstevel #if !defined(TEXT_DOMAIN)
5903831d35Sstevel #define	TEXT_DOMAIN	"SYS_TEST"
6003831d35Sstevel #endif
6203831d35Sstevel #define	KBYTE	1024
6303831d35Sstevel #define	MBYTE	(KBYTE * KBYTE)
6503831d35Sstevel #define	MEM_UK_SIZE_MASK	0x3FF
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;
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);
8203831d35Sstevel /*
8303831d35Sstevel  * Used for US-I and US-II systems
8403831d35Sstevel  */
8503831d35Sstevel /*ARGSUSED0*/
8603831d35Sstevel void
display_memorysize(Sys_tree * tree,struct system_kstat_data * kstats,struct mem_total * memory_total)8703831d35Sstevel display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats,
88*6fb59094SToomas Soome     struct mem_total *memory_total)
8903831d35Sstevel {
9003831d35Sstevel 	log_printf(dgettext(TEXT_DOMAIN, "Memory size: "), 0);
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;
9703831d35Sstevel 		mem_size =
9803831d35Sstevel 		    (uint64_t)sysconf(_SC_PAGESIZE) * \
99*6fb59094SToomas Soome 		    (uint64_t)sysconf(_SC_PHYS_PAGES);
10103831d35Sstevel 		if (mem_size >= MBYTE)
10203831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN, "%d Megabytes\n"),
103*6fb59094SToomas Soome 			    (int)((mem_size+MBYTE-1) / MBYTE), 0);
10403831d35Sstevel 		else
10503831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN, "%d Kilobytes\n"),
106*6fb59094SToomas Soome 			    (int)((mem_size+KBYTE-1) / KBYTE), 0);
10703831d35Sstevel 	}
10803831d35Sstevel }
11003831d35Sstevel /*ARGSUSED0*/
11103831d35Sstevel void
display_memoryconf(Sys_tree * tree)1122bcbf80cSPeter Tribble display_memoryconf(Sys_tree *tree)
11303831d35Sstevel {
11403831d35Sstevel 	/*
11503831d35Sstevel 	 * This function is intentionally blank
11603831d35Sstevel 	 */
11703831d35Sstevel }
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
get_us3_mem_regs(Board_node * bnode)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;
13703831d35Sstevel 	for (pnode = dev_find_node(bnode->nodes, "memory-controller");
138*6fb59094SToomas Soome 	    pnode != NULL;
139*6fb59094SToomas Soome 	    pnode = dev_next_node(pnode, "memory-controller")) {
14103831d35Sstevel 		/* Get portid of this mc from libdevinfo. */
14203831d35Sstevel 		portid = (*(int *)get_prop_val(find_prop(pnode, "portid")));
14403831d35Sstevel 		/* read the logical_bank_ma_regs property for this mc node. */
14503831d35Sstevel 		ma_reg_arr = (uint64_t *)get_prop_val(
146*6fb59094SToomas Soome 		    find_prop(pnode, MEM_CFG_PROP_NAME));
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;
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 		}
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(
179*6fb59094SToomas Soome 		    find_prop(pnode, "bank-status"));
18003831d35Sstevel 		status_offset = 0;
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);
19603831d35Sstevel 				/* Move offset to next bank_status string */
19703831d35Sstevel 				status_offset += (strlen(bank_status) + 1);
19803831d35Sstevel 			} else {
199*6fb59094SToomas Soome 				bank_status = strdup("no_status");
20003831d35Sstevel 			}
20203831d35Sstevel 			/*
20303831d35Sstevel 			 * create a bank_node for this bank
20403831d35Sstevel 			 * and add it to the list.
20503831d35Sstevel 			 */
20603831d35Sstevel 			add_bank_node(madr[i], portid, bank_status);
20803831d35Sstevel 			/*
20903831d35Sstevel 			 * find the segment to which this bank
21003831d35Sstevel 			 * belongs. If it doesn't already exist
21103831d35Sstevel 			 * then create it. If it exists, add to it.
21203831d35Sstevel 			 */
21303831d35Sstevel 			add_seg_node();
21403831d35Sstevel 		}
21503831d35Sstevel 	}
21603831d35Sstevel 	return (0);
21703831d35Sstevel }
21903831d35Sstevel static void
add_bank_node(uint64_t mc_decode,int portid,char * bank_status)22003831d35Sstevel add_bank_node(uint64_t mc_decode, int portid, char *bank_status)
22103831d35Sstevel {
22203831d35Sstevel 	static int	id = 0;
22303831d35Sstevel 	memory_bank_t	*new, *bank;
22403831d35Sstevel 	uint32_t	ifactor = MC_INTLV(mc_decode);
22503831d35Sstevel 	uint64_t	seg_size;
22703831d35Sstevel 	if ((new = malloc(sizeof (memory_bank_t))) == NULL) {
22803831d35Sstevel 		perror("malloc");
22903831d35Sstevel 		exit(1);
23003831d35Sstevel 	}
23203831d35Sstevel 	new->portid = portid;
23303831d35Sstevel 	new->id = id++;
23403831d35Sstevel 	new->valid = (mc_decode >> 63);
23503831d35Sstevel 	new->uk = MC_UK(mc_decode);
23603831d35Sstevel 	new->um = MC_UM(mc_decode);
23703831d35Sstevel 	new->lk = MC_LK(mc_decode);
23803831d35Sstevel 	new->lm = MC_LM(mc_decode);
24003831d35Sstevel 	seg_size = ((((uint64_t)new->uk & MEM_UK_SIZE_MASK) + 1) << 26);
24103831d35Sstevel 	new->bank_size = seg_size / ifactor;
24203831d35Sstevel 	new->bank_status = bank_status;
24403831d35Sstevel 	new->next = NULL;
24503831d35Sstevel 	new->seg_next = NULL;
24703831d35Sstevel 	/* Handle the first bank found */
24803831d35Sstevel 	if (bank_head == NULL) {
24903831d35Sstevel 		bank_head = new;
25003831d35Sstevel 		bank_tail = new;
25103831d35Sstevel 		return;
25203831d35Sstevel 	}
25403831d35Sstevel 	/* find last bank in list */
25503831d35Sstevel 	bank = bank_head;
25603831d35Sstevel 	while (bank->next)
25703831d35Sstevel 		bank = bank->next;
25903831d35Sstevel 	/* insert this bank into the list */
26003831d35Sstevel 	bank->next = new;
26103831d35Sstevel 	bank_tail = new;
26203831d35Sstevel }
26403831d35Sstevel void
display_us3_banks(void)26503831d35Sstevel display_us3_banks(void)
26603831d35Sstevel {
26703831d35Sstevel 	uint64_t	base, bank_size;
26803831d35Sstevel 	uint32_t	intlv;
26903831d35Sstevel 	memory_bank_t	*bank, *tmp_bank;
27003831d35Sstevel 	memory_seg_t	*seg;
27103831d35Sstevel 	int		 mcid;
27203831d35Sstevel 	uint64_t	dimm_size;
27303831d35Sstevel 	uint64_t	total_bank_size = 0;
27403831d35Sstevel 	uint64_t	total_sys_mem;
27503831d35Sstevel 	static uint64_t	bank0_size, bank1_size, bank2_size, bank3_size;
27703831d35Sstevel 	if ((bank_head == NULL) || (seg_head == NULL)) {
27803831d35Sstevel 		log_printf("\nCannot find any memory bank/segment info.\n");
27903831d35Sstevel 		return;
28003831d35Sstevel 	}