diff options
| author | stevel <none@none> | 2006-03-29 15:44:11 -0800 |
|---|---|---|
| committer | stevel <none@none> | 2006-03-29 15:44:11 -0800 |
| commit | 03831d35f7499c87d51205817c93e9a8d42c4bae (patch) | |
| tree | bffe92ec7d68bd67b0cd216c9186bf7a01118b8a /usr/src/lib/libprtdiag/common/memory.c | |
| parent | 1b01eaa0e4d0703606fba8c845845b4a7f0c0583 (diff) | |
| download | illumos-joyent-03831d35f7499c87d51205817c93e9a8d42c4bae.tar.gz | |
6392837 move sram to usr/src
6393316 move fcgp2 to usr/src
6393416 move gptwo_pci to usr/src
6393420 move gptwo_wci to usr/src
6393421 move sc_gptwocfg to usr/src
6393424 move axq to usr/src
6393425 move dman to usr/src
6393426 move iosram to usr/src
6393427 move mboxsc to usr/src
6393428 move sbdp to usr/src
6393429 move schpc to usr/src
6393432 move sckmdrv to usr/src
6393435 move scosmb to usr/src
6393437 move driver sgcn to usr/src
6393438 move sgenv to usr/src
6393439 move sgfru to usr/src
6393440 move driver sghsc to usr/src
6393441 move driver sgsbbc to usr/src
6393442 move driver ssm to usr/src
6393452 move pcf8574_nct to usr/src
6393453 move pcf8591_nct to usr/src
6393454 move acebus to usr/src
6393455 move scsb to usr/src
6393462 move lw8 to usr/src
6393463 move driver rmc_comm to usr/src
6393464 move driver rmcadm to usr/src
6393475 move todsg to usr/src
6393889 move libprtdiag to usr/src
6393890 move libprtdiag_psr to usr/src
6393896 move librsc to usr/src
6393898 move scadm to usr/src
6399766 move serengeti platform to usr/src
6399770 move starcat platform to usr/src
6400949 daktari platmod Makefile references incorrect platmod directory
Diffstat (limited to 'usr/src/lib/libprtdiag/common/memory.c')
| -rw-r--r-- | usr/src/lib/libprtdiag/common/memory.c | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/usr/src/lib/libprtdiag/common/memory.c b/usr/src/lib/libprtdiag/common/memory.c new file mode 100644 index 0000000000..1e1aaff425 --- /dev/null +++ b/usr/src/lib/libprtdiag/common/memory.c @@ -0,0 +1,541 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include <sys/sbd_ioctl.h> +#include <sys/sbdp_mem.h> +#include <sys/serengeti.h> +#include <sys/mc.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +#define KBYTE 1024 +#define MBYTE (KBYTE * KBYTE) + +#define MEM_UK_SIZE_MASK 0x3FF + +/* + * Global variables. + */ +static memory_bank_t *bank_head; +static memory_bank_t *bank_tail; +static memory_seg_t *seg_head; + +/* + * Local functions. + */ +static void add_bank_node(uint64_t mc_decode, int portid, char *bank_status); +static void add_seg_node(void); +static memory_seg_t *match_seg(uint64_t); + + +/* + * Used for US-I and US-II systems + */ +/*ARGSUSED0*/ +void +display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats, + struct grp_info *grps, struct mem_total *memory_total) +{ + log_printf(dgettext(TEXT_DOMAIN, "Memory size: "), 0); + + if (sysconf(_SC_PAGESIZE) == -1 || sysconf(_SC_PHYS_PAGES) == -1) + log_printf(dgettext(TEXT_DOMAIN, "unable to determine\n"), 0); + else { + uint64_t mem_size; + + mem_size = + (uint64_t)sysconf(_SC_PAGESIZE) * \ + (uint64_t)sysconf(_SC_PHYS_PAGES); + + if (mem_size >= MBYTE) + log_printf(dgettext(TEXT_DOMAIN, "%d Megabytes\n"), + (int)((mem_size+MBYTE-1) / MBYTE), 0); + else + log_printf(dgettext(TEXT_DOMAIN, "%d Kilobytes\n"), + (int)((mem_size+KBYTE-1) / KBYTE), 0); + } +} + +/*ARGSUSED0*/ +void +display_memoryconf(Sys_tree *tree, struct grp_info *grps) +{ + /* + * This function is intentionally blank + */ +} + +/* + * The following functions are for use by any US-III based systems. + * All they need to do is to call get_us3_mem_regs() + * and then display_us3_banks(). Each platform then needs to decide how + * to format this data by over-riding the generic function + * print_us3_memory_line(). + */ +int +get_us3_mem_regs(Board_node *bnode) +{ + Prom_node *pnode; + int portid; + uint64_t *ma_reg_arr; + uint64_t madr[NUM_MBANKS_PER_MC]; + void *bank_status_array; + char *bank_status; + int i, status_offset; + + for (pnode = dev_find_node(bnode->nodes, "memory-controller"); + pnode != NULL; + pnode = dev_next_node(pnode, "memory-controller")) { + + /* Get portid of this mc from libdevinfo. */ + portid = (*(int *)get_prop_val(find_prop(pnode, "portid"))); + + /* read the logical_bank_ma_regs property for this mc node. */ + ma_reg_arr = (uint64_t *)get_prop_val( + find_prop(pnode, MEM_CFG_PROP_NAME)); + + /* + * There are situations where a memory-controller node + * will not have the logical_bank_ma_regs property and + * we need to allow for these cases. They include: + * - Excalibur/Littleneck systems that only + * support memory on one of their CPUs. + * - Systems that support DR where a cpu board + * can be unconfigured but still connected. + * It is up to the caller of this function to ensure + * that the bank_head and seg_head pointers are not + * NULL after processing all memory-controllers in the + * system. This would indicate a situation where no + * memory-controllers in the system have a logical_bank_ma_regs + * property which should never happen. + */ + if (ma_reg_arr == NULL) + continue; + + /* + * The first NUM_MBANKS_PER_MC of uint64_t's in the + * logical_bank_ma_regs property are the madr values. + */ + for (i = 0; i < NUM_MBANKS_PER_MC; i++) { + madr[i] = *ma_reg_arr++; + } + + /* + * Get the bank_status property for this mem controller from + * OBP. This contains the bank-status for each logical bank. + */ + bank_status_array = (void *)get_prop_val( + find_prop(pnode, "bank-status")); + status_offset = 0; + + /* + * process each logical bank + */ + for (i = 0; i < NUM_MBANKS_PER_MC; i++) { + /* + * Get the bank-status string for this bank + * from the bank_status_array we just retrieved + * from OBP. If the prop was not found, we + * malloc a bank_status and set it to "no_status". + */ + if (bank_status_array) { + bank_status = ((char *)bank_status_array + + status_offset); + + /* Move offset to next bank_status string */ + status_offset += (strlen(bank_status) + 1); + } else { + bank_status = malloc(strlen("no_status")); + strcpy(bank_status, "no_status"); + } + + /* + * create a bank_node for this bank + * and add it to the list. + */ + add_bank_node(madr[i], portid, bank_status); + + /* + * find the segment to which this bank + * belongs. If it doesn't already exist + * then create it. If it exists, add to it. + */ + add_seg_node(); + } + } + return (0); +} + +static void +add_bank_node(uint64_t mc_decode, int portid, char *bank_status) +{ + static int id = 0; + memory_bank_t *new, *bank; + uint32_t ifactor = MC_INTLV(mc_decode); + uint64_t seg_size; + + if ((new = malloc(sizeof (memory_bank_t))) == NULL) { + perror("malloc"); + exit(1); + } + + new->portid = portid; + new->id = id++; + new->valid = (mc_decode >> 63); + new->uk = MC_UK(mc_decode); + new->um = MC_UM(mc_decode); + new->lk = MC_LK(mc_decode); + new->lm = MC_LM(mc_decode); + + seg_size = ((((uint64_t)new->uk & MEM_UK_SIZE_MASK) + 1) << 26); + new->bank_size = seg_size / ifactor; + new->bank_status = bank_status; + + new->next = NULL; + new->seg_next = NULL; + + /* Handle the first bank found */ + if (bank_head == NULL) { + bank_head = new; + bank_tail = new; + return; + } + + /* find last bank in list */ + bank = bank_head; + while (bank->next) + bank = bank->next; + + /* insert this bank into the list */ + bank->next = new; + bank_tail = new; +} + +void +display_us3_banks(void) +{ + uint64_t base, bank_size; + uint32_t intlv; + memory_bank_t *bank, *tmp_bank; + memory_seg_t *seg; + int mcid; + uint64_t dimm_size; + uint64_t total_bank_size = 0; + uint64_t total_sys_mem; + static uint64_t bank0_size, bank1_size, bank2_size, bank3_size; + + if ((bank_head == NULL) || (seg_head == NULL)) { + log_printf("\nCannot find any memory bank/segment info.\n"); + return; + } + + for (bank = bank_head; bank; bank = bank->next) { + /* + * Interleave factor is determined from the + * lk bits in the Mem Addr Decode register. + * + * The Base Address of the memory segment in which this + * bank belongs is determined from the um abd uk bits + * of the Mem Addr Decode register. + * + * See section 9.1.5 of Cheetah Programmer's reference + * manual. + */ + intlv = ((bank->lk ^ 0xF) + 1); + base = bank->um & ~(bank->uk); + + mcid = SG_PORTID_TO_SAFARI_ID(bank->portid); + + /* If bank is not valid, set size to zero incase it's garbage */ + if (bank->valid) + bank_size = ((bank->bank_size) / MBYTE); + else + bank_size = 0; + + /* + * Keep track of all banks found so we can check later + * that this value matches the total memory in the + * system using the pagesize and number of pages. + */ + total_bank_size += bank_size; + + /* Find the matching segment for this bank. */ + seg = match_seg(base); + + /* + * Find the Dimm size by adding banks 0 + 2 and divide by 4 + * and then adding banks 1 + 3 and divide by 4. We divide + * by 2 if one of the logical banks size is zero. + */ + switch ((bank->id) % 4) { + case 0: + /* have bank0_size, need bank2_size */ + bank0_size = bank_size; + bank2_size = 0; + + tmp_bank = bank->next; + while (tmp_bank) { + if (tmp_bank->valid == 0) { + tmp_bank = tmp_bank->next; + continue; + } + /* Is next bank on the same mc ? */ + if (mcid != SG_PORTID_TO_SAFARI_ID( + tmp_bank->portid)) { + break; + } + if ((tmp_bank->id) % 4 == 2) { + bank2_size = + (tmp_bank->bank_size / MBYTE); + break; + } + tmp_bank = tmp_bank->next; + } + if (bank2_size) + dimm_size = (bank0_size + bank2_size) / 4; + else + dimm_size = bank0_size / 2; + break; + case 1: + /* have bank1_size, need bank3_size */ + bank1_size = bank_size; + bank3_size = 0; + + tmp_bank = bank->next; + while (tmp_bank) { + if (tmp_bank->valid == 0) { + tmp_bank = tmp_bank->next; + continue; + } + /* Is next bank on the same mc ? */ + if (mcid != SG_PORTID_TO_SAFARI_ID( + tmp_bank->portid)) { + break; + } + if ((tmp_bank->id) % 4 == 3) { + bank3_size = + (tmp_bank->bank_size / MBYTE); + break; + } + tmp_bank = tmp_bank->next; + } + if (bank3_size) + dimm_size = (bank1_size + bank3_size) / 4; + else + dimm_size = bank1_size / 2; + break; + case 2: + /* have bank0_size and bank2_size */ + bank2_size = bank_size; + if (bank0_size) + dimm_size = (bank0_size + bank2_size) / 4; + else + dimm_size = bank2_size / 2; + break; + case 3: + /* have bank1_size and bank3_size */ + bank3_size = bank_size; + if (bank1_size) + dimm_size = (bank1_size + bank3_size) / 4; + else + dimm_size = bank3_size / 4; + break; + } + + if (bank->valid == 0) + continue; + + /* + * Call platform specific code for formatting memory + * information. + */ + print_us3_memory_line(bank->portid, bank->id, bank_size, + bank->bank_status, dimm_size, intlv, seg->id); + } + + printf("\n"); + + /* + * Sanity check to ensure that the total amount of system + * memory matches the total number of memory banks that + * we find here. Scream if there is a mis-match. + */ + total_sys_mem = (((uint64_t)sysconf(_SC_PAGESIZE) * \ + (uint64_t)sysconf(_SC_PHYS_PAGES)) / MBYTE); + + if (total_bank_size != total_sys_mem) { + log_printf(dgettext(TEXT_DOMAIN, + "\nError: total bank size [%lldMB] does not match total " + "system memory [%lldMB]\n"), total_bank_size, + total_sys_mem, 0); + } + +} + +static void +add_seg_node(void) +{ + uint64_t base; + memory_seg_t *new; + static int id = 0; + memory_bank_t *bank = bank_tail; + + if (bank->valid != 1) + return; + + base = bank->um & ~(bank->uk); + + if ((new = match_seg(base)) == NULL) { + /* + * This bank is part of a new segment, so create + * a struct for it and added to the list of segments + */ + if ((new = malloc(sizeof (memory_seg_t))) == NULL) { + perror("malloc"); + exit(1); + } + new->id = id++; + new->base = base; + new->size = (((uint64_t)bank->uk +1) << 26); + new->intlv = ((bank->lk ^ 0xF) + 1); + + /* + * add to the seg list + */ + new->next = seg_head; + seg_head = new; + } + + new->nbanks++; + /* + * add bank into segs bank list. Note we add at the head + */ + bank->seg_next = new->banks; + new->banks = bank; +} + +static memory_seg_t * +match_seg(uint64_t base) +{ + memory_seg_t *cur_seg; + + for (cur_seg = seg_head; cur_seg; cur_seg = cur_seg->next) { + if (cur_seg-> base == base) + break; + } + return (cur_seg); +} + +/*ARGSUSED0*/ +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) +{ + log_printf(dgettext(TEXT_DOMAIN, + "\n No print_us3_memory_line() function specified for" + " this platform\n"), 0); +} + +int +display_us3_failed_banks(int system_failed) +{ + memory_bank_t *bank; + int found_failed_bank = 0; + + if ((bank_head == NULL) || (seg_head == NULL)) { + log_printf("\nCannot find any memory bank/segment info.\n"); + return (1); + } + + for (bank = bank_head; bank; bank = bank->next) { + /* + * check to see if the bank is invalid and also + * check if the bank_status is unpopulated. Unpopulated + * means the bank is empty. + */ + + if ((bank->valid == 0) && + (strcmp(bank->bank_status, "unpopulated"))) { + if (!system_failed && !found_failed_bank) { + found_failed_bank = TRUE; + log_printf("\n", 0); + log_printf(dgettext(TEXT_DOMAIN, + "Failed Field Replaceable Units (FRU) in " + "System:\n"), 0); + log_printf("==========================" + "====================\n", 0); + } + /* + * Call platform specific code for formatting memory + * information. + */ + print_us3_failed_memory_line(bank->portid, bank->id, + bank->bank_status); + } + } + if (found_failed_bank) + return (1); + else + return (0); +} + +/*ARGSUSED0*/ +void +print_us3_failed_memory_line(int portid, int bank_id, char *bank_status) +{ + log_printf(dgettext(TEXT_DOMAIN, + "\n No print_us3_failed_memory_line() function specified for" + " this platform\n"), 0); +} |
