Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
     24  * All rights reserved.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <unistd.h>
     32 #include <ctype.h>
     33 #include <string.h>
     34 #include <kvm.h>
     35 #include <varargs.h>
     36 #include <errno.h>
     37 #include <time.h>
     38 #include <dirent.h>
     39 #include <fcntl.h>
     40 #include <sys/param.h>
     41 #include <sys/stat.h>
     42 #include <sys/types.h>
     43 #include <sys/utsname.h>
     44 #include <sys/openpromio.h>
     45 #include <kstat.h>
     46 #include <libintl.h>
     47 #include <syslog.h>
     48 #include <sys/dkio.h>
     49 #include <sys/sbd_ioctl.h>
     50 #include <sys/sbdp_mem.h>
     51 #include <sys/serengeti.h>
     52 #include <sys/mc.h>
     53 #include "pdevinfo.h"
     54 #include "display.h"
     55 #include "pdevinfo_sun4u.h"
     56 #include "display_sun4u.h"
     57 #include "libprtdiag.h"
     58 
     59 #if !defined(TEXT_DOMAIN)
     60 #define	TEXT_DOMAIN	"SYS_TEST"
     61 #endif
     62 
     63 #define	KBYTE	1024
     64 #define	MBYTE	(KBYTE * KBYTE)
     65 
     66 #define	MEM_UK_SIZE_MASK	0x3FF
     67 
     68 /*
     69  * Global variables.
     70  */
     71 static memory_bank_t	*bank_head;
     72 static memory_bank_t	*bank_tail;
     73 static memory_seg_t	*seg_head;
     74 
     75 /*
     76  * Local functions.
     77  */
     78 static void add_bank_node(uint64_t mc_decode, int portid, char *bank_status);
     79 static void add_seg_node(void);
     80 static memory_seg_t *match_seg(uint64_t);
     81 
     82 
     83 /*
     84  * Used for US-I and US-II systems
     85  */
     86 /*ARGSUSED0*/
     87 void
     88 display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats,
     89 	struct grp_info *grps, struct mem_total *memory_total)
     90 {
     91 	log_printf(dgettext(TEXT_DOMAIN, "Memory size: "), 0);
     92 
     93 	if (sysconf(_SC_PAGESIZE) == -1 || sysconf(_SC_PHYS_PAGES) == -1)
     94 		log_printf(dgettext(TEXT_DOMAIN, "unable to determine\n"), 0);
     95 	else {
     96 		uint64_t	mem_size;
     97 
     98 		mem_size =
     99 		    (uint64_t)sysconf(_SC_PAGESIZE) * \
    100 			(uint64_t)sysconf(_SC_PHYS_PAGES);
    101 
    102 		if (mem_size >= MBYTE)
    103 			log_printf(dgettext(TEXT_DOMAIN, "%d Megabytes\n"),
    104 				(int)((mem_size+MBYTE-1) / MBYTE), 0);
    105 		else
    106 			log_printf(dgettext(TEXT_DOMAIN, "%d Kilobytes\n"),
    107 				(int)((mem_size+KBYTE-1) / KBYTE), 0);
    108 	}
    109 }
    110 
    111 /*ARGSUSED0*/
    112 void
    113 display_memoryconf(Sys_tree *tree, struct grp_info *grps)
    114 {
    115 	/*
    116 	 * This function is intentionally blank
    117 	 */
    118 }
    119 
    120 /*
    121  * The following functions are for use by any US-III based systems.
    122  * All they need to do is to call get_us3_mem_regs()
    123  * and then display_us3_banks(). Each platform then needs to decide how
    124  * to format this data by over-riding the generic function
    125  * print_us3_memory_line().
    126  */
    127 int
    128 get_us3_mem_regs(Board_node *bnode)
    129 {
    130 	Prom_node	*pnode;
    131 	int		portid;
    132 	uint64_t	*ma_reg_arr;
    133 	uint64_t	madr[NUM_MBANKS_PER_MC];
    134 	void		*bank_status_array;
    135 	char		*bank_status;
    136 	int		i, status_offset;
    137 
    138 	for (pnode = dev_find_node(bnode->nodes, "memory-controller");
    139 		pnode != NULL;
    140 		pnode = dev_next_node(pnode, "memory-controller")) {
    141 
    142 		/* Get portid of this mc from libdevinfo. */
    143 		portid = (*(int *)get_prop_val(find_prop(pnode, "portid")));
    144 
    145 		/* read the logical_bank_ma_regs property for this mc node. */
    146 		ma_reg_arr = (uint64_t *)get_prop_val(
    147 				find_prop(pnode, MEM_CFG_PROP_NAME));
    148 
    149 		/*
    150 		 * There are situations where a memory-controller node
    151 		 * will not have the logical_bank_ma_regs property and
    152 		 * we need to allow for these cases. They include:
    153 		 *	- Excalibur/Littleneck systems that only
    154 		 *	  support memory on one of their CPUs.
    155 		 *	- Systems that support DR where a cpu board
    156 		 *	  can be unconfigured but still connected.
    157 		 * It is up to the caller of this function to ensure
    158 		 * that the bank_head and seg_head pointers are not
    159 		 * NULL after processing all memory-controllers in the
    160 		 * system. This would indicate a situation where no
    161 		 * memory-controllers in the system have a logical_bank_ma_regs
    162 		 * property which should never happen.
    163 		 */
    164 		if (ma_reg_arr == NULL)
    165 			continue;
    166 
    167 		/*
    168 		 * The first NUM_MBANKS_PER_MC of uint64_t's in the
    169 		 * logical_bank_ma_regs property are the madr values.
    170 		 */
    171 		for (i = 0; i < NUM_MBANKS_PER_MC; i++) {
    172 			madr[i] = *ma_reg_arr++;
    173 		}
    174 
    175 		/*
    176 		 * Get the bank_status property for this mem controller from
    177 		 * OBP. This contains the bank-status for each logical bank.
    178 		 */
    179 		bank_status_array = (void *)get_prop_val(
    180 				find_prop(pnode, "bank-status"));
    181 		status_offset = 0;
    182 
    183 		/*
    184 		 * process each logical bank
    185 		 */
    186 		for (i = 0; i < NUM_MBANKS_PER_MC; i++) {
    187 			/*
    188 			 * Get the bank-status string for this bank
    189 			 * from the bank_status_array we just retrieved
    190 			 * from OBP. If the prop was not found, we
    191 			 * malloc a bank_status and set it to "no_status".
    192 			 */
    193 			if (bank_status_array) {
    194 				bank_status = ((char *)bank_status_array +
    195 				    status_offset);
    196 
    197 				/* Move offset to next bank_status string */
    198 				status_offset += (strlen(bank_status) + 1);
    199 			} else {
    200 				bank_status = malloc(strlen("no_status"));
    201 				strcpy(bank_status, "no_status");
    202 			}
    203 
    204 			/*
    205 			 * create a bank_node for this bank
    206 			 * and add it to the list.
    207 			 */
    208 			add_bank_node(madr[i], portid, bank_status);
    209 
    210 			/*
    211 			 * find the segment to which this bank
    212 			 * belongs. If it doesn't already exist
    213 			 * then create it. If it exists, add to it.
    214 			 */
    215 			add_seg_node();
    216 		}
    217 	}
    218 	return (0);
    219 }
    220 
    221 static void
    222 add_bank_node(uint64_t mc_decode, int portid, char *bank_status)
    223 {
    224 	static int	id = 0;
    225 	memory_bank_t	*new, *bank;
    226 	uint32_t	ifactor = MC_INTLV(mc_decode);
    227 	uint64_t	seg_size;
    228 
    229 	if ((new = malloc(sizeof (memory_bank_t))) == NULL) {
    230 		perror("malloc");
    231 		exit(1);
    232 	}
    233 
    234 	new->portid = portid;
    235 	new->id = id++;
    236 	new->valid = (mc_decode >> 63);
    237 	new->uk = MC_UK(mc_decode);
    238 	new->um = MC_UM(mc_decode);
    239 	new->lk = MC_LK(mc_decode);
    240 	new->lm = MC_LM(mc_decode);
    241 
    242 	seg_size = ((((uint64_t)new->uk & MEM_UK_SIZE_MASK) + 1) << 26);
    243 	new->bank_size = seg_size / ifactor;
    244 	new->bank_status = bank_status;
    245 
    246 	new->next = NULL;
    247 	new->seg_next = NULL;
    248 
    249 	/* Handle the first bank found */
    250 	if (bank_head == NULL) {
    251 		bank_head = new;
    252 		bank_tail = new;
    253 		return;
    254 	}
    255 
    256 	/* find last bank in list */
    257 	bank = bank_head;
    258 	while (bank->next)
    259 		bank = bank->next;
    260 
    261 	/* insert this bank into the list */
    262 	bank->next = new;
    263 	bank_tail = new;
    264 }
    265 
    266 void
    267 display_us3_banks(void)
    268 {
    269 	uint64_t	base, bank_size;
    270 	uint32_t	intlv;
    271 	memory_bank_t	*bank, *tmp_bank;
    272 	memory_seg_t	*seg;
    273 	int		 mcid;
    274 	uint64_t	dimm_size;
    275 	uint64_t	total_bank_size = 0;
    276 	uint64_t	total_sys_mem;
    277 	static uint64_t	bank0_size, bank1_size, bank2_size, bank3_size;
    278 
    279 	if ((bank_head == NULL) || (seg_head == NULL)) {
    280 		log_printf("\nCannot find any memory bank/segment info.\n");
    281 		return;
    282 	}
    283 
    284 	for (bank = bank_head; bank; bank = bank->next) {
    285 		/*
    286 		 * Interleave factor is determined from the
    287 		 * lk bits in the Mem Addr Decode register.
    288 		 *
    289 		 * The Base Address of the memory segment in which this
    290 		 * bank belongs is determined from the um abd uk bits
    291 		 * of the Mem Addr Decode register.
    292 		 *
    293 		 * See section 9.1.5 of Cheetah Programmer's reference
    294 		 * manual.
    295 		 */
    296 		intlv 		= ((bank->lk ^ 0xF) + 1);
    297 		base 		= bank->um & ~(bank->uk);
    298 
    299 		mcid 		= SG_PORTID_TO_SAFARI_ID(bank->portid);
    300 
    301 		/* If bank is not valid, set size to zero incase it's garbage */
    302 		if (bank->valid)
    303 			bank_size = ((bank->bank_size) / MBYTE);
    304 		else
    305 			bank_size = 0;
    306 
    307 		/*
    308 		 * Keep track of all banks found so we can check later
    309 		 * that this value matches the total memory in the
    310 		 * system using the pagesize and number of pages.
    311 		 */
    312 		total_bank_size	+= bank_size;
    313 
    314 		/* Find the matching segment for this bank. */
    315 		seg = match_seg(base);
    316 
    317 		/*
    318 		 * Find the Dimm size by adding banks 0 + 2 and divide by 4
    319 		 * and then adding banks 1 + 3 and divide by 4. We divide
    320 		 * by 2 if one of the logical banks size is zero.
    321 		 */
    322 		switch ((bank->id) % 4) {
    323 		case 0:
    324 			/* have bank0_size, need bank2_size */
    325 			bank0_size = bank_size;
    326 			bank2_size = 0;
    327 
    328 			tmp_bank = bank->next;
    329 			while (tmp_bank) {
    330 				if (tmp_bank->valid == 0) {
    331 					tmp_bank = tmp_bank->next;
    332 					continue;
    333 				}
    334 				/* Is next bank on the same mc ? */
    335 				if (mcid != SG_PORTID_TO_SAFARI_ID(
    336 				    tmp_bank->portid)) {
    337 					break;
    338 				}
    339 				if ((tmp_bank->id) % 4 == 2) {
    340 					bank2_size =
    341 					    (tmp_bank->bank_size / MBYTE);
    342 					break;
    343 				}
    344 				tmp_bank = tmp_bank->next;
    345 			}
    346 			if (bank2_size)
    347 				dimm_size = (bank0_size + bank2_size) / 4;
    348 			else
    349 				dimm_size = bank0_size / 2;
    350 			break;
    351 		case 1:
    352 			/* have bank1_size, need bank3_size */
    353 			bank1_size = bank_size;
    354 			bank3_size = 0;
    355 
    356 			tmp_bank = bank->next;
    357 			while (tmp_bank) {
    358 				if (tmp_bank->valid == 0) {
    359 					tmp_bank = tmp_bank->next;
    360 					continue;
    361 				}
    362 				/* Is next bank on the same mc ? */
    363 				if (mcid != SG_PORTID_TO_SAFARI_ID(
    364 				    tmp_bank->portid)) {
    365 					break;
    366 				}
    367 				if ((tmp_bank->id) % 4 == 3) {
    368 					bank3_size =
    369 					    (tmp_bank->bank_size / MBYTE);
    370 					break;
    371 				}
    372 				tmp_bank = tmp_bank->next;
    373 			}
    374 			if (bank3_size)
    375 				dimm_size = (bank1_size + bank3_size) / 4;
    376 			else
    377 				dimm_size = bank1_size / 2;
    378 			break;
    379 		case 2:
    380 			/* have bank0_size and bank2_size */
    381 			bank2_size = bank_size;
    382 			if (bank0_size)
    383 				dimm_size = (bank0_size + bank2_size) / 4;
    384 			else
    385 				dimm_size = bank2_size / 2;
    386 			break;
    387 		case 3:
    388 			/* have bank1_size and bank3_size */
    389 			bank3_size = bank_size;
    390 			if (bank1_size)
    391 				dimm_size = (bank1_size + bank3_size) / 4;
    392 			else
    393 				dimm_size = bank3_size / 4;
    394 			break;
    395 		}
    396 
    397 		if (bank->valid == 0)
    398 			continue;
    399 
    400 		/*
    401 		 * Call platform specific code for formatting memory
    402 		 * information.
    403 		 */
    404 		print_us3_memory_line(bank->portid, bank->id, bank_size,
    405 		    bank->bank_status, dimm_size, intlv, seg->id);
    406 	}
    407 
    408 	printf("\n");
    409 
    410 	/*
    411 	 * Sanity check to ensure that the total amount of system
    412 	 * memory matches the total number of memory banks that
    413 	 * we find here. Scream if there is a mis-match.
    414 	 */
    415 	total_sys_mem = (((uint64_t)sysconf(_SC_PAGESIZE) * \
    416 		(uint64_t)sysconf(_SC_PHYS_PAGES)) / MBYTE);
    417 
    418 	if (total_bank_size != total_sys_mem) {
    419 		log_printf(dgettext(TEXT_DOMAIN,
    420 		    "\nError: total bank size [%lldMB] does not match total "
    421 			"system memory [%lldMB]\n"), total_bank_size,
    422 				total_sys_mem, 0);
    423 	}
    424 
    425 }
    426 
    427 static void
    428 add_seg_node(void)
    429 {
    430 	uint64_t	base;
    431 	memory_seg_t	*new;
    432 	static int	id = 0;
    433 	memory_bank_t	*bank = bank_tail;
    434 
    435 	if (bank->valid != 1)
    436 		return;
    437 
    438 	base = bank->um & ~(bank->uk);
    439 
    440 	if ((new = match_seg(base)) == NULL) {
    441 		/*
    442 		 * This bank is part of a new segment, so create
    443 		 * a struct for it and added to the list of segments
    444 		 */
    445 		if ((new = malloc(sizeof (memory_seg_t))) == NULL) {
    446 			perror("malloc");
    447 			exit(1);
    448 		}
    449 		new->id = id++;
    450 		new->base = base;
    451 		new->size = (((uint64_t)bank->uk +1) << 26);
    452 		new->intlv = ((bank->lk ^ 0xF) + 1);
    453 
    454 		/*
    455 		 * add to the seg list
    456 		 */
    457 		new->next = seg_head;
    458 		seg_head = new;
    459 	}
    460 
    461 	new->nbanks++;
    462 	/*
    463 	 * add bank into segs bank list.  Note we add at the head
    464 	 */
    465 	bank->seg_next = new->banks;
    466 	new->banks = bank;
    467 }
    468 
    469 static memory_seg_t *
    470 match_seg(uint64_t base)
    471 {
    472 	memory_seg_t	*cur_seg;
    473 
    474 	for (cur_seg = seg_head; cur_seg; cur_seg = cur_seg->next) {
    475 		if (cur_seg-> base == base)
    476 			break;
    477 	}
    478 	return (cur_seg);
    479 }
    480 
    481 /*ARGSUSED0*/
    482 void
    483 print_us3_memory_line(int portid, int bank_id, uint64_t bank_size,
    484     char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id)
    485 {
    486 	log_printf(dgettext(TEXT_DOMAIN,
    487 	    "\n No print_us3_memory_line() function specified for"
    488 	    " this platform\n"), 0);
    489 }
    490 
    491 int
    492 display_us3_failed_banks(int system_failed)
    493 {
    494 	memory_bank_t	*bank;
    495 	int		found_failed_bank = 0;
    496 
    497 	if ((bank_head == NULL) || (seg_head == NULL)) {
    498 		log_printf("\nCannot find any memory bank/segment info.\n");
    499 		return (1);
    500 	}
    501 
    502 	for (bank = bank_head; bank; bank = bank->next) {
    503 		/*
    504 		 * check to see if the bank is invalid and also
    505 		 * check if the bank_status is unpopulated.  Unpopulated
    506 		 * means the bank is empty.
    507 		 */
    508 
    509 		if ((bank->valid == 0) &&
    510 		    (strcmp(bank->bank_status, "unpopulated"))) {
    511 			if (!system_failed && !found_failed_bank) {
    512 				found_failed_bank = TRUE;
    513 				log_printf("\n", 0);
    514 				log_printf(dgettext(TEXT_DOMAIN,
    515 				"Failed Field Replaceable Units (FRU) in "
    516 				    "System:\n"), 0);
    517 				log_printf("=========================="
    518 				    "====================\n", 0);
    519 			}
    520 			/*
    521 			 * Call platform specific code for formatting memory
    522 			 * information.
    523 			 */
    524 			print_us3_failed_memory_line(bank->portid, bank->id,
    525 			    bank->bank_status);
    526 		}
    527 	}
    528 	if (found_failed_bank)
    529 		return (1);
    530 	else
    531 		return (0);
    532 }
    533 
    534 /*ARGSUSED0*/
    535 void
    536 print_us3_failed_memory_line(int portid, int bank_id, char *bank_status)
    537 {
    538 	log_printf(dgettext(TEXT_DOMAIN,
    539 	    "\n No print_us3_failed_memory_line() function specified for"
    540 	    " this platform\n"), 0);
    541 }
    542