/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2020 Peter Tribble. * * Opl Platform specific functions. * * called when : * machine_type == MTYPE_OPL */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Globals and externs */ #define KBYTE 1024 #define MBYTE (KBYTE * KBYTE) #define HZ_TO_MHZ(x) ((((uint64_t)(x)) + 500000) / 1000000) #define SCF_SECURE_MODE_KSTAT_NAMED "secure_mode" #define SCF_STAT_MODE_UNLOCK 0 #define SCF_STAT_MODE_LOCK 1 #define SCF_SYSTEM_KSTAT_NAME "scf" #ifndef TEXT_DOMAIN #define TEXT_DOMAIN "SYS_TEST" #endif /* TEXT_DOMAIN */ /* * Global functions and variables * these functions will overlay the symbol table of libprtdiag * at runtime (Opl systems only) */ struct cs_status { int cs_number; int status; uint_t avail_hi; uint_t avail_lo; uint_t dimm_hi; uint_t dimm_lo; int dimms; }; int do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag); void *get_prop_val(Prop *prop); void display_ffb(Board_node *, int); void display_sbus(Board_node *board); void display_cpu_devices(Sys_tree *tree); void display_cpus(Board_node *board); void display_memoryconf(Sys_tree *tree); void display_io_cards(struct io_card *list); void display_io_devices(Sys_tree *tree); void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, struct system_kstat_data *kstats); Prop *find_prop(Prom_node *pnode, char *name); int do_piclinfo(int); int get_proc_mode(void); /* Local functions */ static void opl_disp_environ(void); static void opl_disp_hw_revisions(Sys_tree *tree, Prom_node *root); static uint64_t print_opl_memory_line(int lsb, struct cs_status *cs_stat, int ngrps, int mirror_mode); static uint64_t get_opl_mem_regs(Board_node *bnode); void add_node(Sys_tree *root, Prom_node *pnode); static int get_prop_size(Prop *prop); static int v_flag = 0; /* * Linked list of IO card info for display. * Using file scope for use in a recursive function. */ static struct io_card *card_list = NULL; /* * Check prom node for a class-code. If it exists and it's not a bridge device * then add an io_card to card_list. Then recursively call this function for * its child and sibling nodes. */ static void walk_tree_for_pci_devices(Prom_node *node, int board_number) { struct io_card card; char *str; void *val; int ccode; if (node == NULL) { return; } /* Look for a class-code property. Skip, if it's a bridge */ ccode = -1; val = get_prop_val(find_prop(node, "class-code")); if (val != NULL) { ccode = *(int *)val; } if ((ccode != -1) && (ccode < 0x60000 || ccode > 0x6ffff)) { (void) memset(&card, 0, sizeof (card)); card.board = board_number; str = (char *)get_prop_val(find_prop(node, "name")); (void) strlcpy(card.name, (str == NULL ? "N/A":str), sizeof (card.name)); str = (char *)get_prop_val(find_prop(node, "model")); (void) strlcpy(card.model, (str == NULL ? "N/A":str), sizeof (card.model)); /* insert card to the list */ card_list = insert_io_card(card_list, &card); } /* Call this function for its child/sibling */ walk_tree_for_pci_devices(node->child, board_number); walk_tree_for_pci_devices(node->sibling, board_number); } /* * For display of I/O devices for "prtdiag" */ void display_io_devices(Sys_tree *tree) { Board_node *bnode; if (v_flag) { /* * OPL's PICL interface for display of PCI I/O devices * for "prtdiag -v" */ (void) do_piclinfo(v_flag); } else { log_printf("\n", 0); log_printf("=========================", 0); log_printf(dgettext(TEXT_DOMAIN, " IO Cards "), 0); log_printf("=========================", 0); log_printf("\n", 0); log_printf("\n", 0); bnode = tree->bd_list; while (bnode != NULL) { walk_tree_for_pci_devices(bnode->nodes, bnode->board_num); bnode = bnode->next; } display_io_cards(card_list); free_io_cards(card_list); } } /* * There are no FFB's on OPL. */ /*ARGSUSED*/ void display_ffb(Board_node *board, int table) { } /* * There are no Sbus's on OPL. */ /*ARGSUSED*/ void display_sbus(Board_node *board) { } /* * Details of I/O information. Print out all the io cards. */ void display_io_cards(struct io_card *list) { char *hdrfmt = "%-6.6s %-14.14s %-12.12s\n"; struct io_card *p; if (list == NULL) return; (void) textdomain(TEXT_DOMAIN); log_printf(hdrfmt, gettext("LSB"), gettext("Name"), gettext("Model"), 0); log_printf(hdrfmt, "---", "-----------------", "------------", 0); for (p = list; p != NULL; p = p->next) { /* Board number */ log_printf(" %02d ", p->board, 0); /* Card name */ log_printf("%-15.15s", p->name, 0); /* Card model */ log_printf("%-12.12s", p->model, 0); log_printf("\n", 0); } log_printf("\n", 0); } /* * Details of CPU information. */ void display_cpu_devices(Sys_tree *tree) { Board_node *bnode; char *hdrfmt = "%-4.4s %-4.4s %-40.40s %-5.5s %-5.5s %-5.5s %-4.4s\n"; (void) textdomain(TEXT_DOMAIN); /* * Display the table header for CPUs . Then display the CPU * frequency, cache size, and processor revision of all cpus. */ log_printf("\n", 0); log_printf("====================================", 0); log_printf(gettext(" CPUs "), 0); log_printf("====================================", 0); log_printf("\n\n", 0); log_printf(hdrfmt, "", gettext("CPU"), gettext(" CPU "), gettext("Run"), gettext("L2$"), gettext("CPU"), gettext("CPU"), 0); log_printf(hdrfmt, gettext("LSB"), gettext("Chip"), gettext(" ID "), gettext("MHz"), gettext(" MB"), gettext("Impl."), gettext("Mask"), 0); log_printf(hdrfmt, "---", "----", "----------------------------------------", "----", "---", "-----", "----", 0); /* Now display all of the cpus on each board */ for (bnode = tree->bd_list; bnode != NULL; bnode = bnode->next) { display_cpus(bnode); } log_printf("\n", 0); } /* * Display the CPUs present on this board. */ void display_cpus(Board_node *board) { int *impl, *mask, *cpuid, *portid, *l2cache_size; uint_t freq; /* CPU clock frequency */ Prom_node *pnode, *cpu; char *name; (void) textdomain(TEXT_DOMAIN); /* * Get the Cpus' properties for display */ for (pnode = board->nodes; pnode != NULL; pnode = pnode->sibling) { char cpu_str[MAXSTRLEN], fcpu_str[MAXSTRLEN] = {0}; name = get_node_name(pnode); if ((name == NULL) || (strncmp(name, "cmp", 3) != 0)) { continue; } portid = (int *)get_prop_val(find_prop(pnode, "portid")); freq = (HZ_TO_MHZ(get_cpu_freq(pnode->child))); l2cache_size = (int *)get_prop_val(find_prop(pnode->child, "l2-cache-size")); impl = (int *)get_prop_val(find_prop(pnode->child, "implementation#")); mask = (int *)get_prop_val(find_prop(pnode->child, "mask#")); /* Lsb id */ log_printf(" %02d ", board->board_num, 0); if (portid != NULL) log_printf("%3d ", (((*portid)>>3)&0x3), 0); /* * OPL * Specific parsing of the CMP/CORE/CPU chain. * The internal cpu tree built by walk_di_tree() * in common code can be illustrated by the diagram * below: * * Olympus: * * cmp->cpu->cpu->cpu->cpu->(next board nodes) * / \ * core core * * Jupiter: * * cmp->cpu->cpu->cpu->cpu->cpu->cpu->cpu->cpu->(board nodes) * | * _____________ * / \ \ \ * core core core core * * * where "/" or "\" are children * and "->" are siblings * */ for (cpu = pnode->sibling; cpu != NULL; ) { Prom_node *cpu_next = NULL; name = get_node_name(cpu); if ((name == NULL) || (strncmp(name, "cpu", 3) != 0)) { break; } /* Id assigned to Virtual processor core */ cpuid = (int *)get_prop_val(find_prop(cpu, "cpuid")); cpu_next = cpu->sibling; if (cpu_next != NULL) { name = get_node_name(cpu_next); if ((name == NULL) || (strncmp(name, "cpu", 3) != 0)) { cpu_next = NULL; } } if (cpuid != NULL) { /* Used for printing in comma format */ (void) sprintf(cpu_str, "%4d", *cpuid); (void) strlcat(fcpu_str, cpu_str, MAXSTRLEN); if (cpu_next != NULL) { (void) strlcat(fcpu_str, ",", MAXSTRLEN); } } else { (void) sprintf(cpu_str, "%4s", "N/A"); (void) strlcat(fcpu_str, cpu_str, MAXSTRLEN); if (cpu_next != NULL) { (void) strlcat(fcpu_str, ",", MAXSTRLEN); } } cpu = cpu_next; } log_printf("%-40.40s", fcpu_str, 0); /* Running frequency */ if (freq != 0) log_printf(" %4ld ", freq, 0); else log_printf(" %4s ", "N/A", 0); /* L2 cache size */ if (l2cache_size == NULL) log_printf(" %3s ", "N/A", 0); else { log_printf("%4.1f ", (float)(*l2cache_size) / (float)(1<<20), 0); } /* Implementation number of processor */ if (impl != NULL) log_printf(" %4d ", *impl, 0); else log_printf(" %4s ", "N/A", 0); /* Mask Set version */ /* Bits 31:24 of VER register is mask. */ /* Mask value : Non MTP mode - 00-7f, MTP mode - 80-ff */ if (mask == NULL) log_printf("%3s", "N/A", 0); else log_printf("%-3d", (*mask)&0xff, 0); log_printf("\n", 0); } } /* * Gather memory information: Details of memory information. */ static uint64_t get_opl_mem_regs(Board_node *bnode) { Prom_node *pnode; struct cs_status *cs_stat; uint64_t total_mem = 0; int cs_size, ngrps; pnode = dev_find_node(bnode->nodes, "pseudo-mc"); while (pnode != NULL) { cs_size = get_prop_size(find_prop(pnode, "cs-status")); if (cs_size > 0) { int *mirror_mode = NULL; int mode = 0; /* OBP returns lists of 7 ints */ cs_stat = (struct cs_status *)get_prop_val (find_prop(pnode, "cs-status")); mirror_mode = (int *)(get_prop_val (find_prop(pnode, "mirror-mode"))); if (mirror_mode != NULL) mode = (*mirror_mode); /* * The units of cs_size will be either number of bytes * or number of int array elements as this is derived * from the libprtdiag Prop node size field which has * inconsistent units. Until this is addressed in * libprtdiag, we need a heuristic to determine the * number of CS groups. Given that the maximum number * of CS groups is 2, the maximum number of cs-status * array elements will be 2*7=14. Since this is smaller * than the byte size of a single struct status, we use * this to decide if we are dealing with bytes or array * elements in determining the number of CS groups. */ if (cs_size < sizeof (struct cs_status)) { /* cs_size is number of total int [] elements */ ngrps = cs_size / 7; } else { /* cs_size is total byte count */ ngrps = cs_size/sizeof (struct cs_status); } if (cs_stat != NULL) { total_mem += print_opl_memory_line(bnode->board_num, cs_stat, ngrps, mode); } } pnode = dev_next_node(pnode, "pseudo-mc"); } return (total_mem); } /* * Display memory information. */ void display_memoryconf(Sys_tree *tree) { Board_node *bnode = tree->bd_list; uint64_t total_mem = 0, total_sys_mem = 0; char *hdrfmt = "\n%-5.5s %-6.6s %-18.18s %-10.10s" " %-6.6s %-5.5s %-7.7s %-10.10s"; (void) textdomain(TEXT_DOMAIN); log_printf("============================", 0); log_printf(gettext(" Memory Configuration "), 0); log_printf("============================", 0); log_printf("\n", 0); log_printf(hdrfmt, "", gettext("Memory"), gettext("Available"), gettext("Memory"), gettext("DIMM"), gettext("# of"), gettext("Mirror"), gettext("Interleave"), 0); log_printf(hdrfmt, gettext("LSB"), gettext("Group"), gettext("Size"), gettext("Status"), gettext("Size"), gettext("DIMMs"), gettext("Mode"), gettext("Factor"), 0); log_printf(hdrfmt, "---", "-------", "------------------", "-------", "------", "-----", "-------", "----------", 0); log_printf("\n", 0); for (bnode = tree->bd_list; bnode != NULL; bnode = bnode->next) { total_mem += get_opl_mem_regs(bnode); } /* * Sanity check to ensure that the total amount of system * memory matches the total number of memory that * we find here. Display error message if there is a mis-match. */ total_sys_mem = (((uint64_t)sysconf(_SC_PAGESIZE) * (uint64_t)sysconf (_SC_PHYS_PAGES)) / MBYTE); if (total_mem != total_sys_mem) { log_printf(dgettext(TEXT_DOMAIN, "\nError:total available " "size [%lldMB] does not match total system memory " "[%lldMB]\n"), total_mem, total_sys_mem, 0); } } /* * This function provides Opl's formatting of the memory config * information that get_opl_mem_regs() has gathered. */ static uint64_t print_opl_memory_line(int lsb, struct cs_status *cs_stat, int ngrps, int mirror_mode) { int i; uint64_t total_board_mem = 0; int i_factor = 2; /* default to non-mirror mode */ int interleave; (void) textdomain(TEXT_DOMAIN); if (mirror_mode) i_factor *= 2; /* * Interleave factor calculation: * Obtain "mirror-mode" property from pseudo-mc. * cs_stat[0].dimms/i_factor represents interleave factor per * pseudo-mc node. Must use cs_stat[0].dimms since this will yield * interleave factor even if some DIMMs are isolated, except for * the case where the entire memory group has been deconfigured (eg. due * to DIMM failure); in this case, we use the second memory group * (i.e. cs_stat[1]). * * Mirror mode: * interleave factor = (# of DIMMs on cs_stat[0]/4) * * Non-mirror mode: * interleave factor = (# of DIMMs on cs_stat[0]/2) */ if (cs_stat[0].dimms == 0) interleave = cs_stat[1].dimms/i_factor; else interleave = cs_stat[0].dimms/i_factor; for (i = 0; i < ngrps; i++) { uint64_t mem_size; mem_size = ((((uint64_t)cs_stat[i].avail_hi)<<32) + cs_stat[i].avail_lo); if (mem_size == 0) continue; /* Lsb Id */ log_printf(" %02d ", lsb, 0); /* Memory Group Number */ if ((cs_stat[i].cs_number) == 0) log_printf("%-6.6s", "A", 0); else log_printf("%-6.6s", "B", 0); /* Memory Group Size */ log_printf("%8lldMB ", mem_size/MBYTE, 0); total_board_mem += (mem_size/MBYTE); /* Memory Group Status */ log_printf("%-11.11s", cs_stat[i].status ? "partial": "okay", 0); /* DIMM Size */ log_printf("%4lldMB ", ((((uint64_t)cs_stat[i].dimm_hi)<<32) + cs_stat[i].dimm_lo)/MBYTE, 0); /* Number of DIMMs */ log_printf(" %2d", cs_stat[i].dimms); /* Mirror Mode */ if (mirror_mode) { log_printf("%-4.4s", " yes"); } else log_printf("%-4.4s", " no "); /* Interleave Factor */ if (interleave) log_printf(" %d-way\n", interleave); else log_printf(" None\n"); } return (total_board_mem); } /* * Details of hardware revision and environmental status. */ /*ARGSUSED*/ void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, struct system_kstat_data *kstats) { /* Print the PROM revisions */ opl_disp_hw_revisions(tree, root); } /* * Gather and display hardware revision and environmental status */ /*ARGSUSED*/ static void opl_disp_hw_revisions(Sys_tree *tree, Prom_node *root) { char *version; Prom_node *pnode; int value; (void) textdomain(TEXT_DOMAIN); /* Print the header */ log_printf("\n", 0); log_printf("====================", 0); log_printf(gettext(" Hardware Revisions "), 0); log_printf("====================", 0); log_printf("\n\n", 0); /* Display Prom revision header */ log_printf(gettext("System PROM revisions:"), 0); log_printf("\n----------------------\n", 0); log_printf("\n", 0); /* Display OBP version info */ pnode = dev_find_node(root, "openprom"); if (pnode != NULL) { version = (char *)get_prop_val(find_prop(pnode, "version")); if (version != NULL) log_printf("%s\n\n", version, 0); else log_printf("%s\n\n", "N/A", 0); } /* Print the header */ log_printf("\n", 0); log_printf("===================", 0); log_printf(gettext(" Environmental Status "), 0); log_printf("===================", 0); log_printf("\n\n", 0); opl_disp_environ(); /* * PICL interface needs to be used for system processor mode display. * Check existence of OBP property "SPARC64-VII-mode". * No display if property does not exist. * If property exists then system is in (Jupiter) SPARC64-VII-mode. */ value = get_proc_mode(); if (value == 0) { /* Print the header */ log_printf("\n", 0); log_printf("===================", 0); log_printf(gettext(" System Processor Mode "), 0); log_printf("===================", 0); log_printf("\n\n", 0); /* Jupiter mode */ log_printf("%s\n\n", "SPARC64-VII mode"); } } /* * Gather environmental information */ static void opl_disp_environ(void) { kstat_ctl_t *kc; kstat_t *ksp; kstat_named_t *k; if ((kc = kstat_open()) == NULL) return; if ((ksp = kstat_lookup (kc, "scfd", 0, SCF_SYSTEM_KSTAT_NAME)) == NULL) { (void) kstat_close(kc); return; } if (kstat_read(kc, ksp, NULL) == -1) { (void) kstat_close(kc); return; } if ((k = (kstat_named_t *)kstat_data_lookup (ksp, SCF_SECURE_MODE_KSTAT_NAMED)) == NULL) { (void) kstat_close(kc); return; } if (k->value.c[0] == SCF_STAT_MODE_LOCK) log_printf("Mode switch is in LOCK mode ", 0); else if (k->value.c[0] == SCF_STAT_MODE_UNLOCK) log_printf("Mode switch is in UNLOCK mode", 0); else log_printf("Mode switch is in UNKNOWN mode", 0); log_printf("\n", 0); (void) kstat_close(kc); } /* * Calls do_devinfo() in order to use the libdevinfo device tree * instead of OBP's device tree. */ int do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag) { v_flag = syserrlog; return (do_devinfo(syserrlog, pgname, log_flag, prt_flag)); } /* * Return the property value for the Prop * passed in. (When using libdevinfo) */ void * get_prop_val(Prop *prop) { if (prop == NULL) return (NULL); return ((void *)(prop->value.val_ptr)); } /* * Return the property size for the Prop * passed in. (When using libdevinfo) */ static int get_prop_size(Prop *prop) { if ((prop != NULL) && (prop->size > 0)) return (prop->size); else return (0); } /* * Search a Prom node and retrieve the property with the correct * name. (When using libdevinfo) */ Prop * find_prop(Prom_node *pnode, char *name) { Prop *prop; if (pnode == NULL) return (NULL); for (prop = pnode->props; prop != NULL; prop = prop->next) { if (prop->name.val_ptr != NULL && strcmp((char *)(prop->name.val_ptr), name) == 0) break; } return (prop); } /* * This function adds a board node to the board structure where that * that node's physical component lives. */ void add_node(Sys_tree *root, Prom_node *pnode) { int board; Board_node *bnode; Prom_node *p; char *type; if ((board = get_board_num(pnode)) == -1) { type = get_node_type(pnode); if ((type != NULL) && (strcmp(type, "cpu") == 0)) board = get_board_num((pnode->parent)->parent); } /* find the node with the same board number */ if ((bnode = find_board(root, board)) == NULL) { bnode = insert_board(root, board); bnode->board_type = UNKNOWN_BOARD; } /* now attach this prom node to the board list */ /* Insert this node at the end of the list */ pnode->sibling = NULL; if (bnode->nodes == NULL) bnode->nodes = pnode; else { p = bnode->nodes; while (p->sibling != NULL) p = p->sibling; p->sibling = pnode; } }