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
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.
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]
23 * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
24 * All rights reserved.
27 #pragma ident "%Z%%M% %I% %E% SMI"
40 #include <sys/param.h>
42 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <sys/openpromio.h>
49 #include <sys/sbd_ioctl.h>
50 #include <sys/sbdp_mem.h>
51 #include <sys/serengeti.h>
55 #include "pdevinfo_sun4u.h"
56 #include "display_sun4u.h"
57 #include "libprtdiag.h"
59 #if !defined(TEXT_DOMAIN)
60 #define TEXT_DOMAIN "SYS_TEST"
64 #define MBYTE (KBYTE * KBYTE)
66 #define MEM_UK_SIZE_MASK 0x3FF
71 static memory_bank_t
*bank_head
;
72 static memory_bank_t
*bank_tail
;
73 static memory_seg_t
*seg_head
;
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);
84 * Used for US-I and US-II systems
88 display_memorysize(Sys_tree
*tree
, struct system_kstat_data
*kstats
,
89 struct grp_info
*grps
, struct mem_total
*memory_total
)
91 log_printf(dgettext(TEXT_DOMAIN
, "Memory size: "), 0);
93 if (sysconf(_SC_PAGESIZE
) == -1 || sysconf(_SC_PHYS_PAGES
) == -1)
94 log_printf(dgettext(TEXT_DOMAIN
, "unable to determine\n"), 0);
99 (uint64_t)sysconf(_SC_PAGESIZE
) * \
100 (uint64_t)sysconf(_SC_PHYS_PAGES
);
102 if (mem_size
>= MBYTE
)
103 log_printf(dgettext(TEXT_DOMAIN
, "%d Megabytes\n"),
104 (int)((mem_size
+MBYTE
-1) / MBYTE
), 0);
106 log_printf(dgettext(TEXT_DOMAIN
, "%d Kilobytes\n"),
107 (int)((mem_size
+KBYTE
-1) / KBYTE
), 0);
113 display_memoryconf(Sys_tree
*tree
, struct grp_info
*grps
)
116 * This function is intentionally blank
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().
128 get_us3_mem_regs(Board_node
*bnode
)
132 uint64_t *ma_reg_arr
;
133 uint64_t madr
[NUM_MBANKS_PER_MC
];
134 void *bank_status_array
;
136 int i
, status_offset
;
138 for (pnode
= dev_find_node(bnode
->nodes
, "memory-controller");
140 pnode
= dev_next_node(pnode
, "memory-controller")) {
142 /* Get portid of this mc from libdevinfo. */
143 portid
= (*(int *)get_prop_val(find_prop(pnode
, "portid")));
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
));
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.
164 if (ma_reg_arr
== NULL
)
168 * The first NUM_MBANKS_PER_MC of uint64_t's in the
169 * logical_bank_ma_regs property are the madr values.
171 for (i
= 0; i
< NUM_MBANKS_PER_MC
; i
++) {
172 madr
[i
] = *ma_reg_arr
++;
176 * Get the bank_status property for this mem controller from
177 * OBP. This contains the bank-status for each logical bank.
179 bank_status_array
= (void *)get_prop_val(
180 find_prop(pnode
, "bank-status"));
184 * process each logical bank
186 for (i
= 0; i
< NUM_MBANKS_PER_MC
; i
++) {
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".
193 if (bank_status_array
) {
194 bank_status
= ((char *)bank_status_array
+
197 /* Move offset to next bank_status string */
198 status_offset
+= (strlen(bank_status
) + 1);
200 bank_status
= malloc(strlen("no_status"));
201 strcpy(bank_status
, "no_status");
205 * create a bank_node for this bank
206 * and add it to the list.
208 add_bank_node(madr
[i
], portid
, bank_status
);
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.
222 add_bank_node(uint64_t mc_decode
, int portid
, char *bank_status
)
225 memory_bank_t
*new, *bank
;
226 uint32_t ifactor
= MC_INTLV(mc_decode
);
229 if ((new = malloc(sizeof (memory_bank_t
))) == NULL
) {
234 new->portid
= portid
;
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
);
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
;
247 new->seg_next
= NULL
;
249 /* Handle the first bank found */
250 if (bank_head
== NULL
) {
256 /* find last bank in list */
261 /* insert this bank into the list */
267 display_us3_banks(void)
269 uint64_t base
, bank_size
;
271 memory_bank_t
*bank
, *tmp_bank
;
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
;
279 if ((bank_head
== NULL
) || (seg_head
== NULL
)) {
280 log_printf("\nCannot find any memory bank/segment info.\n");
284 for (bank
= bank_head
; bank
; bank
= bank
->next
) {
286 * Interleave factor is determined from the
287 * lk bits in the Mem Addr Decode register.
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.
293 * See section 9.1.5 of Cheetah Programmer's reference
296 intlv
= ((bank
->lk
^ 0xF) + 1);
297 base
= bank
->um
& ~(bank
->uk
);
299 mcid
= SG_PORTID_TO_SAFARI_ID(bank
->portid
);
301 /* If bank is not valid, set size to zero incase it's garbage */
303 bank_size
= ((bank
->bank_size
) / MBYTE
);
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.
312 total_bank_size
+= bank_size
;
314 /* Find the matching segment for this bank. */
315 seg
= match_seg(base
);
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.
322 switch ((bank
->id
) % 4) {
324 /* have bank0_size, need bank2_size */
325 bank0_size
= bank_size
;
328 tmp_bank
= bank
->next
;
330 if (tmp_bank
->valid
== 0) {
331 tmp_bank
= tmp_bank
->next
;
334 /* Is next bank on the same mc ? */
335 if (mcid
!= SG_PORTID_TO_SAFARI_ID(
339 if ((tmp_bank
->id
) % 4 == 2) {
341 (tmp_bank
->bank_size
/ MBYTE
);
344 tmp_bank
= tmp_bank
->next
;
347 dimm_size
= (bank0_size
+ bank2_size
) / 4;
349 dimm_size
= bank0_size
/ 2;
352 /* have bank1_size, need bank3_size */
353 bank1_size
= bank_size
;
356 tmp_bank
= bank
->next
;
358 if (tmp_bank
->valid
== 0) {
359 tmp_bank
= tmp_bank
->next
;
362 /* Is next bank on the same mc ? */
363 if (mcid
!= SG_PORTID_TO_SAFARI_ID(
367 if ((tmp_bank
->id
) % 4 == 3) {
369 (tmp_bank
->bank_size
/ MBYTE
);
372 tmp_bank
= tmp_bank
->next
;
375 dimm_size
= (bank1_size
+ bank3_size
) / 4;
377 dimm_size
= bank1_size
/ 2;
380 /* have bank0_size and bank2_size */
381 bank2_size
= bank_size
;
383 dimm_size
= (bank0_size
+ bank2_size
) / 4;
385 dimm_size
= bank2_size
/ 2;
388 /* have bank1_size and bank3_size */
389 bank3_size
= bank_size
;
391 dimm_size
= (bank1_size
+ bank3_size
) / 4;
393 dimm_size
= bank3_size
/ 4;
397 if (bank
->valid
== 0)
401 * Call platform specific code for formatting memory
404 print_us3_memory_line(bank
->portid
, bank
->id
, bank_size
,
405 bank
->bank_status
, dimm_size
, intlv
, seg
->id
);
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.
415 total_sys_mem
= (((uint64_t)sysconf(_SC_PAGESIZE
) * \
416 (uint64_t)sysconf(_SC_PHYS_PAGES
)) / MBYTE
);
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
,
433 memory_bank_t
*bank
= bank_tail
;
435 if (bank
->valid
!= 1)
438 base
= bank
->um
& ~(bank
->uk
);
440 if ((new = match_seg(base
)) == NULL
) {
442 * This bank is part of a new segment, so create
443 * a struct for it and added to the list of segments
445 if ((new = malloc(sizeof (memory_seg_t
))) == NULL
) {
451 new->size
= (((uint64_t)bank
->uk
+1) << 26);
452 new->intlv
= ((bank
->lk
^ 0xF) + 1);
455 * add to the seg list
457 new->next
= seg_head
;
463 * add bank into segs bank list. Note we add at the head
465 bank
->seg_next
= new->banks
;
469 static memory_seg_t
*
470 match_seg(uint64_t base
)
472 memory_seg_t
*cur_seg
;
474 for (cur_seg
= seg_head
; cur_seg
; cur_seg
= cur_seg
->next
) {
475 if (cur_seg
-> base
== base
)
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
)
486 log_printf(dgettext(TEXT_DOMAIN
,
487 "\n No print_us3_memory_line() function specified for"
488 " this platform\n"), 0);
492 display_us3_failed_banks(int system_failed
)
495 int found_failed_bank
= 0;
497 if ((bank_head
== NULL
) || (seg_head
== NULL
)) {
498 log_printf("\nCannot find any memory bank/segment info.\n");
502 for (bank
= bank_head
; bank
; bank
= bank
->next
) {
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.
509 if ((bank
->valid
== 0) &&
510 (strcmp(bank
->bank_status
, "unpopulated"))) {
511 if (!system_failed
&& !found_failed_bank
) {
512 found_failed_bank
= TRUE
;
514 log_printf(dgettext(TEXT_DOMAIN
,
515 "Failed Field Replaceable Units (FRU) in "
517 log_printf("=========================="
518 "====================\n", 0);
521 * Call platform specific code for formatting memory
524 print_us3_failed_memory_line(bank
->portid
, bank
->id
,
528 if (found_failed_bank
)
536 print_us3_failed_memory_line(int portid
, int bank_id
, char *bank_status
)
538 log_printf(dgettext(TEXT_DOMAIN
,
539 "\n No print_us3_failed_memory_line() function specified for"
540 " this platform\n"), 0);