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
display_memorysize(Sys_tree * tree,struct system_kstat_data * kstats,struct grp_info * grps,struct mem_total * memory_total)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
display_memoryconf(Sys_tree * tree,struct grp_info * grps)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
get_us3_mem_regs(Board_node * bnode)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
add_bank_node(uint64_t mc_decode,int portid,char * bank_status)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
display_us3_banks(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
add_seg_node(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 *
match_seg(uint64_t base)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
print_us3_memory_line(int portid,int bank_id,uint64_t bank_size,char * bank_status,uint64_t dimm_size,uint32_t intlv,int seg_id)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
display_us3_failed_banks(int system_failed)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
print_us3_failed_memory_line(int portid,int bank_id,char * bank_status)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