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. 25*2bcbf80cSPeter Tribble * Copyright (c) 2020 Peter Tribble. 2603831d35Sstevel */ 2703831d35Sstevel 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" 5703831d35Sstevel 5803831d35Sstevel #if !defined(TEXT_DOMAIN) 5903831d35Sstevel #define TEXT_DOMAIN "SYS_TEST" 6003831d35Sstevel #endif 6103831d35Sstevel 6203831d35Sstevel #define KBYTE 1024 6303831d35Sstevel #define MBYTE (KBYTE * KBYTE) 6403831d35Sstevel 6503831d35Sstevel #define MEM_UK_SIZE_MASK 0x3FF 6603831d35Sstevel 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; 7303831d35Sstevel 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); 8003831d35Sstevel 8103831d35Sstevel 8203831d35Sstevel /* 8303831d35Sstevel * Used for US-I and US-II systems 8403831d35Sstevel */ 8503831d35Sstevel /*ARGSUSED0*/ 8603831d35Sstevel void 8703831d35Sstevel display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats, 88*2bcbf80cSPeter Tribble struct mem_total *memory_total) 8903831d35Sstevel { 9003831d35Sstevel log_printf(dgettext(TEXT_DOMAIN, "Memory size: "), 0); 9103831d35Sstevel 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; 9603831d35Sstevel 9703831d35Sstevel mem_size = 9803831d35Sstevel (uint64_t)sysconf(_SC_PAGESIZE) * \ 9903831d35Sstevel (uint64_t)sysconf(_SC_PHYS_PAGES); 10003831d35Sstevel 10103831d35Sstevel if (mem_size >= MBYTE) 10203831d35Sstevel log_printf(dgettext(TEXT_DOMAIN, "%d Megabytes\n"), 10303831d35Sstevel (int)((mem_size+MBYTE-1) / MBYTE), 0); 10403831d35Sstevel else 10503831d35Sstevel log_printf(dgettext(TEXT_DOMAIN, "%d Kilobytes\n"), 10603831d35Sstevel (int)((mem_size+KBYTE-1) / KBYTE), 0); 10703831d35Sstevel } 10803831d35Sstevel } 10903831d35Sstevel 11003831d35Sstevel /*ARGSUSED0*/ 11103831d35Sstevel void 112*2bcbf80cSPeter Tribble display_memoryconf(Sys_tree *tree) 11303831d35Sstevel { 11403831d35Sstevel /* 11503831d35Sstevel * This function is intentionally blank 11603831d35Sstevel */ 11703831d35Sstevel } 11803831d35Sstevel 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 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; 13603831d35Sstevel 13703831d35Sstevel for (pnode = dev_find_node(bnode->nodes, "memory-controller"); 13803831d35Sstevel pnode != NULL; 13903831d35Sstevel pnode = dev_next_node(pnode, "memory-controller")) { 14003831d35Sstevel 14103831d35Sstevel /* Get portid of this mc from libdevinfo. */ 14203831d35Sstevel portid = (*(int *)get_prop_val(find_prop(pnode, "portid"))); 14303831d35Sstevel 14403831d35Sstevel /* read the logical_bank_ma_regs property for this mc node. */ 14503831d35Sstevel ma_reg_arr = (uint64_t *)get_prop_val( 14603831d35Sstevel find_prop(pnode, MEM_CFG_PROP_NAME)); 14703831d35Sstevel 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; 16503831d35Sstevel 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 } 17303831d35Sstevel 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( 17903831d35Sstevel find_prop(pnode, "bank-status")); 18003831d35Sstevel status_offset = 0; 18103831d35Sstevel 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); 19503831d35Sstevel 19603831d35Sstevel /* Move offset to next bank_status string */ 19703831d35Sstevel status_offset += (strlen(bank_status) + 1); 19803831d35Sstevel } else { 19903831d35Sstevel bank_status = malloc(strlen("no_status")); 20003831d35Sstevel strcpy(bank_status, "no_status"); 20103831d35Sstevel } 20203831d35Sstevel 20303831d35Sstevel /* 20403831d35Sstevel * create a bank_node for this bank 20503831d35Sstevel * and add it to the list. 20603831d35Sstevel */ 20703831d35Sstevel add_bank_node(madr[i], portid, bank_status); 20803831d35Sstevel 20903831d35Sstevel /* 21003831d35Sstevel * find the segment to which this bank 21103831d35Sstevel * belongs. If it doesn't already exist 21203831d35Sstevel * then create it. If it exists, add to it. 21303831d35Sstevel */ 21403831d35Sstevel add_seg_node(); 21503831d35Sstevel } 21603831d35Sstevel } 21703831d35Sstevel return (0); 21803831d35Sstevel } 21903831d35Sstevel 22003831d35Sstevel static void 22103831d35Sstevel add_bank_node(uint64_t mc_decode, int portid, char *bank_status) 22203831d35Sstevel { 22303831d35Sstevel static int id = 0; 22403831d35Sstevel memory_bank_t *new, *bank; 22503831d35Sstevel uint32_t ifactor = MC_INTLV(mc_decode); 22603831d35Sstevel uint64_t seg_size; 22703831d35Sstevel 22803831d35Sstevel if ((new = malloc(sizeof (memory_bank_t))) == NULL) { 22903831d35Sstevel perror("malloc"); 23003831d35Sstevel exit(1); 23103831d35Sstevel } 23203831d35Sstevel 23303831d35Sstevel new->portid = portid; 23403831d35Sstevel new->id = id++; 23503831d35Sstevel new->valid = (mc_decode >> 63); 23603831d35Sstevel new->uk = MC_UK(mc_decode); 23703831d35Sstevel new->um = MC_UM(mc_decode); 23803831d35Sstevel new->lk = MC_LK(mc_decode); 23903831d35Sstevel new->lm = MC_LM(mc_decode); 24003831d35Sstevel 24103831d35Sstevel seg_size = ((((uint64_t)new->uk & MEM_UK_SIZE_MASK) + 1) << 26); 24203831d35Sstevel new->bank_size = seg_size / ifactor; 24303831d35Sstevel new->bank_status = bank_status; 24403831d35Sstevel 24503831d35Sstevel new->next = NULL; 24603831d35Sstevel new->seg_next = NULL; 24703831d35Sstevel 24803831d35Sstevel /* Handle the first bank found */ 24903831d35Sstevel if (bank_head == NULL) { 25003831d35Sstevel bank_head = new; 25103831d35Sstevel bank_tail = new; 25203831d35Sstevel return; 25303831d35Sstevel } 25403831d35Sstevel 25503831d35Sstevel /* find last bank in list */ 25603831d35Sstevel bank = bank_head; 25703831d35Sstevel while (bank->next) 25803831d35Sstevel bank = bank->next; 25903831d35Sstevel 26003831d35Sstevel /* insert this bank into the list */ 26103831d35Sstevel bank->next = new; 26203831d35Sstevel bank_tail = new; 26303831d35Sstevel } 26403831d35Sstevel 26503831d35Sstevel void 26603831d35Sstevel display_us3_banks(void) 26703831d35Sstevel { 26803831d35Sstevel uint64_t base, bank_size; 26903831d35Sstevel uint32_t intlv; 27003831d35Sstevel memory_bank_t *bank, *tmp_bank; 27103831d35Sstevel memory_seg_t *seg; 27203831d35Sstevel int mcid; 27303831d35Sstevel uint64_t dimm_size; 27403831d35Sstevel uint64_t total_bank_size = 0; 27503831d35Sstevel uint64_t total_sys_mem; 27603831d35Sstevel static uint64_t bank0_size, bank1_size, bank2_size, bank3_size; 27703831d35Sstevel 27803831d35Sstevel if ((bank_head == NULL) || (seg_head == NULL)) { 27903831d35Sstevel log_printf("\nCannot find any memory bank/segment info.\n"); 28003831d35Sstevel return; 28103831d35Sstevel } 28203831d35Sstevel 28303831d35Sstevel for (bank = bank_head; bank; bank = bank->next) { 28403831d35Sstevel /* 28503831d35Sstevel * Interleave factor is determined from the 28603831d35Sstevel * lk bits in the Mem Addr Decode register. 28703831d35Sstevel * 28803831d35Sstevel * The Base Address of the memory segment in which this 28903831d35Sstevel * bank belongs is determined from the um abd uk bits 29003831d35Sstevel * of the Mem Addr Decode register. 29103831d35Sstevel * 29203831d35Sstevel * See section 9.1.5 of Cheetah Programmer's reference 29303831d35Sstevel * manual. 29403831d35Sstevel */ 29503831d35Sstevel intlv = ((bank->lk ^ 0xF) + 1); 29603831d35Sstevel base = bank->um & ~(bank->uk); 29703831d35Sstevel 29803831d35Sstevel mcid = SG_PORTID_TO_SAFARI_ID(bank->portid); 29903831d35Sstevel 30003831d35Sstevel /* If bank is not valid, set size to zero incase it's garbage */ 30103831d35Sstevel if (bank->valid) 30203831d35Sstevel bank_size = ((bank->bank_size) / MBYTE); 30303831d35Sstevel else 30403831d35Sstevel bank_size = 0; 30503831d35Sstevel 30603831d35Sstevel /* 30703831d35Sstevel * Keep track of all banks found so we can check later 30803831d35Sstevel * that this value matches the total memory in the 30903831d35Sstevel * system using the pagesize and number of pages. 31003831d35Sstevel */ 31103831d35Sstevel total_bank_size += bank_size; 31203831d35Sstevel 31303831d35Sstevel /* Find the matching segment for this bank. */ 31403831d35Sstevel seg = match_seg(base); 31503831d35Sstevel 31603831d35Sstevel /* 31703831d35Sstevel * Find the Dimm size by adding banks 0 + 2 and divide by 4 31803831d35Sstevel * and then adding banks 1 + 3 and divide by 4. We divide 31903831d35Sstevel * by 2 if one of the logical banks size is zero. 32003831d35Sstevel */ 32103831d35Sstevel switch ((bank->id) % 4) { 32203831d35Sstevel case 0: 32303831d35Sstevel /* have bank0_size, need bank2_size */ 32403831d35Sstevel bank0_size = bank_size; 32503831d35Sstevel bank2_size = 0; 32603831d35Sstevel 32703831d35Sstevel tmp_bank = bank->next; 32803831d35Sstevel while (tmp_bank) { 32903831d35Sstevel if (tmp_bank->valid == 0) { 33003831d35Sstevel tmp_bank = tmp_bank->next; 33103831d35Sstevel continue; 33203831d35Sstevel } 33303831d35Sstevel /* Is next bank on the same mc ? */ 33403831d35Sstevel if (mcid != SG_PORTID_TO_SAFARI_ID( 33503831d35Sstevel tmp_bank->portid)) { 33603831d35Sstevel break; 33703831d35Sstevel } 33803831d35Sstevel if ((tmp_bank->id) % 4 == 2) { 33903831d35Sstevel bank2_size = 34003831d35Sstevel (tmp_bank->bank_size / MBYTE); 34103831d35Sstevel break; 34203831d35Sstevel } 34303831d35Sstevel tmp_bank = tmp_bank->next; 34403831d35Sstevel } 34503831d35Sstevel if (bank2_size) 34603831d35Sstevel dimm_size = (bank0_size + bank2_size) / 4; 34703831d35Sstevel else 34803831d35Sstevel dimm_size = bank0_size / 2; 34903831d35Sstevel break; 35003831d35Sstevel case 1: 35103831d35Sstevel /* have bank1_size, need bank3_size */ 35203831d35Sstevel bank1_size = bank_size; 35303831d35Sstevel bank3_size = 0; 35403831d35Sstevel 35503831d35Sstevel tmp_bank = bank->next; 35603831d35Sstevel while (tmp_bank) { 35703831d35Sstevel if (tmp_bank->valid == 0) { 35803831d35Sstevel tmp_bank = tmp_bank->next; 35903831d35Sstevel continue; 36003831d35Sstevel } 36103831d35Sstevel /* Is next bank on the same mc ? */ 36203831d35Sstevel if (mcid != SG_PORTID_TO_SAFARI_ID( 36303831d35Sstevel tmp_bank->portid)) { 36403831d35Sstevel break; 36503831d35Sstevel } 36603831d35Sstevel if ((tmp_bank->id) % 4 == 3) { 36703831d35Sstevel bank3_size = 36803831d35Sstevel (tmp_bank->bank_size / MBYTE); 36903831d35Sstevel break; 37003831d35Sstevel } 37103831d35Sstevel tmp_bank = tmp_bank->next; 37203831d35Sstevel } 37303831d35Sstevel if (bank3_size) 37403831d35Sstevel dimm_size = (bank1_size + bank3_size) / 4; 37503831d35Sstevel else 37603831d35Sstevel dimm_size = bank1_size / 2; 37703831d35Sstevel break; 37803831d35Sstevel case 2: 37903831d35Sstevel /* have bank0_size and bank2_size */ 38003831d35Sstevel bank2_size = bank_size; 38103831d35Sstevel if (bank0_size) 38203831d35Sstevel dimm_size = (bank0_size + bank2_size) / 4; 38303831d35Sstevel else 38403831d35Sstevel dimm_size = bank2_size / 2; 38503831d35Sstevel break; 38603831d35Sstevel case 3: 38703831d35Sstevel /* have bank1_size and bank3_size */ 38803831d35Sstevel bank3_size = bank_size; 38903831d35Sstevel if (bank1_size) 39003831d35Sstevel dimm_size = (bank1_size + bank3_size) / 4; 39103831d35Sstevel else 39203831d35Sstevel dimm_size = bank3_size / 4; 39303831d35Sstevel break; 39403831d35Sstevel } 39503831d35Sstevel 39603831d35Sstevel if (bank->valid == 0) 39703831d35Sstevel continue; 39803831d35Sstevel 39903831d35Sstevel /* 40003831d35Sstevel * Call platform specific code for formatting memory 40103831d35Sstevel * information. 40203831d35Sstevel */ 40303831d35Sstevel print_us3_memory_line(bank->portid, bank->id, bank_size, 40403831d35Sstevel bank->bank_status, dimm_size, intlv, seg->id); 40503831d35Sstevel } 40603831d35Sstevel 40703831d35Sstevel printf("\n"); 40803831d35Sstevel 40903831d35Sstevel /* 41003831d35Sstevel * Sanity check to ensure that the total amount of system 41103831d35Sstevel * memory matches the total number of memory banks that 41203831d35Sstevel * we find here. Scream if there is a mis-match. 41303831d35Sstevel */ 41403831d35Sstevel total_sys_mem = (((uint64_t)sysconf(_SC_PAGESIZE) * \ 41503831d35Sstevel (uint64_t)sysconf(_SC_PHYS_PAGES)) / MBYTE); 41603831d35Sstevel 41703831d35Sstevel if (total_bank_size != total_sys_mem) { 41803831d35Sstevel log_printf(dgettext(TEXT_DOMAIN, 41903831d35Sstevel "\nError: total bank size [%lldMB] does not match total " 42003831d35Sstevel "system memory [%lldMB]\n"), total_bank_size, 42103831d35Sstevel total_sys_mem, 0); 42203831d35Sstevel } 42303831d35Sstevel 42403831d35Sstevel } 42503831d35Sstevel 42603831d35Sstevel static void 42703831d35Sstevel add_seg_node(void) 42803831d35Sstevel { 42903831d35Sstevel uint64_t base; 43003831d35Sstevel memory_seg_t *new; 43103831d35Sstevel static int id = 0; 43203831d35Sstevel memory_bank_t *bank = bank_tail; 43303831d35Sstevel 43403831d35Sstevel if (bank->valid != 1) 43503831d35Sstevel return; 43603831d35Sstevel 43703831d35Sstevel base = bank->um & ~(bank->uk); 43803831d35Sstevel 43903831d35Sstevel if ((new = match_seg(base)) == NULL) { 44003831d35Sstevel /* 44103831d35Sstevel * This bank is part of a new segment, so create 44203831d35Sstevel * a struct for it and added to the list of segments 44303831d35Sstevel */ 44403831d35Sstevel if ((new = malloc(sizeof (memory_seg_t))) == NULL) { 44503831d35Sstevel perror("malloc"); 44603831d35Sstevel exit(1); 44703831d35Sstevel } 44803831d35Sstevel new->id = id++; 44903831d35Sstevel new->base = base; 45003831d35Sstevel new->size = (((uint64_t)bank->uk +1) << 26); 45103831d35Sstevel new->intlv = ((bank->lk ^ 0xF) + 1); 45203831d35Sstevel 45303831d35Sstevel /* 45403831d35Sstevel * add to the seg list 45503831d35Sstevel */ 45603831d35Sstevel new->next = seg_head; 45703831d35Sstevel seg_head = new; 45803831d35Sstevel } 45903831d35Sstevel 46003831d35Sstevel new->nbanks++; 46103831d35Sstevel /* 46203831d35Sstevel * add bank into segs bank list. Note we add at the head 46303831d35Sstevel */ 46403831d35Sstevel bank->seg_next = new->banks; 46503831d35Sstevel new->banks = bank; 46603831d35Sstevel } 46703831d35Sstevel 46803831d35Sstevel static memory_seg_t * 46903831d35Sstevel match_seg(uint64_t base) 47003831d35Sstevel { 47103831d35Sstevel memory_seg_t *cur_seg; 47203831d35Sstevel 47303831d35Sstevel for (cur_seg = seg_head; cur_seg; cur_seg = cur_seg->next) { 47403831d35Sstevel if (cur_seg-> base == base) 47503831d35Sstevel break; 47603831d35Sstevel } 47703831d35Sstevel return (cur_seg); 47803831d35Sstevel } 47903831d35Sstevel 48003831d35Sstevel /*ARGSUSED0*/ 48103831d35Sstevel void 48203831d35Sstevel print_us3_memory_line(int portid, int bank_id, uint64_t bank_size, 48303831d35Sstevel char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id) 48403831d35Sstevel { 48503831d35Sstevel log_printf(dgettext(TEXT_DOMAIN, 48603831d35Sstevel "\n No print_us3_memory_line() function specified for" 48703831d35Sstevel " this platform\n"), 0); 48803831d35Sstevel } 48903831d35Sstevel 49003831d35Sstevel int 49103831d35Sstevel display_us3_failed_banks(int system_failed) 49203831d35Sstevel { 49303831d35Sstevel memory_bank_t *bank; 49403831d35Sstevel int found_failed_bank = 0; 49503831d35Sstevel 49603831d35Sstevel if ((bank_head == NULL) || (seg_head == NULL)) { 49703831d35Sstevel log_printf("\nCannot find any memory bank/segment info.\n"); 49803831d35Sstevel return (1); 49903831d35Sstevel } 50003831d35Sstevel 50103831d35Sstevel for (bank = bank_head; bank; bank = bank->next) { 50203831d35Sstevel /* 50303831d35Sstevel * check to see if the bank is invalid and also 50403831d35Sstevel * check if the bank_status is unpopulated. Unpopulated 50503831d35Sstevel * means the bank is empty. 50603831d35Sstevel */ 50703831d35Sstevel 50803831d35Sstevel if ((bank->valid == 0) && 50903831d35Sstevel (strcmp(bank->bank_status, "unpopulated"))) { 51003831d35Sstevel if (!system_failed && !found_failed_bank) { 51103831d35Sstevel found_failed_bank = TRUE; 51203831d35Sstevel log_printf("\n", 0); 51303831d35Sstevel log_printf(dgettext(TEXT_DOMAIN, 51403831d35Sstevel "Failed Field Replaceable Units (FRU) in " 51503831d35Sstevel "System:\n"), 0); 51603831d35Sstevel log_printf("==========================" 51703831d35Sstevel "====================\n", 0); 51803831d35Sstevel } 51903831d35Sstevel /* 52003831d35Sstevel * Call platform specific code for formatting memory 52103831d35Sstevel * information. 52203831d35Sstevel */ 52303831d35Sstevel print_us3_failed_memory_line(bank->portid, bank->id, 52403831d35Sstevel bank->bank_status); 52503831d35Sstevel } 52603831d35Sstevel } 52703831d35Sstevel if (found_failed_bank) 52803831d35Sstevel return (1); 52903831d35Sstevel else 53003831d35Sstevel return (0); 53103831d35Sstevel } 53203831d35Sstevel 53303831d35Sstevel /*ARGSUSED0*/ 53403831d35Sstevel void 53503831d35Sstevel print_us3_failed_memory_line(int portid, int bank_id, char *bank_status) 53603831d35Sstevel { 53703831d35Sstevel log_printf(dgettext(TEXT_DOMAIN, 53803831d35Sstevel "\n No print_us3_failed_memory_line() function specified for" 53903831d35Sstevel " this platform\n"), 0); 54003831d35Sstevel } 541