xref: /titanic_52/usr/src/lib/libprtdiag/common/memory.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
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