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