summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorgavinm <none@none>2006-10-05 17:39:16 -0700
committergavinm <none@none>2006-10-05 17:39:16 -0700
commit8a40a695ee676a322b094e9afe5375567bfb51e3 (patch)
treeb59160470b2412ad6a4afc8cf53da85376e7a18a /usr/src
parent6fec36253a9fa8abfdf231fb3ea9fc656a2ff872 (diff)
downloadillumos-joyent-8a40a695ee676a322b094e9afe5375567bfb51e3.tar.gz
PSARC 2006/564 FMA for Athlon 64 and Opteron Rev F/G Processors
PSARC 2006/566 eversholt language enhancements: confprop_defined 6362846 eversholt doesn't allow dashes in pathname components 6391591 AMD NB config should not set NbMcaToMstCpuEn 6391605 AMD DRAM scrubber should be disabled when errata #99 applies 6398506 memory controller driver should not bother to attach at all on rev F 6424822 FMA needs to support AMD family 0xf revs F and G 6443847 FMA x64 multibit ChipKill rules need to follow MQSC guidelines 6443849 Accrue inf_sys and s_ecc ECC errors against memory 6443858 mc-amd can free unitsrtr before usage in subsequent error path 6443891 mc-amd does not recognise mismatched dimm support 6455363 x86 error injector should allow addr option for most errors 6455370 Opteron erratum 101 only applies on revs D and earlier 6455373 Identify chip-select lines used on a dimm 6455377 improve x64 quadrank dimm support 6455382 add generic interfaces for amd chip revision and package/socket type 6468723 mem scheme fmri containment test for hc scheme is busted 6473807 eversholt could use some mdb support 6473811 eversholt needs a confprop_defined function 6473819 eversholt should show version of rules active in DE 6475302 ::nvlist broken by some runtime link ordering changes
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/fm/dicts/AMD.dict5
-rw-r--r--usr/src/cmd/fm/dicts/AMD.po88
-rw-r--r--usr/src/cmd/fm/eversholt/common/check.c12
-rw-r--r--usr/src/cmd/fm/eversholt/common/eftread.c24
-rw-r--r--usr/src/cmd/fm/eversholt/common/eftread.h9
-rw-r--r--usr/src/cmd/fm/eversholt/common/esclex.c27
-rw-r--r--usr/src/cmd/fm/eversholt/common/escparse.y10
-rw-r--r--usr/src/cmd/fm/eversholt/common/literals.h6
-rw-r--r--usr/src/cmd/fm/eversholt/common/tree.c28
-rw-r--r--usr/src/cmd/fm/eversholt/common/tree.h6
-rw-r--r--usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.esc659
-rw-r--r--usr/src/cmd/fm/modules/Makefile.plugin43
-rw-r--r--usr/src/cmd/fm/modules/common/cpumem-retire/cma_main.c18
-rw-r--r--usr/src/cmd/fm/modules/common/cpumem-retire/cma_page.c3
-rw-r--r--usr/src/cmd/fm/modules/common/eversholt/Makefile7
-rw-r--r--usr/src/cmd/fm/modules/common/eversholt/eft_mdb.c192
-rw-r--r--usr/src/cmd/fm/modules/common/eversholt/eval.c77
-rw-r--r--usr/src/cmd/fm/modules/common/eversholt/fme.c4
-rw-r--r--usr/src/cmd/fm/modules/common/eversholt/platform.c74
-rw-r--r--usr/src/cmd/fm/schemes/mem/mem_unum.c7
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c2
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/nvpair.c9
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/nvpair.h9
-rw-r--r--usr/src/cmd/mdb/common/modules/libnvpair/libnvpair.c9
-rw-r--r--usr/src/cmd/mdb/i86pc/modules/amd_opteron/ao.c253
-rw-r--r--usr/src/common/mc/mc-amd/mcamd_api.h135
-rw-r--r--usr/src/common/mc/mc-amd/mcamd_misc.c79
-rw-r--r--usr/src/common/mc/mc-amd/mcamd_patounum.c446
-rw-r--r--usr/src/common/mc/mc-amd/mcamd_rowcol.c404
-rw-r--r--usr/src/common/mc/mc-amd/mcamd_rowcol_impl.h68
-rw-r--r--usr/src/common/mc/mc-amd/mcamd_rowcol_tbl.c750
-rw-r--r--usr/src/common/mc/mc-amd/mcamd_unumtopa.c33
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/hc_canon.h2
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/mem.c66
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_protocol.c103
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/chip/chip.c675
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/chip/chip.h17
-rw-r--r--usr/src/pkgdefs/SUNWfmd/prototype_com1
-rw-r--r--usr/src/uts/i86pc/Makefile.files4
-rw-r--r--usr/src/uts/i86pc/Makefile.workarounds5
-rw-r--r--usr/src/uts/i86pc/cpu/amd_opteron/ao.h45
-rw-r--r--usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c58
-rw-r--r--usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c29
-rw-r--r--usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c550
-rw-r--r--usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in15
-rw-r--r--usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c107
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd.h136
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.c435
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.h73
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_dimmcfg_impl.h91
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_drv.c1103
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_off.in43
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_pcicfg.c99
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_pcicfg.h56
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_subr.c314
-rw-r--r--usr/src/uts/i86pc/mc-amd/Makefile1
-rw-r--r--usr/src/uts/i86pc/os/cmi.c25
-rw-r--r--usr/src/uts/i86pc/os/cpuid.c153
-rw-r--r--usr/src/uts/i86pc/sys/cpu_module.h2
-rw-r--r--usr/src/uts/i86pc/sys/cpu_module_impl.h2
-rw-r--r--usr/src/uts/intel/sys/fm/cpu/AMD.h32
-rw-r--r--usr/src/uts/intel/sys/mc.h17
-rw-r--r--usr/src/uts/intel/sys/mc_amd.h694
-rw-r--r--usr/src/uts/intel/sys/mca_amd.h137
-rw-r--r--usr/src/uts/intel/sys/x86_archext.h87
65 files changed, 6603 insertions, 2070 deletions
diff --git a/usr/src/cmd/fm/dicts/AMD.dict b/usr/src/cmd/fm/dicts/AMD.dict
index 5a3e0f0321..8ab66b836a 100644
--- a/usr/src/cmd/fm/dicts/AMD.dict
+++ b/usr/src/cmd/fm/dicts/AMD.dict
@@ -45,3 +45,8 @@ fault.cpu.amd.dcachestag=14
fault.cpu.amd.l1dtlb=15
fault.cpu.amd.l2dtlb=16
fault.cpu.amd.datapath=17
+fault.cpu.amd.dramchannel=18
+fault.memory.dimm_testfail=19
+fault.memory.page_sb=20
+fault.memory.page_ck=21
+fault.memory.page_ue=22
diff --git a/usr/src/cmd/fm/dicts/AMD.po b/usr/src/cmd/fm/dicts/AMD.po
index 3fd66573a8..ef8254d4a1 100644
--- a/usr/src/cmd/fm/dicts/AMD.po
+++ b/usr/src/cmd/fm/dicts/AMD.po
@@ -288,12 +288,92 @@ msgstr "Schedule a repair procedure to replace the affected CPU. Use fmdump -v
msgid "AMD-8000-JF.type"
msgstr "Fault"
msgid "AMD-8000-JF.severity"
-msgstr "Major"
+msgstr "Minor"
msgid "AMD-8000-JF.description"
-msgstr "The number of errors associated with this CPU has exceeded acceptable levels. Refer to %s for more information."
+msgstr "(This is a misdiagnosis of a potential main memory (DIMM) fault as a CPU cache fault. Ignore this fault and look for memory errors and faults from the DIMMS associated with this CPU.)\n Refer to %s for more information."
msgid "AMD-8000-JF.response"
-msgstr "An attempt will be made to remove this CPU from service."
+msgstr "A cpu has likely been offlined in response to this misdiagnosis.\n"
msgid "AMD-8000-JF.impact"
msgstr "Performance of this system may be affected."
msgid "AMD-8000-JF.action"
-msgstr "Schedule a repair procedure to replace the affected CPU. Use fmdump -v -u <EVENT_ID> to identify the module."
+msgstr "Online any CPU that was automatically offlined in response to this fault\n(use fmdump -v -u <EVENT_ID> to identify the CPU). The fault\nshould also be repaired with fmadm repair <EVENT_ID>.\nThis misdiagnosis bug is being fixed - patch numbers for Solaris 10\nwill appear here when aavailable.\n\n"
+#
+# code: AMD-8000-KW
+# keys: fault.cpu.amd.dramchannel
+#
+msgid "AMD-8000-KW.type"
+msgstr "Fault"
+msgid "AMD-8000-KW.severity"
+msgstr "Major"
+msgid "AMD-8000-KW.description"
+msgstr "A memory channel is experiencing errors from sources outside of the memory modules (DIMMs).\n Refer to %s for more information."
+msgid "AMD-8000-KW.response"
+msgstr "None - there is no hardware resource that the system can disable to attempt to isolate this fault.\n"
+msgid "AMD-8000-KW.impact"
+msgstr "A faulty DRAM channel could lead to large numbers of correctable errors, some uncorrectable errors which will affect system availability, and perhaps even some undetectable errors and consequent data corruption.\n"
+msgid "AMD-8000-KW.action"
+msgstr "Schedule system maintenance time as soon as possible and investigate the affected DRAM channel. Use fmdump -v -u <EVENT_ID> to identify the channel.\n"
+#
+# code: AMD-8000-L1
+# keys: fault.memory.dimm_testfail
+#
+msgid "AMD-8000-L1.type"
+msgstr "Fault"
+msgid "AMD-8000-L1.severity"
+msgstr "Major"
+msgid "AMD-8000-L1.description"
+msgstr "The system BIOS has failed one or more ranks of this DIMM during test.\n Refer to %s for more information."
+msgid "AMD-8000-L1.response"
+msgstr "The BIOS has not configured the affected rank(s) into the system memory\nmap.\n"
+msgid "AMD-8000-L1.impact"
+msgstr "Available system memory is substantially reduced"
+msgid "AMD-8000-L1.action"
+msgstr "Schedule a repair procedure to replace the affected memory module. Use fmdump -v -u <EVENT_ID> to identify the module."
+#
+# code: AMD-8000-MU
+# keys: fault.memory.page_sb
+#
+msgid "AMD-8000-MU.type"
+msgstr "Fault"
+msgid "AMD-8000-MU.severity"
+msgstr "Minor"
+msgid "AMD-8000-MU.description"
+msgstr "The number of errors associated with this memory page has exceeded acceptable levels. Refer to %s for more information."
+msgid "AMD-8000-MU.response"
+msgstr "An attempt will be made to remove this memory page from service."
+msgid "AMD-8000-MU.impact"
+msgstr "The performance of the system may be minimally impacted as a result of removing the memory page from operation."
+msgid "AMD-8000-MU.action"
+msgstr "No repair action is recommended at this time."
+#
+# code: AMD-8000-N7
+# keys: fault.memory.page_ck
+#
+msgid "AMD-8000-N7.type"
+msgstr "Fault"
+msgid "AMD-8000-N7.severity"
+msgstr "Minor"
+msgid "AMD-8000-N7.description"
+msgstr "The number of errors associated with this memory page has exceeded acceptable levels. Refer to %s for more information."
+msgid "AMD-8000-N7.response"
+msgstr "An attempt will be made to remove this memory page from service."
+msgid "AMD-8000-N7.impact"
+msgstr "The performance of the system may be minimally impacted as a result of removing the memory page from operation."
+msgid "AMD-8000-N7.action"
+msgstr "No repair action is recommended at this time."
+#
+# code: AMD-8000-PM
+# keys: fault.memory.page_ue
+#
+msgid "AMD-8000-PM.type"
+msgstr "Fault"
+msgid "AMD-8000-PM.severity"
+msgstr "Major"
+msgid "AMD-8000-PM.description"
+msgstr "The number of errors associated with this memory page has exceeded acceptable levels. Refer to %s for more information."
+msgid "AMD-8000-PM.response"
+msgstr "An attempt will be made to remove this memory page from service."
+msgid "AMD-8000-PM.impact"
+msgstr "The performance of the system may be minimally impacted as a result of removing the memory page from operation."
+msgid "AMD-8000-PM.action"
+msgstr "No repair action is recommended at this time."
diff --git a/usr/src/cmd/fm/eversholt/common/check.c b/usr/src/cmd/fm/eversholt/common/check.c
index 4c82d3cd6c..452ddd84dc 100644
--- a/usr/src/cmd/fm/eversholt/common/check.c
+++ b/usr/src/cmd/fm/eversholt/common/check.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -1133,7 +1132,8 @@ check_func(struct node *np)
np->u.func.arglist->line,
"confcall(): first argument must be a string "
"(the name of the operation)");
- } else if (np->u.func.s == L_confprop) {
+ } else if (np->u.func.s == L_confprop ||
+ np->u.func.s == L_confprop_defined) {
if (np->u.func.arglist->t == T_LIST &&
(np->u.func.arglist->u.expr.left->t == T_FUNC &&
(np->u.func.arglist->u.expr.left->u.func.s == L_fru ||
@@ -1143,9 +1143,9 @@ check_func(struct node *np)
} else {
outfl(O_ERR, np->u.func.arglist->file,
np->u.func.arglist->line,
- "confprop(): first argument must be a call to "
+ "%s(): first argument must be a call to "
"fru() or asru(); "
- "second argument must be a string");
+ "second argument must be a string", np->u.func.s);
}
} else if (np->u.func.s == L_count) {
if (np->u.func.arglist->t != T_EVENT) {
diff --git a/usr/src/cmd/fm/eversholt/common/eftread.c b/usr/src/cmd/fm/eversholt/common/eftread.c
index d4e95a4b7b..c4d011db27 100644
--- a/usr/src/cmd/fm/eversholt/common/eftread.c
+++ b/usr/src/cmd/fm/eversholt/common/eftread.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* eftread.c -- routines for reading .eft files
@@ -52,6 +51,8 @@
#include <netinet/in.h>
#include <inttypes.h>
+#define MIN(x, y) ((x) <= (y) ? (x) : (y))
+
static int Showheader;
/*
@@ -70,7 +71,7 @@ eftread_showheader(int newval)
*/
FILE *
-eftread_fopen(const char *fname)
+eftread_fopen(const char *fname, char *idbuf, size_t idbufsz)
{
FILE *fp;
FILE *tfp;
@@ -124,9 +125,16 @@ eftread_fopen(const char *fname)
return (NULL);
}
- /* skip over ident strings */
- if (fseek(fp, (long)hdr.identlen, SEEK_CUR) == -1)
- out(O_DIE|O_SYS, "%s: fseek", fname);
+ bzero(idbuf, idbufsz);
+ if (hdr.identlen != 0) {
+ long npos = ftell(fp) + (long)hdr.identlen; /* after ident */
+ size_t rsz = MIN(hdr.identlen, idbufsz - 1);
+
+ if (fread(idbuf, 1, rsz, fp) != rsz)
+ out(O_DIE|O_SYS, "%s: fread", fname);
+ if (fseek(fp, npos, SEEK_SET) == -1)
+ out(O_DIE|O_SYS, "%s: fseek", fname);
+ }
if (hdr.dictlen && (hdr.dictlen < 2 || hdr.dictlen > 1000)) {
(void) fclose(fp);
diff --git a/usr/src/cmd/fm/eversholt/common/eftread.h b/usr/src/cmd/fm/eversholt/common/eftread.h
index 45efeb5719..b45f6d7734 100644
--- a/usr/src/cmd/fm/eversholt/common/eftread.h
+++ b/usr/src/cmd/fm/eversholt/common/eftread.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* eftread.h -- public definitions for eftread module
@@ -37,7 +36,7 @@ extern "C" {
#endif
void eftread_showheader(int newval);
-FILE *eftread_fopen(const char *fname);
+FILE *eftread_fopen(const char *fname, char *idbuf, size_t idbufsz);
#ifdef __cplusplus
}
diff --git a/usr/src/cmd/fm/eversholt/common/esclex.c b/usr/src/cmd/fm/eversholt/common/esclex.c
index 89faae18b8..4fdcdbaf58 100644
--- a/usr/src/cmd/fm/eversholt/common/esclex.c
+++ b/usr/src/cmd/fm/eversholt/common/esclex.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -65,6 +64,7 @@ struct stats *Filecount;
struct filestats {
struct filestats *next;
struct stats *stats;
+ struct stats *idstats;
} *Fstats;
static int Errcount;
@@ -296,6 +296,8 @@ yylex()
for (;;) {
while (Fp == NULL) {
+ char ibuf[80];
+
if (*Files == NULL)
return (record(EOF, NULL));
Fileopened = stable(*Files++);
@@ -305,7 +307,7 @@ yylex()
if ((Fp = popen(Tok, "r")) == NULL)
out(O_DIE|O_SYS, "%s", Tok);
#else
- Fp = eftread_fopen(Fileopened);
+ Fp = eftread_fopen(Fileopened, ibuf, sizeof (ibuf));
#endif /* ESC */
Line = 1;
bol = 1;
@@ -315,11 +317,24 @@ yylex()
static int fnum;
char nbuf[100];
struct filestats *nfs = MALLOC(sizeof (*nfs));
- (void) sprintf(nbuf, "lex.file%d", fnum++);
+
+ (void) sprintf(nbuf, "lex.file%d", fnum);
nfs->stats = stats_new_string(nbuf, "", 0);
stats_string_set(nfs->stats, Fileopened);
+
+ if (ibuf[0] != '\0') {
+ (void) sprintf(nbuf, "lex.file%d-ident",
+ fnum);
+ nfs->idstats =
+ stats_new_string(nbuf, "", 0);
+ stats_string_set(nfs->idstats, ibuf);
+ } else {
+ nfs->idstats = NULL;
+ }
+
nfs->next = Fstats;
Fstats = nfs;
+ fnum++;
}
}
@@ -946,6 +961,8 @@ lex_free(void)
while (nfstats != NULL) {
Fstats = nfstats->next;
stats_delete(nfstats->stats);
+ if (nfstats->idstats != NULL)
+ stats_delete(nfstats->idstats);
FREE(nfstats);
nfstats = Fstats;
}
diff --git a/usr/src/cmd/fm/eversholt/common/escparse.y b/usr/src/cmd/fm/eversholt/common/escparse.y
index 2e88efdc1a..8f48020ebc 100644
--- a/usr/src/cmd/fm/eversholt/common/escparse.y
+++ b/usr/src/cmd/fm/eversholt/common/escparse.y
@@ -3,9 +3,8 @@
* 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.
+ * 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.
@@ -398,6 +397,11 @@ iterid : ID
tree_name($1.s, IT_HORIZONTAL, $1.file, $1.line),
tree_name($3.s, IT_NONE, $3.file, $3.line));
}
+ | ID '-' iterid
+ {
+ /* hack to allow dashes in path name components */
+ $$ = tree_name_repairdash2($1.s, $3);
+ }
;
/* iname is an ID where we can peel numbers off the end */
diff --git a/usr/src/cmd/fm/eversholt/common/literals.h b/usr/src/cmd/fm/eversholt/common/literals.h
index bfb87a8ef5..6c7cb67128 100644
--- a/usr/src/cmd/fm/eversholt/common/literals.h
+++ b/usr/src/cmd/fm/eversholt/common/literals.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -152,6 +151,7 @@ L_DECL(within);
L_DECL(call);
L_DECL(confcall);
L_DECL(confprop);
+L_DECL(confprop_defined);
L_DECL(defined);
L_DECL(payloadprop);
L_DECL(payloadprop_contains);
diff --git a/usr/src/cmd/fm/eversholt/common/tree.c b/usr/src/cmd/fm/eversholt/common/tree.c
index 064da60795..6aa5f1f672 100644
--- a/usr/src/cmd/fm/eversholt/common/tree.c
+++ b/usr/src/cmd/fm/eversholt/common/tree.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -616,6 +615,29 @@ tree_name_repairdash(struct node *np, const char *s)
}
struct node *
+tree_name_repairdash2(const char *s, struct node *np)
+{
+ int len;
+ char *buf;
+
+ ASSERT(np != NULL && s != NULL);
+
+ if (np->t != T_NAME)
+ outfl(O_DIE, np->file, np->line,
+ "tree_name_repairdash: internal error (np type %d)",
+ np->t);
+
+ ASSERT(np->u.name.last != NULL);
+
+ len = strlen(np->u.name.last->u.name.s) + 1 + strlen(s) + 1;
+ buf = MALLOC(len);
+ (void) snprintf(buf, len, "%s-%s", s, np->u.name.last->u.name.s);
+ np->u.name.last->u.name.s = stable(buf);
+ FREE(buf);
+ return (np);
+}
+
+struct node *
tree_name_iterator(struct node *np1, struct node *np2)
{
ASSERT(np1 != NULL);
diff --git a/usr/src/cmd/fm/eversholt/common/tree.h b/usr/src/cmd/fm/eversholt/common/tree.h
index c3a6f49845..79697e88c8 100644
--- a/usr/src/cmd/fm/eversholt/common/tree.h
+++ b/usr/src/cmd/fm/eversholt/common/tree.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -273,6 +272,7 @@ struct node *tree_iname(const char *s, const char *file, int line);
struct node *tree_globid(const char *s, const char *file, int line);
struct node *tree_name_append(struct node *np1, struct node *np2);
struct node *tree_name_repairdash(struct node *np1, const char *s);
+struct node *tree_name_repairdash2(const char *s, struct node *np1);
struct node *tree_name_iterator(struct node *np1, struct node *np2);
struct node *tree_timeval(const char *s, const char *suffix,
const char *file, int line);
diff --git a/usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.esc b/usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.esc
index 4e27233632..0bf5f5d600 100644
--- a/usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.esc
+++ b/usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.esc
@@ -32,14 +32,20 @@
* Eversholt rules for the AMD Opteron CPU/Memory
*/
+fru motherboard;
+fru chip;
fru dimm;
-asru dimm;
-fru chip;
asru chip/cpu;
-
+asru dimm;
+asru dimm/rank;
+asru dram-channel;
+asru chip/memory-controller/chip-select;
-/* #MEM#
+#define MAX(x, y) ((x) >= (y) ? (x) : (y))
+#define MIN(x, y) ((x) <= (y) ? (x) : (y))
+
+/*
* GET_ADDR relies on the fact that variables have global scope across an FME.
* Thus for each FME the assignment only occurs for the first invocation
* but the comparison happens on each. Thus if the new address matches the
@@ -53,36 +59,43 @@ asru chip/cpu;
#define GET_OFFSET ($offset = payloadprop("resource[0].hc-specific.offset"))
/*
- * SET_ADDR is used to set a payload value in the fault that we diagnose
- * for page faults, to record the physical address of the faulting page.
+ * SET_ADDR and SET_OFFSET are used to set a payload value in the fault that
+ * we diagnose for page faults, to record the physical address of the faulting
+ * page. The "asru-" prefix is hooked in the "rewrite-ASRU" confcalls made on
+ * diagnosis of associated faults when the libtopo mem scheme rewrites the
+ * asru in "mem" scheme.
*/
#define SET_ADDR (setpayloadprop("asru-physaddr", $addr))
#define SET_OFFSET (setpayloadprop("asru-offset", $offset))
/*
- * RESOURCE_EXISTS is true if a pair with name "resource" exists in the
+ * RESOURCE_EXISTS is true if a member with name "resource" exists in the
* payload - regardless of type (e.g., nvlist or nvlist array) or value.
*/
#define RESOURCE_EXISTS (payloadprop_defined("resource"))
/*
- * CONTAINS_DIMM is true if the "resource" nvlist array (as used in memory
+ * CONTAINS_RANK is true if the "resource" nvlist array (as used in memory
* ereports) exists and one if its members matches the path for the
- * dimm node. Our memory propogation are of the form "foo@dimm -> blah@cpu"
+ * rank node. Our memory propogation are of the form
+ *
+ * "prop foo@chip/memory-controller/dimm/rank -> blah@chip/cpu"
+ *
* since cpus detect memory errors; in eversholt such a propogation, where
* the lhs path and rhs path do not match, expands to the cross-product of
- * all dimms and cpus in the system. We use CONTAINS_DIMM to constrain
- * the propogation such that it only happens if the payload resource
- * matches the dimm.
+ * all dimms, ranks and cpus on the same chip (since chip appears in the
+ * path on both sides). We use CONTAINS_RANK to constrain the propogation
+ * such that it only happens if the payload resource matches the rank.
*/
-#define CONTAINS_DIMM (payloadprop_contains("resource", asru(dimm)))
+#define CONTAINS_RANK (payloadprop_contains("resource", \
+ asru(chip/memory-controller/dimm/rank)))
/*
* The following will tell us whether a syndrome that is known to be
- * correctable (from a mem_ecc1) is single-bit or multi-bit. For a
+ * correctable (from a mem_ce ereport) is single-bit or multi-bit. For a
* correctable ChipKill syndrome the number of bits set in the lowest
- * nibble indicates how many bit were in error.
+ * nibble indicates how many bits were in error.
*/
#define CBITMASK(synd) ((synd) & 0xf)
@@ -102,207 +115,450 @@ asru chip/cpu;
!CKSINGLE(payloadprop("syndrome")))
/*
- * A single bit fault in a memory dimm can cause:
+ * A single bit fault in a memory rank can cause:
*
- * - mem_ce : reported by nb for an access from a remote cpu
+ * - mem_ce : reported by nb
+ * - inf_sys_ecc1: reported by ic or dc; inf_sys_ecc1 errors detected at the
+ * ic do not record a syndrome; these errors will not be triggered in
+ * ChipKill ECC mode (the NB corrects all ECC errors in that mode)
+ * - s_ecc1: reported by bu; this error will not be triggered in ChipKill
+ * ECC mode (the NB corrects all ECC in that mode)
*
- * Single-bit errors are fed into a per-DIMM SERD engine; if a SERD engine
+ * Single-bit errors are fed into a per-rank SERD engine; if a SERD engine
* trips we diagnose a fault.memory.page so that the response agent can
* retire the page that caused the trip. If the total number of pages
- * faulted in this way on a single DIMM exceeds a threshold we will
- * diagnose a fault.memory.dimm_sb against the DIMM.
+ * faulted in this way on a single rank exceeds a threshold we will
+ * diagnose a fault.memory.dimm_sb against the containing.
*
- * Multibit ChipKill-correctable errors produce an immediate page fault.
- * This is achieved through SERD engines using N=0 so the facility is there
- * to be a little more tolerant of these errors in future.
+ * Multibit ChipKill-correctable errors are treated identically to
+ * single-bit errors, but via separate serd engines to allow distinct
+ * parameters if desired.
*
* Uncorrectable errors produce an immediate page fault and corresponding
* fault.memory.dimm_ue.
*
* Page faults are essentially internal - action is only required when
* they are accompanied by a dimm fault. As such we include message=0
- * on DIMM faults.
+ * on page faults.
*/
-event ereport.cpu.amd.nb.mem_ce@cpu;
+event ereport.cpu.amd.ic.inf_sys_ecc1@chip/cpu{within(5s)};
+event ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu{within(5s)};
+event ereport.cpu.amd.bu.s_ecc1@chip/cpu{within(5s)};
+event ereport.cpu.amd.nb.mem_ce@chip/cpu{within(5s)};
/*
* If the address is not valid then no resource member will be included
* in a nb.mem_ce or nb.mem_ue ereport. These cases should be rare.
+ * We will also discard all inf_sys_ecc1 events detected at the ic since they
+ * have no syndrome and therefore no resource information.
* We will discard such ereports. An alternative may be to SERD them
* on a per MC basis and trip if we see too many such events.
*/
-event upset.memory.discard@cpu;
+event upset.memory.discard1@chip/cpu;
/* #PAGE#
- * Page faults of all types diagnose to a single fault class and are
- * counted with a stat.
+ * Single-bit correctable errors are diagnosed as upsets and feed into per-rank
+ * SERD engines which diagnose fault.memory.page_sb if they trip.
+ *
+ * Multi-bit correctable (via ChipKill) errors are diagnosed as upsets and feed
+ * into additional per-rank SERD engines which diagnose fault.memory.page_ck
+ * if they trip.
*
- * Single-bit errors are diagnosed as upsets and feed into per-DIMM
- * SERD engines which diagnose fault.memory.page if they trip.
+ * The number of fault.memory.page and fault.memory.page_ck diagnosed is
+ * counted in stat engines for each type. These are used in deciding
+ * whether to declare a dimm faulty after repeated page faults.
*/
#define PAGE_FIT 1
#define PAGE_SB_COUNT 2
#define PAGE_SB_TIME 72h
-#define PAGE_CK_COUNT 0
-#define PAGE_CK_TIME 1h
-
-engine stat.page_fault@dimm;
-event fault.memory.page@dimm, FITrate=PAGE_FIT,
- ASRU=dimm, message=0, count=stat.page_fault@dimm,
- action=confcall("rewrite-ASRU");
-event error.memory.page_sb@dimm;
-event error.memory.page_ck@dimm;
-event error.memory.page_ue@dimm;
-
-prop fault.memory.page@dimm (1)->
- error.memory.page_sb@dimm,
- error.memory.page_ck@dimm,
- error.memory.page_ue@dimm;
-
-event ereport.memory.page_sb_trip@dimm;
-engine serd.memory.page_sb@dimm, N=PAGE_SB_COUNT, T=PAGE_SB_TIME,
- method=persistent, trip=ereport.memory.page_sb_trip@dimm;
-event upset.memory.page_sb@dimm, engine=serd.memory.page_sb@dimm;
-
-event ereport.memory.page_ck_trip@dimm;
-engine serd.memory.page_ck@dimm, N=PAGE_CK_COUNT, T=PAGE_CK_TIME,
- method=persistent, trip=ereport.memory.page_ck_trip@dimm;
-event upset.memory.page_ck@dimm, engine=serd.memory.page_ck@dimm;
-
-prop upset.memory.page_sb@dimm (0)->
- ereport.cpu.amd.nb.mem_ce@cpu { CONTAINS_DIMM && SINGLE_BIT_CE };
-
-prop upset.memory.page_ck@dimm (0)->
- ereport.cpu.amd.nb.mem_ce@cpu { CONTAINS_DIMM && MULTI_BIT_CE };
-
-prop error.memory.page_sb@dimm (1)->
- ereport.memory.page_sb_trip@dimm;
-
-prop error.memory.page_ck@dimm (1)->
- ereport.memory.page_ck_trip@dimm;
-
-prop fault.memory.page@dimm { SET_ADDR && SET_OFFSET } (0)->
- ereport.cpu.amd.nb.mem_ce@cpu { CONTAINS_DIMM && GET_ADDR && GET_OFFSET };
-
-prop upset.memory.discard@cpu (1)->
- ereport.cpu.amd.nb.mem_ce@cpu { !RESOURCE_EXISTS };
-
-/* #DIMM_SB#
- * Single-bit DIMM faults are diagnosed when the number of page faults
- * (of all types since they all are counted in a single per-DIMM stat engine)
- * reaches a threshold. Since our tolerance of ChipKill and UE faults
- * is much lower than that for single-bit errors the threshold will only be
- * reached for repeated single-bit page faults. We do not stop diagnosing
- * further single-bit page faults once we have declared a single-bit DIMM
- * fault - we continue diagnosing them and response agents can continue to
- * retire those pages up to the system-imposed retirement limit.
+#define PAGE_CK_COUNT 2
+#define PAGE_CK_TIME 72h
+
+/*
+ * The fraction of pages on a single rank that must be diagnosed as faulty
+ * with single correctable unit faults before we will fault the rank.
+ * Once we have faulted the rank we will continue to diagnose any further page
+ * faults on the rank up to some maximum multiple of the threshold at which
+ * we faulted the dimm. This allows us to potentially contain some fairly
+ * far-reaching but still limited-extent fault (such as a partial column
+ * failure) without getting carried away and allowing a single faulty rank to
+ * use up the entire system-imposed page retirenment limit (which, once
+ * reached, causes retirement request to have no effect other than to fill
+ * the fault manager cache and logs).
+ *
+ * This fraction is specified in basis points, where 100 basis points are
+ * equivalent to 1 percent. It is applied on a per-rank basis.
*
- * We maintain a parallel SERD engine to the page_sb engine which trips
- * in unison, but on trip it generates a distinct ereport which we
- * diagnose to a dimm_sb fault if the threshold has been reached, or
- * to a throwaway upset if not.
+ * The system imposes an absolute maximum on the number of pages it will
+ * retire; the current value is 10 basis points, or 0.1% of 'physmem'. Note
+ * that 'physmem' is reduced from installed memory pages by an amount
+ * reflecting permanent kernel memory allocations. This system page retire
+ * limit bounds the maximum real response to page faults across all ranks
+ * that fault manager response agents can effect, but it should not be confused
+ * with any diagnosis threshold (i.e., the number of faulty pages we are
+ * prepared to tolerate from a single rank before faulting the rank is
+ * distinct from the total number of pages we are prepared to retire from use
+ * in response to that and other faults). It is, however, desirable to
+ * arrange that the maximum number of pages we are prepared to fault from
+ * any one rank is less than the system-wide quota.
*/
+#define PAGE_RETIRE_LIMIT_BPS 5 /* or 0.05%; ~ 131 pages/GB %/
-#define DIMM_SB_FIT 2000
-#define DIMM_SB_THRESH 128
-
-event fault.memory.dimm_sb@dimm, FITrate=DIMM_SB_FIT, FRU=dimm, ASRU=dimm,
- action=confcall("rewrite-ASRU");
-
-event ereport.memory.dimm_sb_trip@dimm;
-event upset.memory.discard@dimm;
-engine serd.memory.dimm_sb@dimm, N=PAGE_SB_COUNT, T=PAGE_SB_TIME,
- method=persistent, trip=ereport.memory.dimm_sb_trip@dimm;
-event upset.memory.dimm_sb@dimm, engine=serd.memory.dimm_sb@dimm;
-
-prop upset.memory.dimm_sb@dimm (0)->
- ereport.cpu.amd.nb.mem_ce@cpu { CONTAINS_DIMM }; /* sb and ck */
-
-prop upset.memory.discard@dimm (1)->
- ereport.memory.dimm_sb_trip@dimm;
-
-prop fault.memory.dimm_sb@dimm (0)->
- ereport.memory.dimm_sb_trip@dimm {
- count(stat.page_fault@dimm) >= DIMM_SB_THRESH };
-
-/* #DIMM_CK#
- * ChipKill-correctable multi-bit errors produce immediate page faults.
- * If the fault is indeed isolated to just a few cells then we have contained
- * the error; if not, say if the SDRAM device is failing, then we will hit a
- * number of other similar errors in a short space of time. Thus we will
- * SERD these in diagnosing a fault.memory.dimm_ck and not simply fault
- * the DIMM at the first instance.
+/*
+ * A macro to manipulate the above fraction. Given a size in bytes convert
+ * this to pages (4K pagesize) and calculate the number of those pages
+ * indicated by PAGE_RETIRE_LIMIT_BPS basis points.
+ */
+#define _BPS_PGCNT(totalbytes) \
+ ((((totalbytes) / 4096 ) * PAGE_RETIRE_LIMIT_BPS) / 10000)
+
+/*
+ * The single-correctable-unit threshold at which number of faulted pages
+ * on a rank we we fault the rank. We insist that this be at least 128 and
+ * never more than 512.
+ */
+#define RANK_THRESH MIN(512, MAX(128, \
+ _BPS_PGCNT(confprop(asru(chip/memory-controller/dimm/rank), "size"))))
+
+/*
+ * The maximum number of single-correctable-unit page faults we will diagnose
+ * on a single rank (must be greater than RANK_THRESH). We set
+ * this at twice the rank fault threshold.
+ */
+#define RANK_PGFLT_MAX (2 * RANK_THRESH)
+
+engine stat.sbpgflt@chip/memory-controller/dimm/rank;
+engine stat.ckpgflt@chip/memory-controller/dimm/rank;
+
+event fault.memory.page_sb@chip/memory-controller/dimm/rank,
+ FITrate=PAGE_FIT, ASRU=dimm/rank, message=0,
+ count=stat.sbpgflt@chip/memory-controller/dimm/rank,
+ action=confcall("rewrite-ASRU"); /* rewrite ASRU to identify page in rank */
+
+#define SB_PGFLTS (count(stat.sbpgflt@chip/memory-controller/dimm/rank))
+
+event fault.memory.page_ck@chip/memory-controller/dimm/rank,
+ FITrate=PAGE_FIT, ASRU=dimm/rank, message=0,
+ count=stat.ckpgflt@chip/memory-controller/dimm/rank,
+ action=confcall("rewrite-ASRU"); /* rewrite ASRU to identify page in rank */
+
+#define CK_PGFLTS (count(stat.ckpgflt@chip/memory-controller/dimm/rank))
+
+#define RANK_PGFLT_LIMIT_REACHED \
+ (SB_PGFLTS + CK_PGFLTS > RANK_PGFLT_MAX)
+
+event ereport.memory.page_sb_trip@chip/memory-controller/dimm/rank;
+engine serd.memory.page_sb@chip/memory-controller/dimm/rank,
+ N=PAGE_SB_COUNT, T=PAGE_SB_TIME, method=persistent,
+ trip=ereport.memory.page_sb_trip@chip/memory-controller/dimm/rank;
+event upset.memory.page_sb@chip/memory-controller/dimm/rank,
+ engine=serd.memory.page_sb@chip/memory-controller/dimm/rank;
+
+event ereport.memory.page_ck_trip@chip/memory-controller/dimm/rank;
+engine serd.memory.page_ck@chip/memory-controller/dimm/rank,
+ N=PAGE_CK_COUNT, T=PAGE_CK_TIME, method=persistent,
+ trip=ereport.memory.page_ck_trip@chip/memory-controller/dimm/rank;
+event upset.memory.page_ck@chip/memory-controller/dimm/rank,
+ engine=serd.memory.page_ck@chip/memory-controller/dimm/rank;
+
+event upset.memory.overpgfltlimit@chip/memory-controller/dimm/rank;
+
+/*
+ * If we have not reached the per-rank limit on faulted pages then
+ * continue to explain ereport observations as upsets which can lead
+ * lead to page fault diagnoses if the serd engine trips.
+ */
+prop upset.memory.page_sb@chip/memory-controller/dimm/rank (0)->
+ ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu
+ { CONTAINS_RANK && SINGLE_BIT_CE && !RANK_PGFLT_LIMIT_REACHED },
+ ereport.cpu.amd.bu.s_ecc1@chip/cpu
+ { CONTAINS_RANK && SINGLE_BIT_CE && !RANK_PGFLT_LIMIT_REACHED },
+ ereport.cpu.amd.nb.mem_ce@chip/cpu
+ { CONTAINS_RANK && SINGLE_BIT_CE && !RANK_PGFLT_LIMIT_REACHED };
+
+prop upset.memory.page_ck@chip/memory-controller/dimm/rank (0)->
+ /* no dc.inf_sys_ecc1 or bu.s_ecc1 in ChipKill mode */
+ ereport.cpu.amd.nb.mem_ce@chip/cpu
+ { CONTAINS_RANK && MULTI_BIT_CE && !RANK_PGFLT_LIMIT_REACHED };
+
+/*
+ * If we have reached the per-rank limit on faulted pages then diagnose
+ * further observations on the rank to a engine-less upset (i.e., discard
+ * them).
+ */
+prop upset.memory.overpgfltlimit@chip/memory-controller/dimm/rank (1)->
+ ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu
+ { CONTAINS_RANK && RANK_PGFLT_LIMIT_REACHED },
+ ereport.cpu.amd.bu.s_ecc1@chip/cpu
+ { CONTAINS_RANK && RANK_PGFLT_LIMIT_REACHED },
+ ereport.cpu.amd.nb.mem_ce@chip/cpu
+ { CONTAINS_RANK && RANK_PGFLT_LIMIT_REACHED };
+
+prop fault.memory.page_sb@chip/memory-controller/dimm/rank (1)->
+ ereport.memory.page_sb_trip@chip/memory-controller/dimm/rank;
+
+prop fault.memory.page_ck@chip/memory-controller/dimm/rank (1)->
+ ereport.memory.page_ck_trip@chip/memory-controller/dimm/rank;
+
+prop fault.memory.page_sb@chip/memory-controller/dimm/rank
+ { SET_ADDR && SET_OFFSET } (0)->
+ ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET },
+ ereport.cpu.amd.bu.s_ecc1@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET },
+ ereport.cpu.amd.nb.mem_ce@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET };
+
+prop fault.memory.page_ck@chip/memory-controller/dimm/rank
+ { SET_ADDR && SET_OFFSET } (0)->
+ ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET },
+ ereport.cpu.amd.bu.s_ecc1@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET },
+ ereport.cpu.amd.nb.mem_ce@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET };
+
+prop upset.memory.discard1@chip/cpu (1)->
+ ereport.cpu.amd.ic.inf_sys_ecc1@chip/cpu; /* always discard - no resource */
+
+prop upset.memory.discard1@chip/cpu (1)->
+ ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu { !RESOURCE_EXISTS },
+ ereport.cpu.amd.bu.s_ecc1@chip/cpu { !RESOURCE_EXISTS },
+ ereport.cpu.amd.nb.mem_ce@chip/cpu { !RESOURCE_EXISTS };
+
+/* #DIMM_SCU#
+ * "Single-correctable-unit" DIMM faults are diagnosed when the total number of
+ * page faults (diagnosed from repeated single-bit or multibit-chipkills)
+ * from any one rank on that DIMM reaches a threshold. A "correctable unit"
+ * is a single bit in normal 64/8 ECC mode, or a single symbol in ChipKill
+ * 128/16 mode (i.e., nibble-aligned nibble for the code used on Opteron).
+ *
+ * We do not stop diagnosing further single-bit page faults once we have
+ * declared a single-bit DIMM fault - we continue diagnosing them and
+ * response agents can continue to retire those pages up to the system-imposed
+ * retirement limit.
+ *
+ * Two distinct fault types may be diagnosed - fault.memory.dimm_sb and
+ * fault.memory.dimm_ck. Which one is diagnosed depends on whether we
+ * have reached the threshold for a majority of single-bit page faults or
+ * multibit page faults.
+ *
+ * Implementation: we maintain parallel SERD engines to the page_sb and
+ * page_ck engines, which trip in unison. On trip it generates a distinct
+ * ereport which we diagnose to a fault if the threshold has been
+ * reached, or to a throwaway upset if not.
+ *
*/
+#define DIMM_SB_FIT 2000
#define DIMM_CK_FIT 4000
-#define DIMM_CK_COUNT 2
-#define DIMM_CK_TIME 72h
-event fault.memory.dimm_ck@dimm, FITrate=DIMM_CK_FIT, FRU=dimm, ASRU=dimm,
- action=confcall("rewrite-ASRU");
+event fault.memory.dimm_sb@chip/memory-controller/dimm/rank,
+ FITrate=DIMM_SB_FIT, FRU=dimm, ASRU=dimm,
+ action=confcall("rewrite-ASRU"); /* rewrite non-leaf ASRU in mem scheme */
-event ereport.memory.dimm_ck_trip@dimm;
-engine serd.memory.dimm_ck@dimm, N=DIMM_CK_COUNT, T=DIMM_CK_TIME,
- method=persistent, trip=ereport.memory.dimm_ck_trip@dimm;
-event upset.memory.dimm_ck@dimm, engine=serd.memory.dimm_ck@dimm;
+event fault.memory.dimm_ck@chip/memory-controller/dimm/rank,
+ FITrate=DIMM_CK_FIT, FRU=dimm, ASRU=dimm,
+ action=confcall("rewrite-ASRU"); /* rewrite non-leaf ASRU in mem scheme */
-prop upset.memory.dimm_ck@dimm (0)->
- ereport.cpu.amd.nb.mem_ce@cpu { CONTAINS_DIMM && MULTI_BIT_CE };
+event ereport.memory.dimm_sb_trip@chip/memory-controller/dimm/rank
+ { within(5s) };
+engine serd.memory.dimm_sb@chip/memory-controller/dimm/rank,
+ N=PAGE_SB_COUNT, T=PAGE_SB_TIME, method=persistent,
+ trip=ereport.memory.dimm_sb_trip@chip/memory-controller/dimm/rank;
+event upset.memory.dimm_sb@chip/memory-controller/dimm/rank,
+ engine=serd.memory.dimm_sb@chip/memory-controller/dimm/rank;
-prop fault.memory.dimm_ck@dimm (1)->
- ereport.memory.dimm_ck_trip@dimm;
+event ereport.memory.dimm_ck_trip@chip/memory-controller/dimm/rank
+ { within(5s) };
+engine serd.memory.dimm_ck@chip/memory-controller/dimm/rank,
+ N=PAGE_CK_COUNT, T=PAGE_CK_TIME, method=persistent,
+ trip=ereport.memory.dimm_ck_trip@chip/memory-controller/dimm/rank;
+event upset.memory.dimm_ck@chip/memory-controller/dimm/rank,
+ engine=serd.memory.dimm_ck@chip/memory-controller/dimm/rank;
-prop fault.memory.page@dimm { SET_ADDR && SET_OFFSET } (0)->
- ereport.cpu.amd.nb.mem_ce@cpu { CONTAINS_DIMM && MULTI_BIT_CE &&
- GET_ADDR && GET_OFFSET };
+event upset.memory.discard2@chip/memory-controller/dimm/rank;
+
+prop upset.memory.dimm_sb@chip/memory-controller/dimm/rank (0)->
+ ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu { CONTAINS_RANK && SINGLE_BIT_CE },
+ ereport.cpu.amd.bu.s_ecc1@chip/cpu { CONTAINS_RANK && SINGLE_BIT_CE },
+ ereport.cpu.amd.nb.mem_ce@chip/cpu { CONTAINS_RANK && SINGLE_BIT_CE };
+
+prop upset.memory.dimm_ck@chip/memory-controller/dimm/rank (0)->
+ ereport.cpu.amd.nb.mem_ce@chip/cpu { CONTAINS_RANK && MULTI_BIT_CE };
+
+/*
+ * The following two propogations diagnose a fault.memory.dimm_sb when
+ * either the dimm_sb or dimm_ck engine trips (for a new page fault)
+ * and the total number of page faults (sb and ck) exceeds the threshold
+ * value with the majority being from sb page faults.
+ */
+prop fault.memory.dimm_sb@chip/memory-controller/dimm/rank (0)->
+ ereport.memory.dimm_sb_trip@chip/memory-controller/dimm/rank
+ { SB_PGFLTS + CK_PGFLTS > RANK_THRESH && SB_PGFLTS > RANK_THRESH / 2 };
+
+prop fault.memory.dimm_sb@chip/memory-controller/dimm/rank (0)->
+ ereport.memory.dimm_ck_trip@chip/memory-controller/dimm/rank
+ { SB_PGFLTS + CK_PGFLTS > RANK_THRESH && SB_PGFLTS > RANK_THRESH / 2 };
+
+/*
+ * The following two propogation diagnose a fault.memory.dimm_ck when
+ * either the dimm_sb or dimm_ck engine trip (for a new page fault)
+ * and the total number of page faults (sb and ck) exceeds the threshold
+ * value with the majority being from ck page faults.
+ */
+prop fault.memory.dimm_ck@chip/memory-controller/dimm/rank (0)->
+ ereport.memory.dimm_sb_trip@chip/memory-controller/dimm/rank
+ { SB_PGFLTS + CK_PGFLTS > RANK_THRESH && CK_PGFLTS > RANK_THRESH / 2 };
+
+prop fault.memory.dimm_ck@chip/memory-controller/dimm/rank (0)->
+ ereport.memory.dimm_ck_trip@chip/memory-controller/dimm/rank
+ { SB_PGFLTS + CK_PGFLTS > RANK_THRESH && CK_PGFLTS > RANK_THRESH / 2 };
+
+prop upset.memory.discard2@chip/memory-controller/dimm/rank (1)->
+ ereport.memory.dimm_sb_trip@chip/memory-controller/dimm/rank,
+ ereport.memory.dimm_ck_trip@chip/memory-controller/dimm/rank;
/* #DIMM_UE#
- * A multi-bit fault in a memory dimm can cause:
+ * #PAGE_UE#
+ * An uncorrectable multi-bit fault in a memory dimm can cause:
*
- * - ue : reported by nb for an access from a remote cpu
+ * - mem_ue : reported by nb for an access from a remote cpu
+ * - inf_sys_eccm : reported by ic or dc; the ic does not report a syndrome
+ * - s_eccm : reported by bu
*
* Note we use a SERD engine here simply as a way of ensuring that we get
- * both dimm and page faults reported
+ * both dimm and page faults reported.
+ *
+ * Since on production systems we force HT Sync Flood on uncorrectable
+ * memory errors (if not already set as such by the BIOS, as it should be)
+ * we won't actually receive these ereports since the system will be reset.
*/
#define DIMM_UE_FIT 6000
-event ereport.cpu.amd.nb.mem_ue@cpu;
-event ereport.memory.page_ue_trip@dimm;
-event ereport.memory.dimm_ue_trip@dimm;
-event fault.memory.dimm_ue@dimm, FITrate=DIMM_UE_FIT, FRU=dimm, ASRU=dimm,
- action=confcall("rewrite-ASRU");
-event upset.memory.page_ue@dimm, engine=serd.memory.page_ue@dimm;
-event upset.memory.dimm_ue@dimm, engine=serd.memory.dimm_ue@dimm;
-
-engine serd.memory.dimm_ue@dimm, N=0, T=1h,
- method=persistent, trip=ereport.memory.dimm_ue_trip@dimm;
+event ereport.cpu.amd.ic.inf_sys_eccm@chip/cpu{within(5s)};
+event ereport.cpu.amd.dc.inf_sys_eccm@chip/cpu{within(5s)};
+event ereport.cpu.amd.bu.s_eccm@chip/cpu{within(5s)};
+event ereport.cpu.amd.nb.mem_ue@chip/cpu{within(5s)};
+
+event fault.memory.dimm_ue@chip/memory-controller/dimm/rank,
+ FITrate=DIMM_UE_FIT, FRU=dimm, ASRU=dimm,
+ action=confcall("rewrite-ASRU"); /* rewrite non-leaf ASRU in mem scheme */
+
+event fault.memory.page_ue@chip/memory-controller/dimm/rank,
+ FITrate=PAGE_FIT, ASRU=dimm/rank, message=0,
+ action=confcall("rewrite-ASRU"); /* rewrite ASRU to identify page in rank */
+
+event ereport.memory.dimm_ue_trip@chip/memory-controller/dimm/rank;
+engine serd.memory.dimm_ue@chip/memory-controller/dimm/rank,
+ N=0, T=1h, method=persistent,
+ trip=ereport.memory.dimm_ue_trip@chip/memory-controller/dimm/rank;
+event upset.memory.dimm_ue@chip/memory-controller/dimm/rank,
+ engine=serd.memory.dimm_ue@chip/memory-controller/dimm/rank;
+
+event ereport.memory.page_ue_trip@chip/memory-controller/dimm/rank;
+engine serd.memory.page_ue@chip/memory-controller/dimm/rank,
+ N=0, T=1h, method=persistent,
+ trip=ereport.memory.page_ue_trip@chip/memory-controller/dimm/rank;
+event upset.memory.page_ue@chip/memory-controller/dimm/rank,
+ engine=serd.memory.page_ue@chip/memory-controller/dimm/rank;
+
+event upset.memory.discard3@chip/cpu;
+
+prop upset.memory.page_ue@chip/memory-controller/dimm/rank (0)->
+ ereport.cpu.amd.ic.inf_sys_eccm@chip/cpu { CONTAINS_RANK },
+ ereport.cpu.amd.dc.inf_sys_eccm@chip/cpu { CONTAINS_RANK },
+ ereport.cpu.amd.bu.s_eccm@chip/cpu { CONTAINS_RANK },
+ ereport.cpu.amd.nb.mem_ue@chip/cpu { CONTAINS_RANK };
+
+prop upset.memory.dimm_ue@chip/memory-controller/dimm/rank (0)->
+ ereport.cpu.amd.ic.inf_sys_eccm@chip/cpu { CONTAINS_RANK },
+ ereport.cpu.amd.dc.inf_sys_eccm@chip/cpu { CONTAINS_RANK },
+ ereport.cpu.amd.bu.s_eccm@chip/cpu { CONTAINS_RANK },
+ ereport.cpu.amd.nb.mem_ue@chip/cpu { CONTAINS_RANK };
+
+prop fault.memory.page_ue@chip/memory-controller/dimm/rank (1)->
+ ereport.memory.page_ue_trip@chip/memory-controller/dimm/rank;
+
+prop fault.memory.page_ue@chip/memory-controller/dimm/rank
+ { SET_ADDR && SET_OFFSET } (0)->
+ ereport.cpu.amd.ic.inf_sys_eccm@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET},
+ ereport.cpu.amd.dc.inf_sys_eccm@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET},
+ ereport.cpu.amd.bu.s_eccm@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET},
+ ereport.cpu.amd.nb.mem_ue@chip/cpu
+ { CONTAINS_RANK && GET_ADDR && GET_OFFSET };
+
+prop fault.memory.dimm_ue@chip/memory-controller/dimm/rank (1)->
+ ereport.memory.dimm_ue_trip@chip/memory-controller/dimm/rank;
+
+prop upset.memory.discard3@chip/cpu (1)->
+ ereport.cpu.amd.ic.inf_sys_eccm@chip/cpu { !RESOURCE_EXISTS },
+ ereport.cpu.amd.dc.inf_sys_eccm@chip/cpu { !RESOURCE_EXISTS },
+ ereport.cpu.amd.bu.s_eccm@chip/cpu { !RESOURCE_EXISTS },
+ ereport.cpu.amd.nb.mem_ce@chip/cpu { !RESOURCE_EXISTS };
+
+/* #CSTESTFAIL#
+ * If the BIOS fails a chip-select during POST, or perhaps after a
+ * sync flood from an uncorrectable error, then on revision F and G it
+ * should mark that chip-select as TestFail in the CS Base register.
+ * When the memory-controller driver discovers all the MC configuration
+ * it notes such failed chip-selects and creates topology nodes for the
+ * chip-select and associated dimms and ranks, and produces an ereport for each
+ * failed chip-select with detector set to the memory-controller node
+ * and resource indicating the failed chip-select.
+ */
+
+event ereport.cpu.amd.mc.cs_testfail@chip/memory-controller;
-engine serd.memory.page_ue@dimm, N=0, T=1h,
- method=persistent, trip=ereport.memory.page_ue_trip@dimm;
+event fault.memory.dimm_testfail@chip/memory-controller/dimm/rank,
+ FITrate=1000, ASRU=dimm, FRU=dimm,
+ action=confcall("rewrite-ASRU"); /* rewrite non-leaf ASRU in mem scheme */
-prop upset.memory.page_ue@dimm (0)->
- ereport.cpu.amd.nb.mem_ue@cpu { CONTAINS_DIMM };
+event error.memory.cs_testfail@chip/memory-controller/chip-select;
-prop upset.memory.dimm_ue@dimm (0)->
- ereport.cpu.amd.nb.mem_ue@cpu { CONTAINS_DIMM };
+#define CONTAINS_CS (payloadprop_contains("resource", \
+ asru(chip/memory-controller/chip-select)))
-prop error.memory.page_ue@dimm (1)->
- ereport.memory.page_ue_trip@dimm;
+prop error.memory.cs_testfail@chip/memory-controller/chip-select ->
+ ereport.cpu.amd.mc.cs_testfail@chip/memory-controller
+ { CONTAINS_CS };
-prop fault.memory.page@dimm { SET_ADDR && SET_OFFSET } (0)->
- ereport.cpu.amd.nb.mem_ue@cpu { CONTAINS_DIMM && GET_ADDR & GET_OFFSET };
+#define CSMATCH(s) \
+ (confprop_defined(asru(chip/memory-controller/chip-select), s) && \
+ confprop(asru(chip/memory-controller/chip-select), s) == \
+ confprop(asru(chip/memory-controller/dimm/rank), "csname"))
-prop fault.memory.dimm_ue@dimm (1)->
- ereport.memory.dimm_ue_trip@dimm;
+prop fault.memory.dimm_testfail@chip/memory-controller/dimm/rank ->
+ error.memory.cs_testfail@chip/memory-controller/chip-select
+ { CSMATCH("dimm1-csname") || CSMATCH("dimm2-csname")};
-prop upset.memory.discard@cpu (1)->
- ereport.cpu.amd.nb.mem_ce@cpu { !RESOURCE_EXISTS };
+/* #ADDRPAR#
+ * DRAM Command/Address Parity Errors.
+ *
+ * - dramaddr_par : reported by the nb; the NB status register includes
+ * a bit indicating which dram controller channel (A or B) experienced
+ * the error.
+ */
+
+event ereport.cpu.amd.nb.dramaddr_par@chip/cpu;
+
+event fault.cpu.amd.dramchannel@chip/memory-controller/dram-channel,
+ FITrate=1000, ASRU=dram-channel;
+
+#define GET_CHANNEL ($chan = (payloadprop("bank-status") >> 32 & 0x200) ? \
+ 1 : 0)
+
+prop fault.cpu.amd.dramchannel@chip/memory-controller/dram-channel[y] (0)->
+ ereport.cpu.amd.nb.dramaddr_par@chip/cpu { GET_CHANNEL && $chan == y };
-/* #L2D#
+/*
* l2 cache data errors.
*/
@@ -377,7 +633,7 @@ prop fault.cpu.amd.l2cachedata@chip/cpu (0)->
ereport.cpu.amd.dc.inf_l2_eccm@chip/cpu,
ereport.cpu.amd.bu.l2d_eccm@chip/cpu;
-/* #L2T#
+/*
* l2 cache main tag errors
*/
@@ -588,7 +844,7 @@ prop fault.cpu.amd.l2itlb@chip/cpu (1)->
prop fault.cpu.amd.l2itlb@chip/cpu (0)->
ereport.cpu.amd.ic.l2tlb_par@chip/cpu;
-/* #DCD#
+/*
* dcache data errors
*/
@@ -723,86 +979,7 @@ event fault.cpu.amd.l2dtlb@chip/cpu, FITrate=L2DTLB_FIT,
prop fault.cpu.amd.l2dtlb@chip/cpu (1)->
ereport.cpu.amd.dc.l2tlb_par@chip/cpu;
-/* #DPATH_SB#
- * Datapath errors between NB/MC and core.
- */
-
-#define CPU_DP_FIT 1000
-
-event fault.cpu.amd.datapath@chip/cpu, FITrate=CPU_DP_FIT, FRU=chip,
- ASRU=chip/cpu;
-event error.cpu.amd.datapath_sb@chip/cpu;
-event error.cpu.amd.datapath_mb@chip/cpu;
-
-prop fault.cpu.amd.datapath@chip/cpu (1)->
- error.cpu.amd.datapath_sb@chip/cpu,
- error.cpu.amd.datapath_mb@chip/cpu;
-
-/*
- * A single bit fault in the datapath between the NB and requesting core
- * can cause:
- *
- * - inf_sys_ecc1 : reported by ic on access from a local cpu
- * - inf_sys_ecc1 : reported by dc on access from a local cpu
- * - s_ecc1 : reported by bu on access from a local cpu (hw prefetch etc)
- *
- * Empirical observations show that in 64/8 ECC mode some memory CEs *can*
- * travel past the DRAM controller and on to the IC/DC/BU to be reported
- * via the above errors. This is not the case with ChipKill enabled.
- * We should not be diagnosing datapath/chip errors for these. While
- * this behaviour is clarified the serd parameters will be set to infinity
- * (and the multibit counterpats will not be seen because of sync flood).
- */
-
-#define CPU_DP_COUNT 5000
-#define CPU_DP_TIME 1m
-
-event ereport.cpu.amd.ic.inf_sys_ecc1@chip/cpu{within(5s)};
-event ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu{within(5s)};
-event ereport.cpu.amd.bu.s_ecc1@chip/cpu{within(5s)};
-event upset.cpu.dp_sb@chip/cpu, engine=serd.cpu.dp_sb@chip/cpu;
-event ereport.cpu.amd.dp_sb_trip@chip/cpu;
-
-engine serd.cpu.dp_sb@chip/cpu, N=CPU_DP_COUNT, T=CPU_DP_TIME,
- method=persistent, trip=ereport.cpu.amd.dp_sb_trip@chip/cpu;
-
-prop upset.cpu.dp_sb@chip/cpu (1)->
- ereport.cpu.amd.ic.inf_sys_ecc1@chip/cpu,
- ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu,
- ereport.cpu.amd.bu.s_ecc1@chip/cpu;
-
-prop error.cpu.amd.datapath_sb@chip/cpu (1)->
- ereport.cpu.amd.dp_sb_trip@chip/cpu;
-
-prop fault.cpu.amd.datapath@chip/cpu (0)->
- ereport.cpu.amd.ic.inf_sys_ecc1@chip/cpu,
- ereport.cpu.amd.dc.inf_sys_ecc1@chip/cpu,
- ereport.cpu.amd.bu.s_ecc1@chip/cpu;
-
-/* #DPATH_MB#
- * A multi-bit fault in the datapath between the NB and requesting core
- * can cause:
- *
- * - inf_sys_eccm : reported by ic on access from a local cpu
- * - inf_sys_eccm : reported by dc on access from a local cpu
- * - s_eccm : reported by bu on access from a local cpu (hw prefetch etc)
- */
-
-event ereport.cpu.amd.ic.inf_sys_eccm@chip/cpu;
-event ereport.cpu.amd.dc.inf_sys_eccm@chip/cpu;
-event ereport.cpu.amd.bu.s_eccm@chip/cpu;
-
-prop error.cpu.amd.datapath_mb@chip/cpu (1)->
- ereport.cpu.amd.ic.inf_sys_eccm@chip/cpu,
- ereport.cpu.amd.dc.inf_sys_eccm@chip/cpu,
- ereport.cpu.amd.bu.s_eccm@chip/cpu;
-
-prop fault.cpu.amd.datapath@chip/cpu (0)->
- ereport.cpu.amd.ic.inf_sys_eccm@chip/cpu,
- ereport.cpu.amd.dc.inf_sys_eccm@chip/cpu,
- ereport.cpu.amd.bu.s_eccm@chip/cpu;
-
-/*
+/* #MISC#
* Ereports that should not normally happen and which we will discard
* without diagnosis if they do. These fall into a few categories:
*
diff --git a/usr/src/cmd/fm/modules/Makefile.plugin b/usr/src/cmd/fm/modules/Makefile.plugin
index 4ba6bc04f5..38c83b94af 100644
--- a/usr/src/cmd/fm/modules/Makefile.plugin
+++ b/usr/src/cmd/fm/modules/Makefile.plugin
@@ -40,6 +40,15 @@ OBJS = $(YOBJS) $(SRCS:%.c=%.o)
CONF = $(MODULE:%=%.conf)
#
+# A module may set DMOD and DMOD_SRCS if it has a mdb proc module.
+# DMOD, if set, must match PROG above (for mdb autoloading) so it will
+# be built in a subdirectory.
+#
+ROOTDMOD = $(DMOD:%.so=$(ROOT)/usr/lib/mdb/proc/%.so)
+DMODPROG = $(DMOD:%=dmod/%)
+DMOD_OBJS = $(DMOD_SRCS:%.c=%.o)
+
+#
# Set ROOTPROG and ROOTCONF based on the values of MODULE, CLASS, and PLATFORMS
# We expect these macros to be defined by the Makefile that is including us.
#
@@ -56,6 +65,9 @@ ROOTCONF = $($(CLASS)_ROOTCONF)
LINTFLAGS += -mu
LINTFILES = $(SRCS:%.c=%.ln)
+DMODLINTTGT = $(DMOD:%=lint_dmod)
+DMODLINTFILES = $(DMOD_SRCS:%.c=%.ln)
+
APIMAP = ../../../fmd/common/fmd_api.map
FMRIMAP = ../../../fmd/common/fmd_fmri.map
@@ -63,35 +75,47 @@ CFLAGS += $(CTF_FLAGS) $(CCVERBOSE) $(XSTRCONST) $(CC_PICFLAGS)
CFLAGS += -G $(XREGSFLAG)
CPPFLAGS += -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT
-LDFLAGS += $(ZTEXT) $(ZCOMBRELOC) $(ZDEFS) $(ZIGNORE) -M$(APIMAP) -M$(FMRIMAP)
-LDLIBS += -lnvpair -lc
+$(PROG) := LDFLAGS += $(ZTEXT) $(ZCOMBRELOC) $(ZDEFS) $(ZIGNORE) -M$(APIMAP) -M$(FMRIMAP)
+$(PROG) := LDLIBS += -lnvpair -lc
+
+$(DMODPROG) := LDFLAGS += $(ZTEXT) $(ZCOMBRELOC)
-all: $(PROG)
+all: $(PROG) $(DMODPROG)
.NO_PARALLEL:
-.PARALLEL: $(OBJS) $(LINTFILES)
+.PARALLEL: $(OBJS) $(LINTFILES) $(DMOD_OBJS) $(DMODLINTFILES)
$(PROG): $(OBJS) $(APIMAP)
$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
$(CTFMERGE) -L VERSION -o $@ $(OBJS)
$(POST_PROCESS_SO)
+$(DMODPROG): $(DMOD_OBJS)
+ -@mkdir -p $(@D)
+ $(LINK.c) $(DMOD_OBJS) -o $@
+ $(POST_PROCESS)
+
%.o: %.c
$(COMPILE.c) $<
$(CTFCONVERT_O)
clean:
- $(RM) $(OBJS) $(LINTFILES) $(CLEANFILES)
+ $(RM) $(OBJS) $(DMOD_OBJS) $(LINTFILES) $(DMODLINTFILES) $(CLEANFILES)
clobber: clean
- $(RM) $(PROG)
+ $(RM) $(PROG) $(DMODPROG)
%.ln: %.c
$(LINT.c) -c $<
-lint: $(LINTFILES)
+lint_prog: $(LINTFILES)
$(LINT) $(LINTFLAGS) $(LINTFILES) $(LDLIBS)
+lint_dmod: $(DMODLINTFILES)
+ $(LINT) $(LINTFLAGS) $(DMODLINTFILES) $(LDLIBS)
+
+lint: lint_prog $(DMODLINTTGT)
+
install_h:
$(ROOTPROG): $$(@D) $(PROG)
@@ -100,6 +124,9 @@ $(ROOTPROG): $$(@D) $(PROG)
$(ROOTCONF): $$(@D) $(CONF)
$(RM) $@; $(INS) -s -m 0644 -f $(@D) $(CONF)
-install: $(ROOTPROG) $(ROOTCONF)
+$(ROOTDMOD): $$(@D) $(DMODPROG)
+ $(RM) $@; $(INS) -s -m 0555 -f $(@D) $(DMODPROG)
+
+install: $(ROOTPROG) $(ROOTCONF) $(ROOTDMOD)
include ../../Makefile.rootdirs
diff --git a/usr/src/cmd/fm/modules/common/cpumem-retire/cma_main.c b/usr/src/cmd/fm/modules/common/cpumem-retire/cma_main.c
index b2381e8b56..b978d22983 100644
--- a/usr/src/cmd/fm/modules/common/cpumem-retire/cma_main.c
+++ b/usr/src/cmd/fm/modules/common/cpumem-retire/cma_main.c
@@ -62,6 +62,12 @@ typedef struct cma_subscriber {
static const cma_subscriber_t cma_subrs[] = {
{ "fault.memory.page", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
cma_page_retire },
+ { "fault.memory.page_sb", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
+ cma_page_retire },
+ { "fault.memory.page_ck", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
+ cma_page_retire },
+ { "fault.memory.page_ue", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
+ cma_page_retire },
{ "fault.memory.dimm", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
NULL },
{ "fault.memory.dimm_sb", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
@@ -70,13 +76,15 @@ static const cma_subscriber_t cma_subrs[] = {
NULL },
{ "fault.memory.dimm_ue", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
NULL },
+ { "fault.memory.dimm_testfail", FM_FMRI_SCHEME_MEM,
+ FM_MEM_SCHEME_VERSION, NULL },
{ "fault.memory.bank", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
NULL },
{ "fault.memory.datapath", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
NULL },
/*
- * The following ultraSPARC-T1 faults do NOT retire a cpu thread,
+ * The following faults do NOT retire a cpu thread,
* and therefore must be intercepted before
* the default "fault.cpu.*" dispatch to cma_cpu_retire.
*/
@@ -90,6 +98,8 @@ static const cma_subscriber_t cma_subrs[] = {
FM_CPU_SCHEME_VERSION, NULL },
{ "fault.cpu.ultraSPARC-T1.mau", FM_FMRI_SCHEME_CPU,
FM_CPU_SCHEME_VERSION, NULL },
+ { "fault.cpu.amd.dramchannel", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
+ NULL },
{ "fault.cpu.*", FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
cma_cpu_retire },
{ NULL, NULL, 0, NULL }
@@ -102,6 +112,7 @@ nvl2subr(fmd_hdl_t *hdl, nvlist_t *nvl, nvlist_t **asrup)
nvlist_t *asru;
char *scheme;
uint8_t version;
+ char *fltclass = "(unknown)";
if (nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU, &asru) != 0 ||
nvlist_lookup_string(asru, FM_FMRI_SCHEME, &scheme) != 0 ||
@@ -119,6 +130,9 @@ nvl2subr(fmd_hdl_t *hdl, nvlist_t *nvl, nvlist_t **asrup)
}
}
+ (void) nvlist_lookup_string(nvl, FM_CLASS, &fltclass);
+ fmd_hdl_error(hdl, "No handling disposition for %s with asru in "
+ "scheme \"%s\"\n", fltclass, scheme);
cma_stats.nop_flts.fmds_value.ui64++;
return (NULL);
}
@@ -226,7 +240,7 @@ static const fmd_prop_t fmd_props[] = {
* of retries on page retires, after which the case will
* be closed.
*/
- { "page_retire_maxretries", FMD_TYPE_UINT32, "8" },
+ { "page_retire_maxretries", FMD_TYPE_UINT32, "5" },
#else
{ "page_retire_maxretries", FMD_TYPE_UINT32, "0" },
#endif /* i386 */
diff --git a/usr/src/cmd/fm/modules/common/cpumem-retire/cma_page.c b/usr/src/cmd/fm/modules/common/cpumem-retire/cma_page.c
index efc65aa6af..47f7f87cb2 100644
--- a/usr/src/cmd/fm/modules/common/cpumem-retire/cma_page.c
+++ b/usr/src/cmd/fm/modules/common/cpumem-retire/cma_page.c
@@ -273,7 +273,8 @@ page_retry(fmd_hdl_t *hdl, cma_page_t *page)
} else {
if (errno == EIO) {
fmd_hdl_debug(hdl, "failed to retry page 0x%llx "
- "retirement: page isn't scheduled for retirement\n",
+ "retirement: page isn't scheduled for retirement"
+ "(request made beyond page_retire limit?)\n",
page->pg_addr);
} else {
fmd_hdl_debug(hdl, "failed to retry page 0x%llx "
diff --git a/usr/src/cmd/fm/modules/common/eversholt/Makefile b/usr/src/cmd/fm/modules/common/eversholt/Makefile
index 84d3fdebbe..056c8094ee 100644
--- a/usr/src/cmd/fm/modules/common/eversholt/Makefile
+++ b/usr/src/cmd/fm/modules/common/eversholt/Makefile
@@ -2,9 +2,8 @@
# 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.
+# 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.
@@ -34,11 +33,13 @@ EVERSRCDIR=../../../eversholt/common
MODULE = eft
CLASS = common
+DMOD = $(MODULE).so
YSRCS=escparse.y
SRCS = alloc.c check.c config.c eft.c eftread.c esclex.c eval.c evnv.c \
fme.c iexpr.c io.c ipath.c itree.c lut.c literals.c out.c platform.c \
ptree.c stable.c stats.c tree.c
+DMOD_SRCS = eft_mdb.c
include ../../Makefile.plugin
diff --git a/usr/src/cmd/fm/modules/common/eversholt/eft_mdb.c b/usr/src/cmd/fm/modules/common/eversholt/eft_mdb.c
new file mode 100644
index 0000000000..67e30f87e1
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/eversholt/eft_mdb.c
@@ -0,0 +1,192 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/mdb_modapi.h>
+
+#include <lut.h>
+#include <itree.h>
+
+#define LUT_SIZE_INIT 300
+#define LUT_SIZE_INCR 100
+
+struct lut {
+ struct lut *lut_left;
+ struct lut *lut_right;
+ uintptr_t lut_lhs; /* search key */
+ uintptr_t lut_rhs; /* the datum */
+};
+
+struct lut_cp {
+ uintptr_t lutcp_addr;
+ struct lut lutcp_lut;
+};
+
+#define LCPSZ sizeof (struct lut_cp)
+
+struct lut_dump_desc {
+ struct lut_cp *ld_array;
+ int ld_arraysz;
+ int ld_nents;
+};
+
+static void
+lut_dump_array_alloc(struct lut_dump_desc *lddp)
+{
+ struct lut_cp *new;
+
+ if (lddp->ld_array == NULL) {
+ lddp->ld_arraysz = LUT_SIZE_INIT;
+ lddp->ld_array = mdb_zalloc(LUT_SIZE_INIT * LCPSZ, UM_SLEEP);
+ return;
+ }
+
+ new = mdb_zalloc((lddp->ld_arraysz + LUT_SIZE_INCR) * LCPSZ, UM_SLEEP);
+ bcopy(lddp->ld_array, new, lddp->ld_arraysz * LCPSZ);
+ mdb_free(lddp->ld_array, lddp->ld_arraysz * LCPSZ);
+ lddp->ld_array = new;
+ lddp->ld_arraysz += LUT_SIZE_INCR;
+}
+
+static void
+lut_dump_array_free(struct lut_dump_desc *lddp)
+{
+ if (lddp->ld_array != NULL) {
+ mdb_free(lddp->ld_array, lddp->ld_arraysz * LCPSZ);
+ lddp->ld_array = NULL;
+ }
+}
+
+static void
+lut_collect_addent(uintptr_t addr, struct lut *ent, struct lut_dump_desc *lddp)
+{
+ struct lut_cp *lcp;
+
+ if (lddp->ld_nents == lddp->ld_arraysz)
+ lut_dump_array_alloc(lddp);
+
+ lcp = &lddp->ld_array[lddp->ld_nents++];
+
+ lcp->lutcp_addr = addr;
+ bcopy(ent, &lcp->lutcp_lut, sizeof (struct lut));
+}
+
+static int
+eft_lut_walk(uintptr_t root, struct lut_dump_desc *lddp)
+{
+ struct lut lutent;
+
+ if (root) {
+ if (mdb_vread(&lutent, sizeof (struct lut), root) !=
+ sizeof (struct lut)) {
+ mdb_warn("failed to read struct lut at %p", root);
+ return (WALK_ERR);
+ }
+
+ if (eft_lut_walk((uintptr_t)lutent.lut_left, lddp) != WALK_NEXT)
+ return (WALK_ERR);
+
+ lut_collect_addent(root, &lutent, lddp);
+
+ if (eft_lut_walk((uintptr_t)lutent.lut_right, lddp) !=
+ WALK_NEXT)
+ return (WALK_ERR);
+ }
+ return (WALK_NEXT);
+}
+
+static int
+lut_collect(uintptr_t addr, struct lut_dump_desc *lddp)
+{
+ lut_dump_array_alloc(lddp);
+
+ if (eft_lut_walk(addr, lddp) != WALK_NEXT) {
+ lut_dump_array_free(lddp);
+ return (WALK_ERR);
+ } else {
+ return (WALK_NEXT); /* caller must free dump array */
+ }
+}
+
+static int
+lut_walk_init(mdb_walk_state_t *wsp)
+{
+ if (wsp->walk_addr == NULL) {
+ mdb_warn("lut walker requires a lut table address\n");
+ return (WALK_ERR);
+ }
+
+ wsp->walk_data = mdb_zalloc(sizeof (struct lut_dump_desc), UM_SLEEP);
+ wsp->walk_arg = 0;
+
+ if (lut_collect(wsp->walk_addr, wsp->walk_data) == WALK_NEXT) {
+ return (WALK_NEXT);
+ } else {
+ mdb_warn("failed to suck in full lut\n");
+ mdb_free(wsp->walk_data, sizeof (struct lut_dump_desc));
+ return (WALK_ERR);
+ }
+}
+
+static int
+lut_walk_step(mdb_walk_state_t *wsp)
+{
+ struct lut_dump_desc *lddp = wsp->walk_data;
+ int *ip = (int *)&wsp->walk_arg;
+ struct lut_cp *lcp = &lddp->ld_array[*ip];
+
+ if (*ip == lddp->ld_nents)
+ return (WALK_DONE);
+
+ ++*ip;
+
+ return (wsp->walk_callback(lcp->lutcp_addr, &lcp->lutcp_lut,
+ wsp->walk_cbdata));
+}
+
+static void
+lut_walk_fini(mdb_walk_state_t *wsp)
+{
+ struct lut_dump_desc *lddp = wsp->walk_data;
+
+ lut_dump_array_free(lddp);
+ mdb_free(lddp, sizeof (struct lut_dump_desc));
+}
+
+static const mdb_walker_t walkers[] = {
+ { "lut", "walk a lookup table", lut_walk_init, lut_walk_step,
+ lut_walk_fini, NULL },
+ { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+static const mdb_modinfo_t modinfo = { MDB_API_VERSION, NULL, walkers };
+
+const mdb_modinfo_t *
+_mdb_init(void)
+{
+ return (&modinfo);
+}
diff --git a/usr/src/cmd/fm/modules/common/eversholt/eval.c b/usr/src/cmd/fm/modules/common/eversholt/eval.c
index c1278df388..d24d9167ae 100644
--- a/usr/src/cmd/fm/modules/common/eversholt/eval.c
+++ b/usr/src/cmd/fm/modules/common/eversholt/eval.c
@@ -149,7 +149,7 @@ eval_func(struct node *funcnp, struct lut *ex, struct node *epnames[],
tree_free(rhs);
return (1);
- } else if (funcname == L_confprop) {
+ } else if (funcname == L_confprop || funcname == L_confprop_defined) {
struct config *cp;
struct node *lhs;
char *path;
@@ -160,8 +160,8 @@ eval_func(struct node *funcnp, struct lut *ex, struct node *epnames[],
else if (np->u.expr.left->u.func.s == L_asru)
lhs = eval_asru(np->u.expr.left->u.func.arglist);
else
- out(O_DIE, "confprop: unexpected lhs type: %s",
- ptree_nodetype2str(np->u.expr.left->t));
+ out(O_DIE, "%s: unexpected lhs type: %s",
+ funcname, ptree_nodetype2str(np->u.expr.left->t));
/* for now s will point to a quote [see addconfigprop()] */
ASSERT(np->u.expr.right->t == T_QUOTE);
@@ -171,14 +171,43 @@ eval_func(struct node *funcnp, struct lut *ex, struct node *epnames[],
path = ipath2str(NULL, ipath(lhs));
cp = config_lookup(croot, path, 0);
tree_free(lhs);
- FREE((void *)path);
- if (cp == NULL)
- return (0);
+ if (cp == NULL) {
+ out(O_ALTFP|O_VERB3, "%s: path %s not found",
+ funcname, path);
+ FREE((void *)path);
+ if (funcname == L_confprop_defined) {
+ valuep->v = 0;
+ valuep->t = UINT64;
+ return (1);
+ } else {
+ return (0);
+ }
+ }
s = config_getprop(cp, np->u.expr.right->u.quote.s);
- if (s == NULL)
- return (0);
- valuep->v = (uintptr_t)stable(s);
- valuep->t = STRING;
+ if (s == NULL) {
+ out(O_ALTFP|O_VERB3, "%s: \"%s\" not found for path %s",
+ funcname, np->u.expr.right->u.quote.s, path);
+ FREE((void *)path);
+ if (funcname == L_confprop_defined) {
+ valuep->v = 0;
+ valuep->t = UINT64;
+ return (1);
+ } else {
+ return (0);
+ }
+ }
+
+ if (funcname == L_confprop) {
+ valuep->v = (uintptr_t)stable(s);
+ valuep->t = STRING;
+ out(O_ALTFP|O_VERB3, "%s(\"%s\", \"%s\") = \"%s\"",
+ funcname, path, np->u.expr.right->u.quote.s,
+ (char *)(uintptr_t)valuep->v);
+ } else {
+ valuep->v = 1;
+ valuep->t = UINT64;
+ }
+ FREE((void *)path);
return (1);
}
@@ -218,7 +247,8 @@ eval_func(struct node *funcnp, struct lut *ex, struct node *epnames[],
if (platform_payloadprop(np, valuep)) {
/* platform_payloadprop() returned false */
- out(O_ALTFP|O_VERB2, "not found.");
+ out(O_ALTFP|O_VERB, "payloadprop \"%s\" not found.",
+ np->u.quote.s);
return (0);
} else {
switch (valuep->t) {
@@ -292,7 +322,8 @@ eval_func(struct node *funcnp, struct lut *ex, struct node *epnames[],
if (platform_payloadprop(np, NULL)) {
/* platform_payloadprop() returned false */
valuep->v = 0;
- out(O_ALTFP|O_VERB2, "not found.");
+ out(O_ALTFP|O_VERB2, "payloadprop_defined: \"%s\" "
+ "not defined.", np->u.quote.s);
} else {
valuep->v = 1;
out(O_ALTFP|O_VERB2, "found.");
@@ -312,7 +343,7 @@ eval_func(struct node *funcnp, struct lut *ex, struct node *epnames[],
"payloadprop_contains(\"%s\", ",
np->u.expr.left->u.quote.s);
ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, np->u.expr.right);
- outfl(O_ALTFP|O_VERB2|O_NONL, np->file, np->line, ") ");
+ out(O_ALTFP|O_VERB2|O_NONL, ") ");
/* evaluate the expression we're comparing against */
if (!eval_expr(np->u.expr.right, ex, epnames, globals, croot,
@@ -322,12 +353,28 @@ eval_func(struct node *funcnp, struct lut *ex, struct node *epnames[],
cmpval.t = UINT64;
cmpval.v = 0;
} else {
- if (cmpval.t == UINT64)
+ switch (cmpval.t) {
+ case UNDEFINED:
+ out(O_ALTFP|O_VERB2, "(undefined type)");
+ break;
+
+ case UINT64:
out(O_ALTFP|O_VERB2,
"(%llu) ", cmpval.v);
- else
+ break;
+
+ case STRING:
out(O_ALTFP|O_VERB2,
"(\"%s\") ", (char *)(uintptr_t)cmpval.v);
+ break;
+
+ case NODEPTR:
+ out(O_ALTFP|O_VERB2|O_NONL, "(");
+ ptree_name_iter(O_ALTFP|O_VERB2|O_NONL,
+ (struct node *)(uintptr_t)(cmpval.v));
+ out(O_ALTFP|O_VERB2, ") ");
+ break;
+ }
}
/* get the payload values and check for a match */
diff --git a/usr/src/cmd/fm/modules/common/eversholt/fme.c b/usr/src/cmd/fm/modules/common/eversholt/fme.c
index bbae024428..95bd97e17d 100644
--- a/usr/src/cmd/fm/modules/common/eversholt/fme.c
+++ b/usr/src/cmd/fm/modules/common/eversholt/fme.c
@@ -1896,6 +1896,10 @@ istat_bump(struct node *snp, int n)
} else
stats_counter_bump(statp);
Istat_need_save = 1;
+
+ ipath_print(O_ALTFP|O_VERB2, ent.ename, ent.ipath);
+ out(O_ALTFP|O_VERB2, " %s to value %d", n ? "set" : "incremented",
+ stats_counter_value(statp));
}
/*ARGSUSED*/
diff --git a/usr/src/cmd/fm/modules/common/eversholt/platform.c b/usr/src/cmd/fm/modules/common/eversholt/platform.c
index 414103d771..1b7b2bf8b2 100644
--- a/usr/src/cmd/fm/modules/common/eversholt/platform.c
+++ b/usr/src/cmd/fm/modules/common/eversholt/platform.c
@@ -391,12 +391,22 @@ add_prop_val(topo_hdl_t *thp, struct cfgdata *rawdata, char *propn,
int addlen, err;
char *propv, *fmristr = NULL;
nvlist_t *fmri;
+ uint64_t ui64;
+ char buf[32]; /* big enough for any 64-bit int */
/*
- * At least try to collect the protocol
- * properties
+ * We can only handle properties of string type
*/
- if (nvpair_type(pv_nvp) == DATA_TYPE_NVLIST) {
+ switch (nvpair_type(pv_nvp)) {
+ case DATA_TYPE_STRING:
+ (void) nvpair_value_string(pv_nvp, &propv);
+ break;
+
+ case DATA_TYPE_NVLIST:
+ /*
+ * At least try to collect the protocol
+ * properties
+ */
(void) nvpair_value_nvlist(pv_nvp, &fmri);
if (topo_fmri_nvl2str(thp, fmri, &fmristr, &err) < 0) {
out(O_ALTFP, "cfgcollect: failed to convert fmri to "
@@ -405,10 +415,18 @@ add_prop_val(topo_hdl_t *thp, struct cfgdata *rawdata, char *propn,
} else {
propv = fmristr;
}
+ break;
- } else if (nvpair_type(pv_nvp) == DATA_TYPE_STRING)
- (void) nvpair_value_string(pv_nvp, &propv);
- else {
+ case DATA_TYPE_UINT64:
+ /*
+ * Convert uint64 to hex strings
+ */
+ (void) nvpair_value_uint64(pv_nvp, &ui64);
+ (void) snprintf(buf, sizeof (buf), "0x%llx", ui64);
+ propv = buf;
+ break;
+
+ default:
return;
}
@@ -1339,33 +1357,27 @@ platform_confcall(struct node *np, struct lut **globals, struct config *croot,
}
/*
- * Loop until we run across asru-specific payload
+ * Loop until we run across asru-specific payload. All
+ * payload members prefixed "asru-" will be added to the
+ * hc-specific nvlist and removed from the original.
*/
- for (nvp = nvlist_next_nvpair(Action_nvl, NULL); nvp != NULL;
- nvp = nvlist_next_nvpair(Action_nvl, nvp)) {
- uint64_t ui;
- char *us;
-
- if (strncmp(nvpair_name(nvp), "asru-", 5) != 0)
- continue;
+ nvp = nvlist_next_nvpair(Action_nvl, NULL);
+ while (nvp != NULL) {
+ if (strncmp(nvpair_name(nvp), "asru-", 5) == 0) {
+ if (nvlist_add_nvpair(hcs, nvp) != 0) {
+ nvlist_free(hcs);
+ outfl(O_ALTFP|O_VERB, np->file,
+ np->line, "unable to rewrite "
+ "resource - nvlist_add_nvpair for "
+ "'%s' failed", nvpair_name(nvp));
+ return (0);
+ }
- if (nvpair_type(nvp) == DATA_TYPE_UINT64) {
- err = nvpair_value_uint64(nvp, &ui);
- err |= nvlist_add_uint64(hcs, nvpair_name(nvp),
- ui);
- } else if (nvpair_type(nvp) == DATA_TYPE_STRING) {
- err = nvpair_value_string(nvp, &us);
- err |= nvlist_add_string(hcs, nvpair_name(nvp),
- us);
+ (void) nvlist_remove(Action_nvl,
+ nvpair_name(nvp), nvpair_type(nvp));
+ nvp = nvlist_next_nvpair(Action_nvl, NULL);
} else {
- continue;
- }
-
- if (err != 0) {
- nvlist_free(hcs);
- outfl(O_ALTFP|O_VERB, np->file, np->line,
- "unable to rewrite resource");
- return (0);
+ nvp = nvlist_next_nvpair(Action_nvl, nvp);
}
}
@@ -1520,7 +1532,7 @@ platform_payloadprop(struct node *np, struct evalue *valuep)
propstr = np->u.quote.s;
if (payloadnvp == NULL) {
- out(O_ALTFP, "platform_payloadprop: no nvp for %s",
+ out(O_ALTFP | O_VERB2, "platform_payloadprop: no nvp for %s",
propstr);
return (1);
}
diff --git a/usr/src/cmd/fm/schemes/mem/mem_unum.c b/usr/src/cmd/fm/schemes/mem/mem_unum.c
index b74fb2395d..cf5f9fa669 100644
--- a/usr/src/cmd/fm/schemes/mem/mem_unum.c
+++ b/usr/src/cmd/fm/schemes/mem/mem_unum.c
@@ -336,10 +336,13 @@ unum_contains_bysubstr(const char *erunum, const char *eeunum)
strncmp(erunum, "/SB", 3) != 0) ||
(strncmp(eeunum, "/N", 2) != 0 && strncmp(eeunum, "/IO", 3) != 0 &&
strncmp(eeunum, "/SB", 3) != 0)) {
- if (ISHCUNUM(erunum) && ISHCUNUM(eeunum))
+ if (ISHCUNUM(erunum) && ISHCUNUM(eeunum)) {
nojnumstrip = 1;
- else
+ erlen = strlen(erunum);
+ eelen = strlen(eeunum);
+ } else {
return (fmd_fmri_set_errno(EINVAL));
+ }
}
if (!nojnumstrip) {
diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
index c291bd84b3..2d836eec48 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
@@ -3401,7 +3401,7 @@ static const mdb_dcmd_t dcmds[] = {
{ NVPAIR_DCMD_NAME, NVPAIR_DCMD_USAGE, NVPAIR_DCMD_DESCR,
nvpair_print },
{ NVLIST_DCMD_NAME, NVLIST_DCMD_USAGE, NVLIST_DCMD_DESCR,
- nvlist_print },
+ print_nvlist },
/* from rctl.c */
{ "rctl_dict", "?", "print systemwide default rctl definitions",
diff --git a/usr/src/cmd/mdb/common/modules/genunix/nvpair.c b/usr/src/cmd/mdb/common/modules/genunix/nvpair.c
index 463f226a2c..3d18c2fd28 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/nvpair.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/nvpair.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -99,7 +98,7 @@ nvpair_walk_step(mdb_walk_state_t *wsp)
*/
/*ARGSUSED*/
int
-nvlist_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+print_nvlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int verbose = B_FALSE;
mdb_arg_t v;
diff --git a/usr/src/cmd/mdb/common/modules/genunix/nvpair.h b/usr/src/cmd/mdb/common/modules/genunix/nvpair.h
index 071f90116d..31cd6b97fc 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/nvpair.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/nvpair.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -57,7 +56,7 @@ extern int nvpair_walk_init(mdb_walk_state_t *wsp);
extern int nvpair_walk_step(mdb_walk_state_t *wsp);
extern int nvpair_print(uintptr_t addr, uint_t flags,
int argc, const mdb_arg_t *argv);
-extern int nvlist_print(uintptr_t addr, uint_t flags,
+extern int print_nvlist(uintptr_t addr, uint_t flags,
int argc, const mdb_arg_t *argv);
#ifdef __cplusplus
diff --git a/usr/src/cmd/mdb/common/modules/libnvpair/libnvpair.c b/usr/src/cmd/mdb/common/modules/libnvpair/libnvpair.c
index c2462e2d1c..52693bf32f 100644
--- a/usr/src/cmd/mdb/common/modules/libnvpair/libnvpair.c
+++ b/usr/src/cmd/mdb/common/modules/libnvpair/libnvpair.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -34,7 +33,7 @@ static const mdb_dcmd_t dcmds[] = {
{ NVPAIR_DCMD_NAME, NVPAIR_DCMD_USAGE, NVPAIR_DCMD_DESCR,
nvpair_print },
{ NVLIST_DCMD_NAME, NVLIST_DCMD_USAGE, NVLIST_DCMD_DESCR,
- nvlist_print },
+ print_nvlist },
{ NULL }
};
diff --git a/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ao.c b/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ao.c
index 78c6b7a3ac..e4a5ba03b0 100644
--- a/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ao.c
+++ b/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ao.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -31,7 +30,9 @@
#define ALLBITS (u_longlong_t)-1
static const mdb_bitmask_t ao_nbcfg_bits[] = {
+ { "SyncOnDramAdrParErrEn", ALLBITS, AMD_NB_CFG_SYNCONDRAMADRPARERREN },
{ "NbMcaToMstCpuEn", ALLBITS, AMD_NB_CFG_NBMCATOMSTCPUEN },
+ { "ReservedBit26", ALLBITS, 0x4000000 },
{ "DisPciCfgCpuErrRsp", ALLBITS, AMD_NB_CFG_DISPCICFGCPUERRRSP },
{ "IoRdDatErrEn", ALLBITS, AMD_NB_CFG_IORDDATERREN },
{ "ChipKillEccEn", ALLBITS, AMD_NB_CFG_CHIPKILLECCEN },
@@ -80,7 +81,10 @@ ao_nbcfg_describe(uintptr_t val, uint_t flags, int argc, const mdb_arg_t *argv)
AMD_NB_CFG_LDTLINKSEL_SHIFT);
}
- if (!(val & AMD_NB_CFG_WDOGTMRDIS)) {
+ if (val & AMD_NB_CFG_WDOGTMRDIS) {
+ mdb_printf("\t0x%08x %s\n", AMD_NB_CFG_WDOGTMRDIS,
+ "WdogTmrDis");
+ } else {
static const uint_t wdogcounts[] = {
4095, 2047, 1023, 511, 255, 127, 63, 31
};
@@ -124,6 +128,239 @@ ao_nbcfg_describe(uintptr_t val, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
+static const char *ao_scrub_rate[] = {
+ "Do not scrub", /* 0b00000 */
+ "40.0 nanosec", /* 0b00001 */
+ "80.0 nanosec", /* 0b00010 */
+ "160.0 nanosec", /* 0b00011 */
+ "320.0 nanosec", /* 0b00100 */
+ "640.0 nanosec", /* 0b00101 */
+ "1.28 microsec", /* 0b00110 */
+ "2.56 microsec", /* 0b00111 */
+ "5.12 microsec", /* 0b01000 */
+ "10.2 microsec", /* 0b01001 */
+ "20.5 microsec", /* 0b01010 */
+ "41.0 microsec", /* 0b01011 */
+ "81.9 microsec", /* 0b01100 */
+ "163.8 microsec", /* 0b01101 */
+ "327.7 microsec", /* 0b01110 */
+ "655.4 microsec", /* 0b01111 */
+ "1.31 millsec", /* 0b10000 */
+ "2.62 millsec", /* 0b10001 */
+ "5.24 millsec", /* 0b10010 */
+ "10.49 millsec", /* 0b10011 */
+ "20.97 millsec", /* 0b10100 */
+ "42.00 millsec", /* 0b10101 */
+ "84.00 millsec", /* 0b10110 */
+};
+
+#define SCRUBCODE(val, low) ((val) >> low & 0x1f)
+
+#define SCRUBSTR(val, low) \
+ (SCRUBCODE(val, low) < sizeof (ao_scrub_rate) / sizeof (char *) ? \
+ ao_scrub_rate[SCRUBCODE(val, low)] : "reserved value!")
+
+/*ARGSUSED*/
+static int
+ao_scrubctl_describe(uintptr_t val, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ if (argc != 0 || !(flags & DCMD_ADDRSPEC))
+ return (DCMD_USAGE);
+
+ mdb_printf("\tDcacheScrub: %s\n\t L2Scrub: %s\n\t DramScrub: %s\n",
+ SCRUBSTR(val, 16), SCRUBSTR(val, 8), SCRUBSTR(val, 0));
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+ao_sparectl_describe(uintptr_t val, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ const char *itypes[] = {
+ "No Interrupt", /* 0b00 */
+ "Reserved", /* 0b01 */
+ "SMI", /* 0b10 */
+ "Reserved", /* 0b11 */
+ };
+
+ if (argc != 0 || !(flags & DCMD_ADDRSPEC))
+ return (DCMD_USAGE);
+
+ mdb_printf(
+ "\t EccErrInt: %s\n"
+ "\tSwapDoneInt: %s\n"
+ "\t BadDramCs: %d\n"
+ "\t SwapDone: %s\n"
+ "\t SwapEn: %s\n",
+ itypes[val >> 14 & 0x3],
+ itypes[val >> 12 & 0x3],
+ val >> 4 & 0x7,
+ val & 0x2 ? "Yes" : "No",
+ val & 0x1 ? "Yes" : "No");
+
+ return (DCMD_OK);
+}
+
+static const char *ao_mcactl_dc[] = {
+ "ECCI (Single-bit ECC Data Errors)",
+ "ECCM (Multi-bit ECC Data Errors)",
+ "DECC (Data Array ECC Errors)",
+ "DMTP (Main Tag Array Parity Errors)",
+ "DSTP (Snoop Tag Array Parity Errors)",
+ "L1TP (L1 TLB Parity Errors)",
+ "L2TP (L2 TLB Parity Errors)",
+};
+
+static const char *ao_mcactl_ic[] = {
+ "ECCI (Single-bit ECC data errors)",
+ "ECCM (Multi-bit ECC data errors)",
+ "IDP (Data array parity errors)",
+ "IMTP (Main tag array parity errors)",
+ "ISTP (Snoop tag array parity errors)",
+ "L1TP (L1 TLB Parity Errors)",
+ "L2TP (L2 TLB Parity Errors)",
+ NULL, /* reserved */
+ NULL, /* reserved */
+ "RDDE (Read Data Errors)",
+};
+
+static const char *ao_mcactl_bu[] = {
+ "S_RDE_HP (System read data hardware prefetch)",
+ "S_RDE_TLB (System read data TLB reload)",
+ "S_RDE_ALL (All system read data)",
+ "S_ECC1_TLB (System data 1-bit ECC TLB reload)",
+ "S_ECC1_HP (System data 1-bit ECC hardware prefetch)",
+ "S_ECCM_TLB (System data multi-bit ECC TLB reload)",
+ "S_ECCM_HP (System data multi-bit ECC hardware prefetch)",
+ "L2T_PAR_ICDC (L2 tag array parity IC or DC fetch)",
+ "L2T_PAR_TLB (L2 tag array parity TLB reload)",
+ "L2T_PAR_SNP (L2 tag array parity snoop)",
+ "L2T_PAR_CPB (L2 tag array parity copyback)",
+ "L2T_PAR_SCR (L2 tag array parity scrub)",
+ "L2D_ECC1_TLB (L2 data array 1-bit ECC TLB reload)",
+ "L2D_ECC1_SNP (L2 data array 1-bit ECC snoop)",
+ "L2D_ECC1_CPB (L2 data array 1-bit ECC copyback)",
+ "L2D_ECCM_TLB (L2 data array multi-bit ECC TLB reload)",
+ "L2D_ECCM_SNP (L2 data array multi-bit ECC snoop)",
+ "L2D_ECCM_CPB (L2 data array multi-bit ECC copyback)",
+ "L2T_ECC1_SCR (L2 tag array 1-bit ECC Scrub)",
+ "L2T_ECCM_SCR (L2 tag array multi-bit ECC Scrub)",
+};
+
+static const char *ao_mcactl_ls[] = {
+ "S_RDE_L (Read Data Errors on Load)",
+ "S_RDE_S (Read Data Errors on Store)",
+};
+
+static const char *ao_mcactl_nb[] = {
+ "CorrEccEn (Correctable ECC Error Reporting Enable)",
+ "UnCorrEccEn (Uncorrectable ECC Error Reporting Enable)",
+ "CrcErr0En (HT Link 0 CRC Error Reporting Enable)",
+ "CrcErr1En (HT Link 1 CRC Error Reporting Enable)",
+ "CrcErr2En (HT Link 2 CRC Error Reporting Enable)",
+ "SyncPkt0En (HT Link 0 Sync Packet Error Reporting Enable)",
+ "SyncPkt1En (HT Link 1 Sync Packet Error Reporting Enable)",
+ "SyncPkt2En (HT Link 2 Sync Packet Error Reporting Enable)",
+ "MstrAbrtEn (Master Abort Error Reporting Enable)",
+ "TgtAbrtEn (Target Abort Error Reporting Enable)",
+ "GartTblWkEn (GART Table Walk Error Reporting Enable)",
+ "AtomicRMWEn (Atomic Read-Modify-Write Error Reporting Enable)",
+ "WchDogTmrEn (Watchdog Timer Error Reporting Enable)",
+ NULL, /* reserved */
+ NULL, /* reserved */
+ NULL, /* reserved */
+ NULL, /* reserved */
+ NULL, /* reserved */
+ "DramParEn (DRAM Parity Error Reporting enable)",
+};
+
+static const struct ao_mcactl {
+ const char *bank_name;
+ const char **bank_ctlbits;
+ int bank_tblsz;
+} ao_mcactls[] = {
+ { "dc", &ao_mcactl_dc[0], sizeof (ao_mcactl_dc) / sizeof (char *) },
+ { "ic", &ao_mcactl_ic[0], sizeof (ao_mcactl_ic) / sizeof (char *) },
+ { "bu", &ao_mcactl_bu[0], sizeof (ao_mcactl_bu) / sizeof (char *) },
+ { "ls", &ao_mcactl_ls[0], sizeof (ao_mcactl_ls) / sizeof (char *) },
+ { "nb", &ao_mcactl_nb[0], sizeof (ao_mcactl_nb) / sizeof (char *) }
+};
+
+#define AO_MCI_CTL 0x0
+#define AO_MCI_MASK 0x1
+
+static int
+ao_mci_ctlmask_common(uintptr_t val, uint_t flags, int argc,
+ const mdb_arg_t *argv, int which)
+{
+ uint64_t bank;
+ const char *bankname = NULL;
+ int i;
+
+ if (argc != 2 || !(flags & DCMD_ADDRSPEC))
+ return (DCMD_USAGE);
+
+ if (mdb_getopts(argc, argv,
+ 't', MDB_OPT_STR, &bankname, NULL) != 2)
+ return (DCMD_USAGE);
+
+ for (i = 0; i < AMD_MCA_BANK_COUNT; i++) {
+ if (strncmp(bankname, ao_mcactls[i].bank_name,
+ 2) == 0) {
+ bank = i;
+ break;
+ }
+ }
+
+ if (i == AMD_MCA_BANK_COUNT) {
+ mdb_warn("Valid bank names: dc, ic, bu, ls, nb\n");
+ return (DCMD_ERR);
+ }
+
+ mdb_printf("Reporting %s for %s:\n", which == AO_MCI_CTL ? "enables" :
+ "masks", ao_mcactls[bank].bank_name);
+ mdb_printf("%3s %4s %s\n", "Bit", "Set?", "Description");
+
+ for (i = 0; i < 63; i++) {
+ int set = val & 0x1ULL << i;
+ int inrange = i < ao_mcactls[bank].bank_tblsz;
+ const char *desc = ao_mcactls[bank].bank_ctlbits[i];
+
+ if (inrange) {
+ int known = desc != NULL;
+
+ mdb_printf("%2d %4s ", i, set ? "Yes" : "- ");
+ if (known)
+ mdb_printf("%s\n", desc);
+ else
+ mdb_printf("reserved%s\n",
+ set ? " - but set!" : "");
+ } else if (set) {
+ mdb_printf("%2d %4s Reserved - but set!\n",
+ i, "Yes");
+ }
+ }
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED3*/
+static int
+ao_mci_ctl(uintptr_t val, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (ao_mci_ctlmask_common(val, flags, argc, argv, AO_MCI_CTL));
+}
+
+/*ARGSUSED3*/
+static int
+ao_mci_mask(uintptr_t val, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (ao_mci_ctlmask_common(val, flags, argc, argv, AO_MCI_MASK));
+}
+
/*ARGSUSED3*/
static int
ao_mpt_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
@@ -261,6 +498,14 @@ static const mdb_dcmd_t dcmds[] = {
{ "ao_poll_trace", ":", "dump a poll trace buffer", ao_mpt_dump },
{ "ao_nbcfg", ":", "decode Northbridge config bits",
ao_nbcfg_describe },
+ { "ao_scrubctl", ":", "decode Scrub Control Register",
+ ao_scrubctl_describe },
+ { "ao_sparectl", ":", "decode Online Spare Control Register",
+ ao_sparectl_describe },
+ { "ao_mci_ctl", ": -t <dc|ic|bu|ls|nb>",
+ "decode MCi_CTL", ao_mci_ctl },
+ { "ao_mci_mask", ": -t <dc|ic|bu|ls|nb>",
+ "decode MCi_MASK", ao_mci_mask },
{ NULL }
};
diff --git a/usr/src/common/mc/mc-amd/mcamd_api.h b/usr/src/common/mc/mc-amd/mcamd_api.h
index 646f457fdc..977b9dfe94 100644
--- a/usr/src/common/mc/mc-amd/mcamd_api.h
+++ b/usr/src/common/mc/mc-amd/mcamd_api.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -62,55 +61,79 @@ typedef struct mcamd_node mcamd_node_t;
struct mcamd_hdl;
/*
- * If changing the properties below be sure to propogate to mcamd_misc.c
- * in common code, mcamd_subr.c in the mc-amd driver, and mcamd_prop.c
- * from libmcamd.
+ * Properties and raw register values for an mcamd_node_t are retrieved via
+ * mcamd_get_numprop(s) and mcamd_get_cfgreg(s) specifying a property or
+ * register code below.
*/
-#define MCAMD_PROP_NUM 0
-#define MCAMD_PROP_BASE_ADDR 1
-#define MCAMD_PROP_LIM_ADDR 2
-#define MCAMD_PROP_MASK 3
-#define MCAMD_PROP_DRAM_ILEN 4
-#define MCAMD_PROP_DRAM_ILSEL 5
-#define MCAMD_PROP_DRAM_HOLE 6
-#define MCAMD_PROP_DRAM_CONFIG 7
-#define MCAMD_PROP_ACCESS_WIDTH 8
-#define MCAMD_PROP_LODIMM 9
-#define MCAMD_PROP_UPDIMM 10
-#define MCAMD_PROP_CSBANKMAP 11
-#define MCAMD_PROP_SIZE 12
-#define MCAMD_PROP_CSBANK_INTLV 13
-#define MCAMD_PROP_CS0 14 /* CS0 to CS3 must be contiguous */
-#define MCAMD_PROP_CS1 15
-#define MCAMD_PROP_CS2 16
-#define MCAMD_PROP_CS3 17
-#define MCAMD_PROP_REV 18
-#define MCAMD_PROP_DISABLED_CS 19
-
-#define MCAMD_NUMPROPS 20
+typedef uint64_t mcamd_prop_t;
+typedef uint32_t mcamd_cfgreg_t;
+typedef enum mcamd_propcode {
+ /*
+ * Common properties
+ */
+ MCAMD_PROP_NUM = 0x4000,
#define MCAMD_PROPSTR_NUM "num"
+ MCAMD_PROP_SIZE,
+#define MCAMD_PROPSTR_SIZE "size"
+ MCAMD_PROP_BASE_ADDR,
#define MCAMD_PROPSTR_BASE_ADDR "base-addr"
+ /*
+ * Memory controller properties
+ */
+ MCAMD_PROP_REV = 0x5000,
+#define MCAMD_PROPSTR_REV "revision"
+ MCAMD_PROP_LIM_ADDR,
#define MCAMD_PROPSTR_LIM_ADDR "lim-addr"
-#define MCAMD_PROPSTR_MASK "mask"
-#define MCAMD_PROPSTR_DRAM_ILEN "dram-ilen"
-#define MCAMD_PROPSTR_DRAM_ILSEL "dram-ilsel"
-#define MCAMD_PROPSTR_DRAM_HOLE "dram-hole"
-#define MCAMD_PROPSTR_DRAM_CONFIG "dram-config"
+ MCAMD_PROP_ILEN,
+#define MCAMD_PROPSTR_ILEN "node-ilen"
+ MCAMD_PROP_ILSEL,
+#define MCAMD_PROPSTR_ILSEL "node-ilsel"
+ MCAMD_PROP_CSINTLVFCTR,
+#define MCAMD_PROPSTR_CSINTLVFCTR "cs-intlv-factor"
+ MCAMD_PROP_DRAMHOLE_SIZE,
+#define MCAMD_PROPSTR_DRAMHOLE_SIZE "dram-hole-size"
+ MCAMD_PROP_ACCESS_WIDTH,
#define MCAMD_PROPSTR_ACCESS_WIDTH "access-width"
-#define MCAMD_PROPSTR_LODIMM "lodimm-num"
-#define MCAMD_PROPSTR_UPDIMM "updimm-num"
-#define MCAMD_PROPSTR_CSBANKMAP "bank-mapping"
-#define MCAMD_PROPSTR_SIZE "size"
-#define MCAMD_PROPSTR_CSBANK_INTLV "csbank-intlv"
-#define MCAMD_PROPSTR_CS0 "csnum0"
-#define MCAMD_PROPSTR_CS1 "csnum1"
-#define MCAMD_PROPSTR_CS2 "csnum2"
-#define MCAMD_PROPSTR_CS3 "csnum3"
-#define MCAMD_PROPSTR_REV "revision"
-#define MCAMD_PROPSTR_DISABLED_CS "disabled-cs"
-
+ MCAMD_PROP_CSBANKMAPREG,
+#define MCAMD_PROPSTR_CSBANKMAPREG "bank-mapping"
+ MCAMD_PROP_BANKSWZL,
+#define MCAMD_PROPSTR_BANKSWZL "bankswizzle"
+ MCAMD_PROP_MOD64MUX,
+#define MCAMD_PROPSTR_MOD64MUX "mismatched-dimm-support"
+ MCAMD_PROP_SPARECS,
+#define MCAMD_PROPSTR_SPARECS "spare-csnum"
+ MCAMD_PROP_BADCS,
+#define MCAMD_PROPSTR_BADCS "bad-csnum"
+ /*
+ * Chip-select properties
+ */
+ MCAMD_PROP_MASK = 0x6000,
+#define MCAMD_PROPSTR_MASK "mask"
+ MCAMD_PROP_CSBE,
+#define MCAMD_PROPSTR_CSBE "cs-bank-enable"
+ MCAMD_PROP_SPARE,
+#define MCAMD_PROPSTR_SPARE "online-spare"
+ MCAMD_PROP_TESTFAIL,
+#define MCAMD_PROPSTR_TESTFAIL "failed-test"
+ MCAMD_PROP_CSDIMM1,
+#define MCAMD_PROPSTR_CSDIMM1 "dimm1-num"
+ MCAMD_PROP_CSDIMM2,
+#define MCAMD_PROPSTR_CSDIMM2 "dimm2-num"
+ MCAMD_PROP_DIMMRANK
+#define MCAMD_PROPSTR_DIMMRANK "dimm-rank"
+} mcamd_propcode_t;
+
+typedef enum mcamd_regcode {
+ MCAMD_REG_DRAMBASE = 0x7000,
+ MCAMD_REG_DRAMLIMIT,
+ MCAMD_REG_DRAMHOLE,
+ MCAMD_REG_DRAMCFGLO,
+ MCAMD_REG_DRAMCFGHI,
+ MCAMD_REG_CSBASE,
+ MCAMD_REG_CSMASK
+} mcamd_regcode_t;
/*
* Flags for mcamd_dprintf
*/
@@ -123,7 +146,8 @@ typedef union mcamd_dimm_offset_un mcamd_dimm_offset_un_t;
* Offset definition. Encode everything in a single uint64_t, allowing some
* room for growth in numbers of rows/columns/banks in future MC revisions.
* Some consumers will handle this as an opaque uint64 to be passed around,
- * while others will want to look inside via the union defined below.
+ * while others will want to look inside via the union defined below. Since
+ * we must support a 32-bit kernel we structure this as two uint32_t.
*/
#define MCAMD_OFFSET_VERSION_0 0x0
@@ -170,13 +194,16 @@ union mcamd_dimm_offset_un {
/*
* Routines provided by the common mcamd code.
*/
-extern const char *mcamd_get_propname(uint_t);
+extern const char *mcamd_get_propname(mcamd_propcode_t);
extern int mcamd_patounum(struct mcamd_hdl *, mcamd_node_t *, uint64_t,
- uint32_t, int, struct mc_unum *);
-
-extern int mcamd_unumtopa(struct mcamd_hdl *, mcamd_node_t *, struct mc_unum *,
+ uint32_t, int, mc_unum_t *);
+extern int mcamd_unumtopa(struct mcamd_hdl *, mcamd_node_t *, mc_unum_t *,
uint64_t *);
+extern int mc_pa_to_offset(struct mcamd_hdl *, mcamd_node_t *, mcamd_node_t *,
+ uint64_t, uint64_t *);
+extern int mc_offset_to_pa(struct mcamd_hdl *, mcamd_node_t *, mcamd_node_t *,
+ uint64_t, uint64_t *);
extern int mcamd_cs_size(struct mcamd_hdl *, mcamd_node_t *, int, size_t *);
@@ -204,8 +231,12 @@ extern mcamd_node_t *mcamd_dimm_next(struct mcamd_hdl *, mcamd_node_t *,
extern mcamd_node_t *mcamd_cs_mc(struct mcamd_hdl *, mcamd_node_t *);
extern mcamd_node_t *mcamd_dimm_mc(struct mcamd_hdl *, mcamd_node_t *);
-extern int mcamd_get_numprop(struct mcamd_hdl *, mcamd_node_t *, uint_t,
- uint64_t *);
+extern int mcamd_get_numprop(struct mcamd_hdl *, mcamd_node_t *,
+ mcamd_propcode_t, mcamd_prop_t *);
+extern int mcamd_get_numprops(struct mcamd_hdl *, ...);
+extern int mcamd_get_cfgreg(struct mcamd_hdl *, mcamd_node_t *,
+ mcamd_regcode_t, mcamd_cfgreg_t *);
+extern int mcamd_get_cfgregs(struct mcamd_hdl *, ...);
extern int mcamd_errno(struct mcamd_hdl *);
extern int mcamd_set_errno(struct mcamd_hdl *, int);
diff --git a/usr/src/common/mc/mc-amd/mcamd_misc.c b/usr/src/common/mc/mc-amd/mcamd_misc.c
index 2bd4d508a3..ca82eeb320 100644
--- a/usr/src/common/mc/mc-amd/mcamd_misc.c
+++ b/usr/src/common/mc/mc-amd/mcamd_misc.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -29,37 +28,55 @@
#include <mcamd_api.h>
-static const char *const _mcamd_proplist[] = {
- MCAMD_PROPSTR_NUM,
- MCAMD_PROPSTR_BASE_ADDR,
- MCAMD_PROPSTR_LIM_ADDR,
- MCAMD_PROPSTR_MASK,
- MCAMD_PROPSTR_DRAM_ILEN,
- MCAMD_PROPSTR_DRAM_ILSEL,
- MCAMD_PROPSTR_DRAM_HOLE,
- MCAMD_PROPSTR_DRAM_CONFIG,
- MCAMD_PROPSTR_ACCESS_WIDTH,
- MCAMD_PROPSTR_LODIMM,
- MCAMD_PROPSTR_UPDIMM,
- MCAMD_PROPSTR_CSBANKMAP,
- MCAMD_PROPSTR_SIZE,
- MCAMD_PROPSTR_CSBANK_INTLV,
- MCAMD_PROPSTR_CS0,
- MCAMD_PROPSTR_CS1,
- MCAMD_PROPSTR_CS2,
- MCAMD_PROPSTR_CS3,
- MCAMD_PROPSTR_REV,
- MCAMD_PROPSTR_DISABLED_CS,
+static struct mcproptostr {
+ mcamd_propcode_t code;
+ const char *name;
+} _propstrings[] = {
+ /*
+ * Common codes
+ */
+ { MCAMD_PROP_NUM, MCAMD_PROPSTR_NUM },
+ { MCAMD_PROP_SIZE, MCAMD_PROPSTR_SIZE },
+ { MCAMD_PROP_BASE_ADDR, MCAMD_PROPSTR_BASE_ADDR },
+ /*
+ * Memory controller properties
+ */
+ { MCAMD_PROP_REV, MCAMD_PROPSTR_REV },
+ { MCAMD_PROP_LIM_ADDR, MCAMD_PROPSTR_LIM_ADDR },
+ { MCAMD_PROP_ILEN, MCAMD_PROPSTR_ILEN },
+ { MCAMD_PROP_ILSEL, MCAMD_PROPSTR_ILSEL },
+ { MCAMD_PROP_CSINTLVFCTR, MCAMD_PROPSTR_CSINTLVFCTR },
+ { MCAMD_PROP_ACCESS_WIDTH, MCAMD_PROPSTR_ACCESS_WIDTH },
+ { MCAMD_PROP_CSBANKMAPREG, MCAMD_PROPSTR_CSBANKMAPREG },
+ { MCAMD_PROP_BANKSWZL, MCAMD_PROPSTR_BANKSWZL },
+ { MCAMD_PROP_DRAMHOLE_SIZE, MCAMD_PROPSTR_DRAMHOLE_SIZE },
+ { MCAMD_PROP_MOD64MUX, MCAMD_PROPSTR_MOD64MUX },
+ { MCAMD_PROP_SPARECS, MCAMD_PROPSTR_SPARECS },
+ { MCAMD_PROP_BADCS, MCAMD_PROPSTR_BADCS },
+ /*
+ * Chip-select properties
+ */
+ { MCAMD_PROP_MASK, MCAMD_PROPSTR_MASK },
+ { MCAMD_PROP_CSBE, MCAMD_PROPSTR_CSBE },
+ { MCAMD_PROP_SPARE, MCAMD_PROPSTR_SPARE },
+ { MCAMD_PROP_TESTFAIL, MCAMD_PROPSTR_TESTFAIL },
+ { MCAMD_PROP_CSDIMM1, MCAMD_PROPSTR_CSDIMM1 },
+ { MCAMD_PROP_CSDIMM2, MCAMD_PROPSTR_CSDIMM2 },
+ { MCAMD_PROP_DIMMRANK, MCAMD_PROPSTR_DIMMRANK },
};
-static const int _mcamd_nprop = sizeof (_mcamd_proplist) /
- sizeof (_mcamd_proplist[0]);
+static const int _nprop = sizeof (_propstrings) /
+ sizeof (struct mcproptostr);
const char *
-mcamd_get_propname(uint_t code)
+mcamd_get_propname(mcamd_propcode_t code)
{
- if (code < _mcamd_nprop)
- return (_mcamd_proplist[code]);
- else
- return (NULL);
+ int i;
+
+ for (i = 0; i < _nprop; i++) {
+ if (_propstrings[i].code == code)
+ return (_propstrings[i].name);
+ }
+
+ return (NULL);
}
diff --git a/usr/src/common/mc/mc-amd/mcamd_patounum.c b/usr/src/common/mc/mc-amd/mcamd_patounum.c
index 9bad114f30..b58a751d60 100644
--- a/usr/src/common/mc/mc-amd/mcamd_patounum.c
+++ b/usr/src/common/mc/mc-amd/mcamd_patounum.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -37,33 +36,49 @@
#include <mcamd_api.h>
#include <mcamd_err.h>
-extern int mc_pa_to_offset(struct mcamd_hdl *, mcamd_node_t *, mcamd_node_t *,
- mcamd_node_t *, uint64_t, uint64_t *);
-#define LO_DIMM 0x1
-#define UP_DIMM 0x2
+#define CSDIMM1 0x1
+#define CSDIMM2 0x2
#define BITS(val, high, low) \
((val) & (((2ULL << (high)) - 1) & ~((1ULL << (low)) - 1)))
+/*
+ * iaddr_gen generates a "normalized" DRAM controller input address
+ * from a system address (physical address) if it falls within the
+ * mapped range for this memory controller. Normalisation is
+ * performed by subtracting the node base address from the system address,
+ * allowing from hoisting, and excising any bits being used in node
+ * interleaving.
+ */
static int
iaddr_gen(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
uint64_t *iaddrp)
{
uint64_t orig = pa;
- uint64_t mcnum, base, lim, dramaddr, ilen, ilsel, top, dramhole;
-
- if (!mcamd_get_numprop(hdl, mc, MCAMD_PROP_NUM, &mcnum) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_BASE_ADDR, &base) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_LIM_ADDR, &lim) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILEN, &ilen) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILSEL, &ilsel) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_HOLE, &dramhole)) {
+ uint64_t mcnum, base, lim, dramaddr, ilen, ilsel, top, holesz;
+
+ if (!mcamd_get_numprops(hdl,
+ mc, MCAMD_PROP_NUM, &mcnum,
+ mc, MCAMD_PROP_BASE_ADDR, &base,
+ mc, MCAMD_PROP_LIM_ADDR, &lim,
+ mc, MCAMD_PROP_ILEN, &ilen,
+ mc, MCAMD_PROP_ILSEL, &ilsel,
+ mc, MCAMD_PROP_DRAMHOLE_SIZE, &holesz,
+ NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_gen: failed to "
"lookup required properties");
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
+ /*
+ * A node with no mapped memory (no active chip-selects is usually
+ * mapped with base and lim both zero. We'll cover that case and
+ * any other where the range is 0.
+ */
+ if (base == lim)
+ return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
+
if (pa < base || pa > lim) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: PA 0x%llx not "
"in range [0x%llx, 0x%llx] of MC %d\n", pa, base, lim,
@@ -80,13 +95,11 @@ iaddr_gen(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
* we need to reduce any address at or above 4GB by the size of
* the hole.
*/
- if (dramhole & MC_DC_HOLE_VALID && pa >= 0x100000000) {
- uint64_t holesize = (dramhole & MC_DC_HOLE_OFFSET_MASK) <<
- MC_DC_HOLE_OFFSET_LSHIFT;
- pa -= holesize;
+ if (holesz != 0 && pa >= 0x100000000) {
+ pa -= holesz;
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: dram hole "
"valid; pa decremented from 0x%llx to 0x%llx for "
- "a dramhole size of 0x%llx\n", orig, pa, holesize);
+ "a dramhole size of 0x%llx\n", orig, pa, holesz);
}
dramaddr = BITS(pa, 39, 0) - BITS(base, 39, 24);
@@ -127,52 +140,123 @@ iaddr_gen(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
return (0);
}
+/*
+ * cs_match determines whether the given DRAM controller input address
+ * would be responded to by the given chip-select (which may or may not
+ * be interleaved with other chip-selects). Since we include nodes
+ * for spare chip-selects (if any) and those marked TestFail (if any)
+ * we must check chip-select-bank-enable.
+ */
static int
cs_match(struct mcamd_hdl *hdl, uint64_t iaddr, mcamd_node_t *cs)
{
- uint64_t csnum, csbase, csmask;
- int match;
-
- if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &csnum) ||
- !mcamd_get_numprop(hdl, cs, MCAMD_PROP_BASE_ADDR, &csbase) ||
- !mcamd_get_numprop(hdl, cs, MCAMD_PROP_MASK, &csmask)) {
+ uint64_t csnum, csbase, csmask, csbe;
+ int match = 0;
+
+ if (!mcamd_get_numprops(hdl,
+ cs, MCAMD_PROP_NUM, &csnum,
+ cs, MCAMD_PROP_BASE_ADDR, &csbase,
+ cs, MCAMD_PROP_MASK, &csmask,
+ cs, MCAMD_PROP_CSBE, &csbe,
+ NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_match: failed to lookup "
"required properties\n");
return (0);
}
- match = ((iaddr & ~csmask) == (csbase & ~csmask));
+ if (csbe) {
+ match = ((iaddr & ~csmask) == (csbase & ~csmask));
- mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_match: iaddr 0x%llx does "
- "%smatch CS %d (base 0x%llx, mask 0x%llx)\n", iaddr,
- match ? "" : "not ", (int)csnum, csbase, csmask);
+ mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_match: iaddr 0x%llx "
+ "does %smatch CS %d (base 0x%llx, mask 0x%llx)\n", iaddr,
+ match ? "" : "not ", (int)csnum, csbase, csmask);
+ } else {
+ mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_match: iaddr 0x%llx "
+ "does not match disabled CS %d\n", iaddr, (int)csnum);
+ }
return (match);
}
+/*
+ * Given a chip-select node determine whether it has been substituted
+ * by the online spare chip-select.
+ */
+static mcamd_node_t *
+cs_sparedto(struct mcamd_hdl *hdl, mcamd_node_t *cs, mcamd_node_t *mc)
+{
+ uint64_t csnum, badcsnum, sparecsnum, tmpcsnum;
+
+ if (!mcamd_get_numprops(hdl,
+ cs, MCAMD_PROP_NUM, &csnum,
+ mc, MCAMD_PROP_BADCS, &badcsnum,
+ mc, MCAMD_PROP_SPARECS, &sparecsnum,
+ NULL)) {
+ mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_sparedto: failed to "
+ "lookup required properties\n");
+ return (NULL);
+ }
+
+ if ((badcsnum == MC_INVALNUM && sparecsnum == MC_INVALNUM) ||
+ csnum != badcsnum)
+ return (NULL);
+
+ for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
+ cs = mcamd_cs_next(hdl, mc, cs)) {
+ if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &tmpcsnum)) {
+ mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_sparedto: "
+ "fail to lookup csnum - cannot reroute to spare\n");
+ return (NULL);
+ }
+ if (tmpcsnum == sparecsnum)
+ break;
+ }
+
+ if (cs != NULL) {
+ mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_sparedto: cs#%d is "
+ "redirected to active online spare of cs#%d\n", csnum,
+ sparecsnum);
+ } else {
+ mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_sparedto: cs#%d is "
+ "redirected but cannot find spare cs# - cannout reroute to "
+ "cs#%d\n", csnum, sparecsnum);
+ }
+
+ return (cs);
+}
+
+/*
+ * Having determined which node and chip-select an address maps to,
+ * as well as whether it is a dimm1, dimm2 or dimm1/dimm2 pair
+ * involved, fill the unum structure including an optional dimm offset
+ * member.
+ */
static int
unum_fill(struct mcamd_hdl *hdl, mcamd_node_t *cs, int which,
- uint64_t iaddr, struct mc_unum *unump, int incloff)
+ uint64_t iaddr, mc_unum_t *unump, int incloff)
{
+ uint64_t chipnum, csnum, dimm1, dimm2, ranknum;
mcamd_node_t *mc, *dimm;
- uint64_t chipnum, csnum, lonum, upnum;
- int i;
int offsetdimm;
+ int i;
if ((mc = mcamd_cs_mc(hdl, cs)) == NULL ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_NUM, &chipnum) ||
- !mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &csnum)) {
+ !mcamd_get_numprops(hdl,
+ mc, MCAMD_PROP_NUM, &chipnum,
+ cs, MCAMD_PROP_NUM, &csnum,
+ cs, MCAMD_PROP_DIMMRANK, &ranknum,
+ NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
"lookup required properties\n");
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
- if ((which & LO_DIMM) &&
- !mcamd_get_numprop(hdl, cs, MCAMD_PROP_LODIMM, &lonum) ||
- (which & UP_DIMM) &&
- !mcamd_get_numprop(hdl, cs, MCAMD_PROP_UPDIMM, &upnum)) {
+ if ((which & CSDIMM1) &&
+ !mcamd_get_numprop(hdl, cs, MCAMD_PROP_CSDIMM1, &dimm1) ||
+ (which & CSDIMM2) &&
+ !mcamd_get_numprop(hdl, cs, MCAMD_PROP_CSDIMM2, &dimm2)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
- "lookup lodimm/hidimm properties\n");
+ "lookup dimm1/dimm2 properties\n");
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
@@ -180,23 +264,24 @@ unum_fill(struct mcamd_hdl *hdl, mcamd_node_t *cs, int which,
unump->unum_chip = chipnum;
unump->unum_mc = 0;
unump->unum_cs = csnum;
+ unump->unum_rank = ranknum;
for (i = 0; i < MC_UNUM_NDIMM; i++) {
- unump->unum_dimms[i] = -1;
+ unump->unum_dimms[i] = MC_INVALNUM;
}
switch (which) {
- case LO_DIMM:
- unump->unum_dimms[0] = lonum;
- offsetdimm = lonum;
+ case CSDIMM1:
+ unump->unum_dimms[0] = dimm1;
+ offsetdimm = dimm1;
break;
- case UP_DIMM:
- unump->unum_dimms[0] = upnum;
- offsetdimm = upnum;
+ case CSDIMM2:
+ unump->unum_dimms[0] = dimm2;
+ offsetdimm = dimm2;
break;
- case LO_DIMM | UP_DIMM:
- unump->unum_dimms[0] = lonum;
- unump->unum_dimms[1] = upnum;
- offsetdimm = lonum;
+ case CSDIMM1 | CSDIMM2:
+ unump->unum_dimms[0] = dimm1;
+ unump->unum_dimms[1] = dimm2;
+ offsetdimm = dimm1;
break;
}
@@ -207,7 +292,7 @@ unum_fill(struct mcamd_hdl *hdl, mcamd_node_t *cs, int which,
/*
* We wish to calculate a dimm offset. In the paired case we will
- * lookup the lodimm (see offsetdimm above).
+ * lookup dimm1 (see offsetdimm above).
*/
for (dimm = mcamd_dimm_next(hdl, mc, NULL); dimm != NULL;
dimm = mcamd_dimm_next(hdl, mc, dimm)) {
@@ -233,57 +318,79 @@ unum_fill(struct mcamd_hdl *hdl, mcamd_node_t *cs, int which,
* mc_pa_to_offset sets the offset to an invalid value if
* it hits an error.
*/
- (void) mc_pa_to_offset(hdl, mc, cs, dimm, iaddr, &unump->unum_offset);
+ (void) mc_pa_to_offset(hdl, mc, cs, iaddr, &unump->unum_offset);
return (0);
}
/*
- * We have translated a system address to a (node, chip-select). That
- * identifies one (in 64-bit MC mode) or two (in 128-bit MC mode DIMMs,
- * either a lodimm or a lodimm/updimm pair. For all cases except an
- * uncorrectable ChipKill error we can interpret the address alignment and
- * syndrome to deduce whether we are on the lodimm or updimm.
+ * We have translated a system address to a (node, chip-select), and wish
+ * to determine the associated dimm or dimms.
+ *
+ * A (node, chip-select) pair identifies one (in 64-bit MC mode) or two (in
+ * 128-bit MC mode) DIMMs. In the case of a single dimm it is usually in a
+ * lodimm (channel A) slot, but if mismatched dimm support is present it may
+ * be an updimm (channel B).
+ *
+ * Where just one dimm is associated with the chip-select we are done.
+ * Where there are two dimms associated with the chip-select we can
+ * use the ECC type and/or syndrome to determine which of the pair we
+ * resolve to, if the error is correctable. If the error is uncorrectable
+ * then in 64/8 ECC mode we can still resolve to a single dimm (since ECC
+ * is calculated and checked on each half of the data separately), but
+ * in ChipKill mode we cannot resolve down to a single dimm.
*/
static int
-mc_whichdimm(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
+mc_whichdimm(struct mcamd_hdl *hdl, mcamd_node_t *cs, uint64_t pa,
uint32_t synd, int syndtype)
{
- uint64_t accwidth;
- uint_t sym, pat;
int lobit, hibit, data, check;
+ uint64_t dimm1, dimm2;
+ uint_t sym, pat;
+ int ndimm;
- if (!mcamd_get_numprop(hdl, mc, MCAMD_PROP_ACCESS_WIDTH, &accwidth) ||
- (accwidth != 64 && accwidth != 128)) {
- mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_whichdimm: failed "
- "to lookup required properties\n");
+ /*
+ * Read the associated dimm instance numbers. The provider must
+ * assure that if there is just one dimm then it is in the first
+ * property, and if there are two then the first must be on
+ * channel A.
+ */
+ if (!mcamd_get_numprops(hdl,
+ cs, MCAMD_PROP_CSDIMM1, &dimm1,
+ cs, MCAMD_PROP_CSDIMM2, &dimm2,
+ NULL)) {
+ mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_whichdimm: failed to "
+ "lookup required properties");
+ return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
+ }
+ ndimm = (dimm1 != MC_INVALNUM) + (dimm2 != MC_INVALNUM);
+ if (ndimm == 0) {
+ mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_whichdimm: found no "
+ "dimms associated with chip-select");
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
- /*
- * In 64 bit mode only LO dimms are occupied.
- */
- if (accwidth == 64) {
- mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: 64-bit mode "
- "therefore LO_DIMM\n");
- return (LO_DIMM);
+ if (ndimm == 1) {
+ mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: just one "
+ "dimm associated with this chip-select");
+ return (CSDIMM1);
}
+ /*
+ * 64/8 ECC is checked separately for the upper and lower
+ * halves, so even an uncorrectable error is contained within
+ * one of the two halves. The error address is accurate to
+ * 8 bytes, so bit 4 distinguises upper from lower.
+ */
if (syndtype == AMD_SYNDTYPE_ECC) {
- /*
- * 64/8 ECC is checked separately for the upper and lower
- * halves, so even an uncorrectable error is contained within
- * one of the two halves. The error address is accurate to
- * 8 bytes, so bit 4 distinguises upper from lower.
- */
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: 64/8 ECC "
"and PA 0x%llx is in %s half\n", pa,
pa & 8 ? "lower" : "upper");
- return (pa & 8 ? UP_DIMM : LO_DIMM);
+ return (pa & 8 ? CSDIMM2 : CSDIMM1);
}
/*
- * ChipKill ECC (necessarily in 128-bit mode.
+ * ChipKill ECC
*/
if (mcamd_cksynd_decode(hdl, synd, &sym, &pat)) {
/*
@@ -298,12 +405,12 @@ mc_whichdimm(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: "
"ChipKill symbol %d (%s %d..%d), so LODIMM\n", sym,
data ? "data" : "check", lobit, hibit);
- return (LO_DIMM);
+ return (CSDIMM1);
} else {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: "
"ChipKill symbol %d (%s %d..%d), so UPDIMM\n", sym,
data ? "data" : "check", lobit, hibit);
- return (UP_DIMM);
+ return (CSDIMM2);
}
} else {
/*
@@ -313,65 +420,60 @@ mc_whichdimm(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichhdimm: "
"uncorrectable ChipKill, could be either LODIMM "
"or UPDIMM\n");
- return (LO_DIMM | UP_DIMM);
+ return (CSDIMM1 | CSDIMM2);
}
}
/*
- * Brute-force BKDG pa to cs translation. The following is from BKDG 3.29
- * so is for revisions prior to F. It is coded to look as much like the
+ * Brute-force BKDG pa to cs translation, coded to look as much like the
* BKDG code as possible.
*/
static int
mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
- uint32_t synd, int syndtype, struct mc_unum *unump)
+ uint32_t synd, int syndtype, mc_unum_t *unump)
{
int which;
- uint64_t mcnum;
+ uint64_t mcnum, rev;
mcamd_node_t *cs;
/*
+ * Raw registers as per BKDG
+ */
+ uint32_t HoleEn;
+ uint32_t DramBase, DramLimit;
+ uint32_t CSBase, CSMask;
+ /*
* Variables as per BKDG
*/
int Ilog;
uint32_t SystemAddr = (uint32_t)(pa >> 8);
uint64_t IntlvEn, IntlvSel;
- uint32_t DramBase, DramLimit; /* assume DramEn */
- uint32_t HoleOffset, HoleEn;
- uint32_t CSBase, CSMask; /* assuume CSBE */
+ uint32_t HoleOffset;
uint32_t InputAddr, Temp;
- /*
- * Additional variables which we need since we will reading
- * MC properties instead of PCI config space, and the MC properties
- * are stored in a cooked state.
- */
- uint64_t prop_drambase, prop_dramlimit, prop_dramhole;
- uint64_t prop_intlven, prop_intlvsel;
- uint64_t prop_csbase, prop_csmask;
-
- if (!mcamd_get_numprop(hdl, mc, MCAMD_PROP_NUM, &mcnum) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_BASE_ADDR, &prop_drambase) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_LIM_ADDR, &prop_dramlimit) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_HOLE, &prop_dramhole) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILEN, &prop_intlven) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILSEL,
- &prop_intlvsel)) {
+ if (!mcamd_get_numprops(hdl,
+ mc, MCAMD_PROP_NUM, &mcnum,
+ mc, MCAMD_PROP_REV, &rev, NULL) || !mcamd_get_cfgregs(hdl,
+ mc, MCAMD_REG_DRAMBASE, &DramBase,
+ mc, MCAMD_REG_DRAMLIMIT, &DramLimit,
+ mc, MCAMD_REG_DRAMHOLE, &HoleEn, NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: failed "
- "to lookup required properties\n");
+ "to lookup required properties and registers\n");
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
/*
- * Brute force deconstruction of the MC properties. If we decide to
- * keep this then we need some of the mcamd.g defines available to us.
+ * BKDG line to skip Why
+ *
+ * F1Offset = ... Register already read,
+ * DramBase = Get_PCI() and retrieved above.
+ * DramEn = ... Function only called for enabled nodes.
*/
- DramBase = ((prop_drambase >> 8) & 0xffff0000) | (prop_intlven << 8);
IntlvEn = (DramBase & 0x00000700) >> 8;
DramBase &= 0xffff0000;
- DramLimit = ((prop_dramlimit >> 8) & 0xffff0000) | (prop_intlvsel << 8);
+ /* DramLimit = Get_PCI() Retrieved above */
IntlvSel = (DramLimit & 0x00000700) >> 8;
DramLimit |= 0x0000ffff;
- HoleEn = prop_dramhole; /* uncooked */
+ /* HoleEn = ... Retrieved above */
HoleOffset = (HoleEn & 0x0000ff00) << 8;
HoleEn &= 0x00000001;
@@ -383,6 +485,11 @@ mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
}
+ if (HoleEn && SystemAddr > 0x00ffffff)
+ InputAddr = SystemAddr - HoleOffset;
+
+ InputAddr = SystemAddr - DramBase;
+
if (IntlvEn) {
if (IntlvSel == ((SystemAddr >> 4) & IntlvEn)) {
switch (IntlvEn) {
@@ -399,8 +506,8 @@ mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
return (mcamd_set_errno(hdl,
EMCAMD_TREEINVALID));
}
- Temp = (SystemAddr >> (4 + Ilog)) << 4;
- InputAddr = (Temp | (SystemAddr & 0x0000000f)) << 4;
+ Temp = (InputAddr >> (4 + Ilog)) << 4;
+ InputAddr = (Temp | (SystemAddr & 0x0000000f));
} else {
/* not this node */
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
@@ -408,41 +515,57 @@ mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
(int)mcnum);
return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
}
- } else {
- /* No interleave */
- InputAddr = (SystemAddr - DramBase) << 4;
}
- if (HoleEn && SystemAddr > 0x00ffffff)
- InputAddr -= HoleOffset;
+ if (!MC_REV_MATCH(rev, MC_REVS_FG))
+ InputAddr <<= 4;
for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
cs = mcamd_cs_next(hdl, mc, cs)) {
- uint64_t csnum;
-
- if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_BASE_ADDR,
- &prop_csbase) ||
- !mcamd_get_numprop(hdl, cs, MCAMD_PROP_MASK,
- &prop_csmask) ||
- !mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &csnum)) {
+ uint64_t csnum, CSEn;
+
+ if (!mcamd_get_cfgregs(hdl,
+ cs, MCAMD_REG_CSBASE, &CSBase,
+ cs, MCAMD_REG_CSMASK, &CSMask,
+ NULL) ||
+ !mcamd_get_numprops(hdl,
+ cs, MCAMD_PROP_NUM, &csnum,
+ cs, MCAMD_PROP_CSBE, &CSEn,
+ NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: "
- "failed to read cs properties\n");
+ "failed to read cs registers\n");
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
- CSBase = ((prop_csbase >> 4) & 0xffe00000) |
- ((prop_csbase >> 4) & 0x0000fe00);
- CSBase &= 0xffe0fe00;
- CSMask = ((prop_csmask >> 4) & 0x3fe00000) |
- ((prop_csmask >> 4) & 0x0000fe00);
- CSMask = (CSMask | 0x001f01ff) & 0x3fffffff;
+ /*
+ * BKDG line to skip Why
+ *
+ * F2Offset = Register already read,
+ * F2MaskOffset (rev F) Register already read
+ * CSBase = Register already read
+ * CSEn = We only keep enabled cs.
+ */
+ if (MC_REV_MATCH(rev, MC_REVS_FG)) {
+ CSBase &= 0x1ff83fe0;
+ /* CSMask = Get_PCI() Retrieved above */
+ CSMask = (CSMask | 0x0007c01f) & 0x1fffffff;
+ } else {
+ CSBase &= 0xffe0fe00;
+ /* CSMask = Get_PCI() Retrieved above */
+ CSMask = (CSMask | 0x001f01ff) & 0x3fffffff;
+ }
+
+ if (CSEn && (InputAddr & ~CSMask) == (CSBase & ~CSMask)) {
+ mcamd_node_t *sparecs;
- if (((InputAddr & ~CSMask) == (CSBase & ~CSMask))) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
"match for chip select %d of MC %d\n", (int)csnum,
(int)mcnum);
- if ((which = mc_whichdimm(hdl, mc, pa, synd,
+ if ((sparecs = cs_sparedto(hdl, cs, mc)) != NULL)
+ cs = sparecs;
+
+ if ((which = mc_whichdimm(hdl, cs, pa, synd,
syndtype)) < 0)
return (-1); /* errno is set for us */
@@ -465,24 +588,28 @@ mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
}
+/*
+ * Called for each memory controller to see if the given address is
+ * mapped to this node (as determined in iaddr_gen) and, if so, which
+ * chip-select on this node responds.
+ */
+
/*ARGSUSED*/
static int
mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
- uint32_t synd, int syndtype, struct mc_unum *unump)
+ uint32_t synd, int syndtype, mc_unum_t *unump)
{
uint64_t iaddr;
- mcamd_node_t *cs;
+ mcamd_node_t *cs, *sparecs;
int which;
#ifdef DEBUG
- struct mc_unum bkdg_unum;
+ mc_unum_t bkdg_unum;
int bkdgres;
/*
* We perform the translation twice, once using the brute-force
* approach of the BKDG and again using a more elegant but more
- * difficult to review against the BKDG approach. Note that both
- * approaches need to change for rev F since it increases max CS
- * size and so iaddr calculation etc changes.
+ * difficult to review against the BKDG approach.
*/
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "BKDG brute-force method begins\n");
bkdgres = mc_bkdg_patounum(hdl, mc, pa, synd, syndtype, &bkdg_unum);
@@ -501,7 +628,18 @@ mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
if (cs == NULL)
return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
- if ((which = mc_whichdimm(hdl, mc, pa, synd, syndtype)) < 0)
+ /*
+ * If the spare chip-select has been swapped in for the one just
+ * matched then it is really the spare that we are after. Note that
+ * when the swap is done the csbase, csmask and CSBE of the spare
+ * rank do not change - accesses to the bad rank (as nominated in
+ * the Online Spare Control Register) are redirect to the spare.
+ */
+ if ((sparecs = cs_sparedto(hdl, cs, mc)) != NULL) {
+ cs = sparecs;
+ }
+
+ if ((which = mc_whichdimm(hdl, cs, pa, synd, syndtype)) < 0)
return (-1); /* errno is set for us */
if (unum_fill(hdl, cs, which, iaddr, unump, 1) < 0)
@@ -509,15 +647,24 @@ mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
#ifdef DEBUG
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "bkdgres=%d res=0\n", bkdgres);
-#ifndef _KERNEL
/* offset is not checked - see note in BKDG algorithm */
- assert(bkdgres == 0 && unump->unum_board == bkdg_unum.unum_board &&
+ if (bkdgres != 0) {
+ mcamd_dprintf(hdl, MCAMD_DBG_ERR, "BKDG alg failed while "
+ "ours succeeded\n");
+ } else if (!(unump->unum_board == bkdg_unum.unum_board &&
unump->unum_chip == bkdg_unum.unum_chip &&
unump->unum_mc == bkdg_unum.unum_mc &&
unump->unum_cs == bkdg_unum.unum_cs &&
unump->unum_dimms[0] == bkdg_unum.unum_dimms[0] &&
- unump->unum_dimms[1] == bkdg_unum.unum_dimms[1]);
-#endif /* !_KERNEL */
+ unump->unum_dimms[1] == bkdg_unum.unum_dimms[1])) {
+ mcamd_dprintf(hdl, MCAMD_DBG_ERR,
+ "BKDG: node %d mc %d cs %d dimm(s) %d/%d\n"
+ "Ours: node 5d mc %d cs %d dimm(s) %d/%d\n",
+ bkdg_unum.unum_chip, bkdg_unum.unum_mc, bkdg_unum.unum_cs,
+ bkdg_unum.unum_dimms[0], bkdg_unum.unum_dimms[1],
+ unump->unum_chip, unump->unum_mc, unump->unum_cs,
+ unump->unum_dimms[0], unump->unum_dimms[1]);
+ }
#endif /* DEBUG */
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "Result: chip %d mc %d cs %d "
@@ -529,13 +676,18 @@ mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
int
mcamd_patounum(struct mcamd_hdl *hdl, mcamd_node_t *root, uint64_t pa,
- uint32_t synd, int syndtype, struct mc_unum *unump)
+ uint32_t synd, int syndtype, mc_unum_t *unump)
{
mcamd_node_t *mc;
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_patounum: pa=0x%llx, "
"synd=0x%x, syndtype=%d\n", pa, synd, syndtype);
+ /*
+ * Consider allowing syndrome 0 to act as a generic multibit
+ * syndrome. For example icache inf_sys_ecc1 captures an address
+ * but no syndrome - we can still resolve this to a dimm or dimms.
+ */
if (!mcamd_synd_validate(hdl, synd, syndtype))
return (mcamd_set_errno(hdl, EMCAMD_SYNDINVALID));
diff --git a/usr/src/common/mc/mc-amd/mcamd_rowcol.c b/usr/src/common/mc/mc-amd/mcamd_rowcol.c
index 3309d69421..979a182937 100644
--- a/usr/src/common/mc/mc-amd/mcamd_rowcol.c
+++ b/usr/src/common/mc/mc-amd/mcamd_rowcol.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -30,59 +29,68 @@
#include <mcamd_rowcol_impl.h>
/*
- * Convenience structures to stash MC and CS properties in. Some of these
- * are read directly, while others are then calculated.
+ * Convenience structures to stash MC and CS properties in.
*/
-struct rcp_mc {
- uint64_t num; /* corresponding chip number */
- uint64_t rev; /* revision */
- uint64_t width; /* access width */
- uint64_t base; /* MC base address */
- uint64_t lim; /* MC limit address */
- uint64_t csbnkmap; /* chip-select bank map */
- uint64_t intlven; /* Node-interleave mask */
- uint64_t intlvsel; /* Node-interleave selection for this node */
- uint64_t csintlvfctr; /* chip-select interleave factor on this node */
- int bnkswzl; /* bank-swizzle mode - derived */
+struct mcprops {
+ mcamd_prop_t num; /* corresponding chip number */
+ mcamd_prop_t rev; /* revision */
+ mcamd_prop_t width; /* access width */
+ mcamd_prop_t base; /* MC base address */
+ mcamd_prop_t lim; /* MC limit address */
+ mcamd_prop_t csbnkmap_reg; /* chip-select bank map */
+ mcamd_prop_t intlven; /* Node-intlv mask */
+ mcamd_prop_t intlvsel; /* Node-intlv selection for this node */
+ mcamd_prop_t csintlvfctr; /* cs intlv factor on this node */
+ mcamd_prop_t bnkswzl; /* bank-swizzle mode */
+ mcamd_prop_t sparecs; /* spare cs#, if any */
+ mcamd_prop_t badcs; /* substituted cs#, if any */
};
-struct rcp_cs {
- uint64_t num; /* chip-select number */
- uint64_t base; /* chip-select base address */
- uint64_t mask; /* chip-select mask */
+struct csprops {
+ mcamd_prop_t num; /* chip-select number */
+ mcamd_prop_t base; /* chip-select base address */
+ mcamd_prop_t mask; /* chip-select mask */
+ mcamd_prop_t testfail; /* marked testFail */
+ mcamd_prop_t dimmrank; /* rank number on dimm(s) */
};
static int
getmcprops(struct mcamd_hdl *hdl, mcamd_node_t *mc, const char *caller,
- struct rcp_mc *pp)
+ struct mcprops *pp)
{
- if (!mcamd_get_numprop(hdl, mc, MCAMD_PROP_NUM, &pp->num) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_REV, &pp->rev) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_BASE_ADDR, &pp->base) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_LIM_ADDR, &pp->lim) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_CSBANKMAP, &pp->csbnkmap) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILEN, &pp->intlven) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILSEL, &pp->intlvsel) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_CSBANK_INTLV,
- &pp->csintlvfctr)) {
+ if (!mcamd_get_numprops(hdl,
+ mc, MCAMD_PROP_NUM, &pp->num,
+ mc, MCAMD_PROP_REV, &pp->rev,
+ mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width,
+ mc, MCAMD_PROP_BASE_ADDR, &pp->base,
+ mc, MCAMD_PROP_LIM_ADDR, &pp->lim,
+ mc, MCAMD_PROP_CSBANKMAPREG, &pp->csbnkmap_reg,
+ mc, MCAMD_PROP_ILEN, &pp->intlven,
+ mc, MCAMD_PROP_ILSEL, &pp->intlvsel,
+ mc, MCAMD_PROP_CSINTLVFCTR, &pp->csintlvfctr,
+ mc, MCAMD_PROP_BANKSWZL, &pp->bnkswzl,
+ mc, MCAMD_PROP_SPARECS, &pp->sparecs,
+ mc, MCAMD_PROP_BADCS, &pp->badcs,
+ NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read mc "
"props for mc 0x%p\n", caller, mc);
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
- pp->bnkswzl = ((pp->csbnkmap & MC_DC_BAM_CSBANK_SWIZZLE) != 0);
-
return (0);
}
static int
getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller,
- struct rcp_cs *csp)
+ struct csprops *csp)
{
- if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &csp->num) ||
- !mcamd_get_numprop(hdl, cs, MCAMD_PROP_BASE_ADDR, &csp->base) ||
- !mcamd_get_numprop(hdl, cs, MCAMD_PROP_MASK, &csp->mask)) {
+ if (!mcamd_get_numprops(hdl,
+ cs, MCAMD_PROP_NUM, &csp->num,
+ cs, MCAMD_PROP_BASE_ADDR, &csp->base,
+ cs, MCAMD_PROP_MASK, &csp->mask,
+ cs, MCAMD_PROP_TESTFAIL, &csp->testfail,
+ cs, MCAMD_PROP_DIMMRANK, &csp->dimmrank,
+ NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs "
"props for cs 0x%p\n", caller, cs);
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
@@ -92,35 +100,43 @@ getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller,
}
static int
-gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct rcp_mc *mcpp,
- const struct bankaddr_mode **bamp, const struct csrcb_map **rcbmp,
- struct csintlv_desc *csid, const char *caller)
+gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
+ const struct rct_bnkaddrmode **bamp, const struct rct_rcbmap **rcbmp,
+ const struct rct_bnkswzlinfo **swzlp, struct rct_csintlv *csid,
+ const char *caller)
{
- if (bamp && (*bamp = rct_bankaddr_mode(mcpp->rev, csmode)) == NULL) {
+ uint_t rev = (uint_t)mcpp->rev;
+ int width = (int)mcpp->width;
+
+ if (bamp && (*bamp = rct_bnkaddrmode(rev, csmode)) == NULL) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank address mode "
- "table for MC rev %d csmode %d\n", caller,
- (int)mcpp->rev, csmode);
+ "table for MC rev %d csmode %d\n", caller, rev, csmode);
return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
}
- if (rcbmp && (*rcbmp = rct_rcbmap(mcpp->rev, mcpp->width,
- csmode)) == NULL) {
+ if (rcbmp && (*rcbmp = rct_rcbmap(rev, width, csmode)) == NULL) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no dram address map "
"table for MC rev %d csmode %d\n", caller,
- (int)mcpp->rev, csmode);
+ rev, csmode);
+ return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
+ }
+
+ if (swzlp && (*swzlp = rct_bnkswzlinfo(rev, width)) == NULL) {
+ mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank swizzling "
+ "table for MC rev %d width %d\n", caller, rev, width);
return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
}
if (csid) {
- if (mcpp->csintlvfctr != 0) {
- rct_csintlv_bits(mcpp->rev, mcpp->width, csmode,
+ if (mcpp->csintlvfctr > 1) {
+ rct_csintlv_bits(rev, width, csmode,
mcpp->csintlvfctr, csid);
if (csid->csi_factor == 0) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: "
"could not work out cs interleave "
"paramters for MC rev %d, width %d, "
"csmode %d, factor %d\n", caller,
- (int)mcpp->rev, (int)mcpp->width, csmode,
+ rev, width, csmode,
(int)mcpp->csintlvfctr);
return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
}
@@ -149,8 +165,8 @@ iaddr_add(struct mcamd_hdl *hdl, uint64_t in, uint64_t add, const char *what)
* we adopt the same convention in address reconstruction then all should work.
*/
static uint32_t
-iaddr_to_row(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
- const struct csrcb_map *rcbm, struct csintlv_desc *csid, uint64_t iaddr)
+iaddr_to_row(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
+ const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint64_t iaddr)
{
uint32_t addr = 0;
int abitno, ibitno;
@@ -158,13 +174,13 @@ iaddr_to_row(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
int swapped = 0;
for (abitno = 0; abitno < nbits; abitno++) {
- ibitno = rcbm->csrcb_rowbits[abitno];
+ ibitno = rcbm->rcb_rowbit[abitno];
if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
swapped++;
}
- if (iaddr & (1 << ibitno))
- addr |= (1 << abitno);
+ if (BITVAL(iaddr, ibitno) != 0)
+ SETBIT(addr, abitno);
}
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_row: iaddr 0x%llx --> "
@@ -175,8 +191,8 @@ iaddr_to_row(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
/*ARGSUSED*/
static uint64_t
-row_to_iaddr(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
- const struct csrcb_map *rcbm, struct csintlv_desc *csid, uint32_t rowaddr)
+row_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
+ const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint32_t rowaddr)
{
uint64_t iaddr = 0;
int abitno, ibitno;
@@ -185,7 +201,7 @@ row_to_iaddr(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
for (abitno = 0; abitno < nbits; abitno++) {
if (BIT(rowaddr, abitno) == 0)
continue;
- ibitno = rcbm->csrcb_rowbits[abitno];
+ ibitno = rcbm->rcb_rowbit[abitno];
if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
}
@@ -197,8 +213,8 @@ row_to_iaddr(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
static uint32_t
-iaddr_to_col(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
- const struct csrcb_map *rcbm, uint64_t iaddr)
+iaddr_to_col(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
+ const struct rct_rcbmap *rcbm, uint64_t iaddr)
{
uint32_t addr = 0;
int abitno, ibitno, bias = 0;
@@ -214,9 +230,9 @@ iaddr_to_col(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
if (abitno == MC_PC_COLADDRBIT)
bias = 1;
- ibitno = rcbm->csrcb_colbits[abitno + bias];
+ ibitno = rcbm->rcb_colbit[abitno + bias];
- if (iaddr & (1 << ibitno))
+ if (BITVAL(iaddr, ibitno) != 0)
SETBIT(addr, abitno);
}
@@ -228,8 +244,8 @@ iaddr_to_col(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
/*ARGSUSED*/
static uint64_t
-col_to_iaddr(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
- const struct csrcb_map *rcbm, uint32_t coladdr)
+col_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
+ const struct rct_rcbmap *rcbm, uint32_t coladdr)
{
uint64_t iaddr = 0;
int abitno, ibitno, bias = 0;
@@ -248,7 +264,7 @@ col_to_iaddr(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
if (abitno == MC_PC_COLADDRBIT)
bias = 1;
- ibitno = rcbm->csrcb_colbits[abitno + bias];
+ ibitno = rcbm->rcb_colbit[abitno + bias];
SETBIT(iaddr, ibitno);
}
@@ -256,24 +272,38 @@ col_to_iaddr(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp,
}
/*
- * Extract bank bit arguments and xor them together. Tables for
- * non bank-swizzling should have all but the first argument zero.
+ * Extract bank bit arguments and swizzle if requested.
*/
static uint32_t
-iaddr_to_bank(struct mcamd_hdl *hdl, const struct csrcb_map *rcbm,
- int bnkswzl, uint64_t iaddr)
+iaddr_to_bank(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
+ const struct rct_bnkswzlinfo *swzlp, uint64_t iaddr)
{
uint32_t addr = 0;
int abitno, ibitno, i;
- int bnkargs = bnkswzl ? MC_RC_BANKARGS : 1;
- for (abitno = 0; abitno < MC_RC_BANKBITS; abitno++) {
- uint32_t val = 0;
- for (i = 0; i < bnkargs; i++) {
- ibitno = rcbm->csrcb_bankargs[abitno][i];
- val ^= ((iaddr >> ibitno) & 0x1);
+ for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
+ uint32_t val;
+
+ /*
+ * rcb_bankbit[abitno] tells us which iaddr bit number
+ * will form bit abitno of the bank address
+ */
+ ibitno = rcbm->rcb_bankbit[abitno];
+ val = BITVAL(iaddr, ibitno);
+
+ /*
+ * If bank swizzling is in operation then xor the bit value
+ * obtained above with other iaddr bits.
+ */
+ if (swzlp) {
+ for (i = 0; i < MC_RC_SWZLBITS; i++) {
+ ibitno = swzlp->bswz_rowbits[abitno][i];
+ val ^= BITVAL(iaddr, ibitno);
+ }
}
- addr |= (val << abitno);
+
+ if (val)
+ SETBIT(addr, abitno);
}
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_bank: iaddr 0x%llx --> "
@@ -292,41 +322,44 @@ iaddr_to_bank(struct mcamd_hdl *hdl, const struct csrcb_map *rcbm,
*/
/*ARGSUSED*/
static uint64_t
-bank_to_iaddr(struct mcamd_hdl *hdl, const struct csrcb_map *rcbm,
- int bnkswzl, uint64_t partiaddr, uint32_t bankaddr)
+bank_to_iaddr(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
+ const struct rct_bnkswzlinfo *swzlp, uint64_t partiaddr, uint32_t bankaddr)
{
uint64_t iaddr = 0;
int abitno, pibitno, i;
- for (abitno = 0; abitno < MC_RC_BANKBITS; abitno++) {
+ for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
uint32_t val = BITVAL(bankaddr, abitno);
- if (bnkswzl) {
- for (i = 1; i < MC_RC_BANKARGS; i++) {
- pibitno = rcbm->csrcb_bankargs[abitno][i];
+ if (swzlp) {
+ for (i = 0; i < MC_RC_SWZLBITS; i++) {
+ pibitno = swzlp->bswz_rowbits[abitno][i];
val ^= BITVAL(partiaddr, pibitno);
}
}
if (val)
- SETBIT(iaddr, rcbm->csrcb_bankargs[abitno][0]);
+ SETBIT(iaddr, rcbm->rcb_bankbit[abitno]);
}
return (iaddr);
}
static int
-iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct rcp_mc *mcpp,
+iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
uint64_t iaddr, uint32_t *rowp, uint32_t *colp, uint32_t *bankp)
{
- const struct bankaddr_mode *bamp;
- const struct csrcb_map *rcbm;
- struct csintlv_desc csi;
-
- if (gettbls(hdl, csmode, mcpp, &bamp, &rcbm, &csi, "iaddr_to_rcb") < 0)
+ const struct rct_bnkaddrmode *bamp;
+ const struct rct_rcbmap *rcbmp;
+ const struct rct_bnkswzlinfo *swzlp = NULL;
+ struct rct_csintlv csi;
+
+ if (gettbls(hdl, csmode, mcpp, &bamp, &rcbmp,
+ mcpp->bnkswzl ? &swzlp : NULL, &csi,
+ "iaddr_to_rcb") < 0)
return (-1); /* errno already set */
- *rowp = iaddr_to_row(hdl, bamp, rcbm, &csi, iaddr);
- *colp = iaddr_to_col(hdl, bamp, rcbm, iaddr);
- *bankp = iaddr_to_bank(hdl, rcbm, mcpp->bnkswzl, iaddr);
+ *rowp = iaddr_to_row(hdl, bamp, rcbmp, &csi, iaddr);
+ *colp = iaddr_to_col(hdl, bamp, rcbmp, iaddr);
+ *bankp = iaddr_to_bank(hdl, rcbmp, swzlp, iaddr);
return (0);
}
@@ -337,7 +370,7 @@ iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct rcp_mc *mcpp,
* interleave or to insert the node interleave selection bits.
*/
static int
-iaddr_unnormalize(struct mcamd_hdl *hdl, struct rcp_mc *mcpp, uint64_t iaddr,
+iaddr_unnormalize(struct mcamd_hdl *hdl, struct mcprops *mcpp, uint64_t iaddr,
uint64_t *rsltp)
{
uint64_t dramaddr;
@@ -373,6 +406,9 @@ iaddr_unnormalize(struct mcamd_hdl *hdl, struct rcp_mc *mcpp, uint64_t iaddr,
* then fill those bits with the current IntlvSel value for
* this node. The node base address must be zero if nodes
* are interleaved.
+ *
+ * Note that the DRAM controller InputAddr is still 36 bits
+ * 35:0 on rev F.
*/
dramaddr = (BITS(iaddr, 35, 12) << intlvbits) |
(mcpp->intlvsel << 12) | BITS(iaddr, 11, 0);
@@ -392,15 +428,13 @@ iaddr_unnormalize(struct mcamd_hdl *hdl, struct rcp_mc *mcpp, uint64_t iaddr,
int
mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
- mcamd_node_t *dimm, uint64_t iaddr, uint64_t *offsetp)
+ uint64_t iaddr, uint64_t *offsetp)
{
mcamd_dimm_offset_un_t offset_un;
uint_t csmode;
uint32_t bankaddr, rowaddr, coladdr;
- int rank;
- mcamd_node_t *tcs;
- struct rcp_mc mcp;
- struct rcp_cs csp;
+ struct mcprops mcp;
+ struct csprops csp;
*offsetp = MCAMD_RC_INVALID_OFFSET;
@@ -408,29 +442,7 @@ mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
getcsprops(hdl, cs, "mc_dimm_offset", &csp) < 0)
return (-1); /* errno already set */
- csmode = MC_CS_MODE(mcp.csbnkmap, csp.num);
-
- /*
- * Convert chip-select number 0 .. 7 to a DIMM rank 0 .. 3. The
- * rank is the index of the member of the dimm mcd_cs array which
- * matches cs.
- */
- for (rank = 0, tcs = mcamd_cs_next(hdl, (mcamd_node_t *)dimm, NULL);
- tcs != NULL;
- rank++, tcs = mcamd_cs_next(hdl, (mcamd_node_t *)dimm, tcs)) {
- struct rcp_cs tcsp;
-
- if (getcsprops(hdl, tcs, "mc_dimm_offset", &tcsp) < 0)
- return (-1); /* errno already set */
- if (tcsp.num == csp.num)
- break;
- }
- if (rank == MC_CHIP_DIMMRANKMAX) {
- mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_dimm_offset: "
- "iteration over chip-selects of dimm 0x%p failed "
- "to match on expected csnum %d\n", dimm, (int)csp.num);
- return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
- }
+ csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
if (iaddr_to_rcb(hdl, csmode, &mcp, iaddr, &rowaddr,
&coladdr, &bankaddr) < 0)
@@ -440,7 +452,7 @@ mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
offset_un.do_valid = 1;
offset_un.do_version = MCAMD_OFFSET_VERSION;
- offset_un.do_rank = rank;
+ offset_un.do_rank = csp.dimmrank;
offset_un.do_row = rowaddr;
offset_un.do_bank = bankaddr;
offset_un.do_col = coladdr;
@@ -451,7 +463,7 @@ mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
}
/*
- * Given a MC and DIMM and offset (dimm rank, row, col, internal bank) we
+ * Given an MC, DIMM and offset (dimm rank, row, col, internal bank) we
* find the corresponding chip-select for the rank and then reconstruct
* a system address. In the absence of serial number support it is possible
* that we may be asked to perform this operation on a dimm which has been
@@ -468,18 +480,15 @@ mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm,
mcamd_node_t *cs;
mcamd_dimm_offset_un_t off_un;
uint32_t rank, rowaddr, bankaddr, coladdr;
- int i;
uint64_t iaddr = 0;
- const struct bankaddr_mode *bamp;
- const struct csrcb_map *rcbm;
- struct csintlv_desc csi;
- struct rcp_mc mcp;
- struct rcp_cs csp;
+ const struct rct_bnkaddrmode *bamp;
+ const struct rct_rcbmap *rcbmp;
+ const struct rct_bnkswzlinfo *swzlp = NULL;
+ struct rct_csintlv csi;
+ struct mcprops mcp;
+ struct csprops csp;
uint64_t csmode;
- int maskhi_hi = MC_DC_CSM_MASKHI_HIBIT;
- int maskhi_lo = MC_DC_CSM_MASKHI_LOBIT;
- int masklo_hi = MC_DC_CSM_MASKLO_HIBIT;
- int masklo_lo = MC_DC_CSM_MASKLO_LOBIT;
+ int maskhi_hi, maskhi_lo, masklo_hi, masklo_lo;
off_un.do_offset = offset;
rank = off_un.do_rank;
@@ -487,65 +496,149 @@ mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm,
rowaddr = off_un.do_row;
coladdr = off_un.do_col;
+ mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: offset 0x%llx "
+ "-> rank %d bank %d row 0x%x col 0x%x\n", offset,
+ rank, bankaddr, rowaddr, coladdr);
+
if (getmcprops(hdl, mc, "mc_offset_to_pa", &mcp) < 0)
return (-1); /* errno already set */
+ maskhi_hi = MC_CSMASKHI_HIBIT(mcp.rev);
+ maskhi_lo = MC_CSMASKHI_LOBIT(mcp.rev);
+ masklo_hi = MC_CSMASKLO_HIBIT(mcp.rev);
+ masklo_lo = MC_CSMASKLO_LOBIT(mcp.rev);
+
/*
- * Find the rank'th chip-select on this dimm.
+ * Find the chip-select on this dimm using the given rank.
*/
- i = 0;
- cs = mcamd_cs_next(hdl, dimm, NULL);
- while (i != rank && cs != NULL) {
- cs = mcamd_cs_next(hdl, dimm, cs);
- i++;
+ for (cs = mcamd_cs_next(hdl, dimm, NULL); cs != NULL;
+ cs = mcamd_cs_next(hdl, dimm, cs)) {
+ if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
+ return (-1); /* errno already set */
+
+ if (csp.dimmrank == rank)
+ break;
}
- if (i != rank || cs == NULL) {
+
+ if (cs == NULL) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: Current "
- "dimm in this slot does not have an %d'th cs\n",
+ "dimm in this slot does not have a cs using rank %d\n",
rank);
return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
}
- if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
- return (-1); /* errno already set */
+ /*
+ * If the cs# has been substituted by the online spare then the
+ * given unum is not actually contributing to the system address
+ * map since all accesses to it are redirected.
+ *
+ * If the cs# failed BIOS test it is not in the address map.
+ *
+ * If the cs# is the online spare cs# then it is contributing to
+ * the system address map only if swapped in, and the csbase etc
+ * parameters to use must be those of the bad cs#.
+ */
+ if (mcp.badcs != MC_INVALNUM && csp.num == mcp.badcs) {
+ return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
+ } else if (csp.testfail) {
+ return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
+ } else if (mcp.sparecs != MC_INVALNUM && csp.num == mcp.sparecs &&
+ mcp.badcs != MC_INVALNUM) {
+ /*
+ * Iterate over all cs# of this memory controller to find
+ * the bad one - the bad cs# need not be on the same dimm
+ * as the spare.
+ */
+ for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
+ cs = mcamd_cs_next(hdl, mc, cs)) {
+ mcamd_prop_t csnum;
+
+ if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM,
+ &csnum)) {
+ mcamd_dprintf(hdl, MCAMD_DBG_ERR,
+ "mcamd_offset_to_pa: csnum lookup failed "
+ "while looking for bad cs#");
+ return (mcamd_set_errno(hdl,
+ EMCAMD_TREEINVALID));
+ }
+ if (csnum == mcp.badcs)
+ break;
+ }
+
+ if (cs == NULL) {
+ mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_offset_to_pa: "
+ "failed to find cs for bad cs#%d\n", mcp.badcs);
+ return (mcamd_set_errno(hdl,
+ EMCAMD_TREEINVALID));
+ }
+
+ /* found bad cs - reread properties from it instead of spare */
+ if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
+ return (-1); /* errno already set */
+ }
- csmode = MC_CS_MODE(mcp.csbnkmap, csp.num);
+ csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
- if (gettbls(hdl, csmode, &mcp, &bamp, &rcbm, &csi,
+ if (gettbls(hdl, csmode, &mcp, &bamp, &rcbmp,
+ mcp.bnkswzl ? &swzlp : NULL, &csi,
"mc_offset_to_pa") < 0)
return (-1); /* errno already set */
- /*CONSTANTCONDITION*/
- if (MC_DC_CSM_UNMASKED_BITS != 0) {
+ /*
+ * If there are umaskable DRAM InputAddr bits the add those bits
+ * to iaddr from the cs base address.
+ */
+ if (MC_CSMASK_UNMASKABLE(mcp.rev) != 0) {
iaddr |= iaddr_add(hdl, iaddr,
- BITS(csp.base, maskhi_hi + MC_DC_CSM_UNMASKED_BITS,
+ BITS(csp.base, maskhi_hi + MC_CSMASK_UNMASKABLE(mcp.rev),
maskhi_hi + 1), "unmaskable cs basehi bits");
}
+ /*
+ * basehi bits not meing masked pass straight through to the
+ * iaddr.
+ */
iaddr |= iaddr_add(hdl, iaddr,
BITS(csp.base, maskhi_hi, maskhi_lo) &
~BITS(csp.mask, maskhi_hi, maskhi_lo),
"cs basehi bits not being masked");
- if (mcp.csintlvfctr != 0) {
+ /*
+ * if cs interleaving is active then baselo address bit are being
+ * masked - pass the rest through.
+ */
+ if (mcp.csintlvfctr > 1) {
iaddr |= iaddr_add(hdl, iaddr,
BITS(csp.base, masklo_hi, masklo_lo) &
~BITS(csp.mask, masklo_hi, masklo_lo),
"cs baselo bits not being masked");
}
+ /*
+ * Reconstruct iaddr bits from known row address
+ */
iaddr |= iaddr_add(hdl, iaddr,
- row_to_iaddr(hdl, bamp, rcbm, &csi, rowaddr),
+ row_to_iaddr(hdl, bamp, rcbmp, &csi, rowaddr),
"add iaddr bits from row");
+ /*
+ * Reconstruct iaddr bits from known column address
+ */
iaddr |= iaddr_add(hdl, iaddr,
- col_to_iaddr(hdl, bamp, rcbm, coladdr),
+ col_to_iaddr(hdl, bamp, rcbmp, coladdr),
"add iaddr bits from col");
+ /*
+ * Reconstruct iaddr bits from known internal banksel address
+ */
iaddr |= iaddr_add(hdl, iaddr,
- bank_to_iaddr(hdl, rcbm, mcp.bnkswzl, iaddr, bankaddr),
+ bank_to_iaddr(hdl, rcbmp, swzlp, iaddr, bankaddr),
"add iaddr bits from bank");
+ /*
+ * Move iaddr up into the range for this MC and insert any
+ * node interleave selection bits.
+ */
if (iaddr_unnormalize(hdl, &mcp, iaddr, pap) < 0)
return (-1); /* errno already set */
@@ -556,15 +649,16 @@ int
mcamd_cs_size(struct mcamd_hdl *hdl, mcamd_node_t *mc, int csnum, size_t *szp)
{
uint_t csmode;
- struct rcp_mc mcp;
- const struct bankaddr_mode *bamp;
+ struct mcprops mcp;
+ const struct rct_bnkaddrmode *bamp;
if (getmcprops(hdl, mc, "mcamd_cs_size", &mcp) < 0)
return (-1); /* errno already set */
- csmode = MC_CS_MODE(mcp.csbnkmap, csnum);
+ csmode = MC_CS_MODE(mcp.csbnkmap_reg, csnum);
- if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, "mcamd_cs_size") < 0)
+ if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, NULL,
+ "mcamd_cs_size") < 0)
return (-1); /* errno already set */
*szp = MC_CS_SIZE(bamp, mcp.width);
diff --git a/usr/src/common/mc/mc-amd/mcamd_rowcol_impl.h b/usr/src/common/mc/mc-amd/mcamd_rowcol_impl.h
index d7341e9b28..72064e2c21 100644
--- a/usr/src/common/mc/mc-amd/mcamd_rowcol_impl.h
+++ b/usr/src/common/mc/mc-amd/mcamd_rowcol_impl.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -55,59 +54,31 @@ extern "C" {
#define BITVAL(var, num) ((BIT(var, num) >> (num)) & 1ULL)
-#define MC_RC_ROW_MAX 14 /* maximum number of row address bits */
-#define MC_RC_COL_MAX 12 /* maximum number of col address bits */
-#define MC_RC_BANKBITS 2 /* number of internal banksel bits */
-#define MC_RC_BANKARGS 3 /* bits used for 1 banksel bit */
-#define MC_RC_CSMODES 16 /* max number of cs bankaddr modes */
+#define MC_RC_ROW_MAX 16 /* maximum number of row address bits */
+#define MC_RC_COL_MAX 12 /* maximum number of col address bits */
+#define MC_RC_BANKBITS_MAX 3 /* number of internal banksel bits */
+#define MC_RC_CSMODES 16 /* max number of cs bankaddr modes */
+#define MC_RC_SWZLBITS 2 /* number of row bits in swizzle */
-/*
- * Row, column and bank mapping is derived after allowing for interleave
- * from the normalized dram address through the tables of BKDG 3.29
- * section 3.5.6.1. We have tables for:
- *
- * . rev CG and earlier, 64-bit MC mode
- * . rev CG and earlier, 128-bit MC mode
- * . rev D and later, 64-bit MC mode (no bank swizzling if rev E)
- * . rev D and later, 128-bit MC mode (no bank swizzling if rev E)
- * . rev E and later, 64-bit MC mode with bank swizzling
- * . rev E and later, 128-bit MC mode with bank swizzling
- *
- * Each table is indexed by CS Mode (equivalently, CS size) and tells us
- * which bits of the normalized dram address (i.e., the address modified for
- * the local MC base address and with node interleave selection bits removed)
- * to use in forming the column address, row address and internal bank
- * selection.
- *
- * Note that for rev CG and earlier there is some overloading of CS mode
- * encoding such that we do not know the number of row and column address
- * bits from the CS mode alone, e.g., for 128MB DIMM modules we may have
- * 13 row bits and 9 column, or 12 row and 10 column. In these case the
- * tables held in the structures defined below will have a duplicated bit
- * number in the row and column bits. In these ambiguous cases cm_rc_ambig
- * should be set in the table.
- */
-
-struct bankaddr_mode {
+struct rct_bnkaddrmode {
int bam_sizemb; /* DIMM size in MB */
int bam_nrows; /* number of row address bits */
int bam_ncols; /* number of column address bits */
int bam_ambig; /* numbers are maximums; keep last */
};
-struct csrcb_map {
- int csrcb_bankargs[MC_RC_BANKBITS][MC_RC_BANKARGS];
- int csrcb_rowbits[MC_RC_ROW_MAX];
- int csrcb_colbits[MC_RC_COL_MAX + 1]; /* one for MC_PC_ALL */
+struct rct_rcbmap {
+ int rcb_nbankbits; /* # of bank address bits */
+ int rcb_bankbit[MC_RC_BANKBITS_MAX]; /* bank address bits */
+ int rcb_rowbit[MC_RC_ROW_MAX];
+ int rcb_colbit[MC_RC_COL_MAX + 1]; /* one for MC_PC_ALL */
};
-struct csrcb_map_tbl {
- int mt_rev; /* revision to which this applies */
- int mt_width; /* MC mode (64 or 128) */
- struct csrcb_map mt_csmap[MC_RC_CSMODES];
+struct rct_bnkswzlinfo {
+ int bswz_rowbits[MC_RC_BANKBITS_MAX][MC_RC_SWZLBITS];
};
-struct csintlv_desc {
+struct rct_csintlv {
int csi_factor; /* cs interleave factor */
int csi_hibit; /* first non-offset bit in addr */
int csi_lobit; /* first row bit in addr */
@@ -121,9 +92,10 @@ struct csintlv_desc {
#define MC_RC_CSI_BITSWAP(csidp, n) \
(csidp->csi_hibit + n - csidp->csi_lobit)
-extern const struct bankaddr_mode *rct_bankaddr_mode(uint_t, uint_t);
-extern const struct csrcb_map *rct_rcbmap(uint_t, int, uint_t);
-extern void rct_csintlv_bits(uint_t, int, uint_t, int, struct csintlv_desc *);
+extern const struct rct_bnkaddrmode *rct_bnkaddrmode(uint_t, uint_t);
+extern const struct rct_rcbmap *rct_rcbmap(uint_t, int, uint_t);
+extern const struct rct_bnkswzlinfo *rct_bnkswzlinfo(uint_t, int);
+extern void rct_csintlv_bits(uint_t, int, uint_t, int, struct rct_csintlv *);
#ifdef __cplusplus
}
diff --git a/usr/src/common/mc/mc-amd/mcamd_rowcol_tbl.c b/usr/src/common/mc/mc-amd/mcamd_rowcol_tbl.c
index f6d8ab5e57..79e360590b 100644
--- a/usr/src/common/mc/mc-amd/mcamd_rowcol_tbl.c
+++ b/usr/src/common/mc/mc-amd/mcamd_rowcol_tbl.c
@@ -32,80 +32,89 @@
#include <mcamd_rowcol_impl.h>
/*
- * Chip-Select Bank Address Mode Encodings - BKDG 3.29 3.5.6
+ * =========== Chip-Select Bank Address Mode Encodings =======================
*/
-static const struct bankaddr_mode bankaddr_modes_pre_d[];
-static const struct bankaddr_mode bankaddr_modes_d_e[];
-static const struct bam_desc {
- int rev;
+/* Individual table declarations */
+static const struct rct_bnkaddrmode bnkaddr_tbls_pre_d[];
+static const struct rct_bnkaddrmode bnkaddr_tbls_d_e[];
+static const struct rct_bnkaddrmode bnkaddr_tbls_f[];
+
+/* Managing bank address mode tables */
+static const struct _bnkaddrmode_tbldesc {
+ uint_t revmask;
int nmodes;
- const struct bankaddr_mode *modetbl;
-} bankaddr_modes[] = {
- { MC_REV_PRE_D, 7, bankaddr_modes_pre_d },
- { MC_REV_D_E, 11, bankaddr_modes_d_e },
+ const struct rct_bnkaddrmode *modetbl;
+} bnkaddr_tbls[] = {
+ { MC_REVS_BC, 7, bnkaddr_tbls_pre_d },
+ { MC_REVS_DE, 11, bnkaddr_tbls_d_e },
+ { MC_REVS_FG, 12, bnkaddr_tbls_f },
};
/*
- * DRAM Address Mappings for bank/row/column - BKDG 3.29 3.5.6.1
+ * =========== DRAM Address Mappings for bank/row/column =====================
*/
-static const struct csrcb_map_tbl dram_addrmap_pre_d_128;
-static const struct csrcb_map_tbl dram_addrmap_pre_d_64;
-static const struct csrcb_map_tbl dram_addrmap_d_e_64;
-static const struct csrcb_map_tbl dram_addrmap_d_e_128;
-static const struct rcbmap_desc {
+
+/* Individual table declarations */
+struct _rcbmap_tbl {
+ uint_t mt_revmask; /* revision to which this applies */
+ int mt_width; /* MC mode (64 or 128) */
+ const struct rct_rcbmap mt_csmap[MC_RC_CSMODES];
+};
+
+static const struct _rcbmap_tbl dram_addrmap_pre_d_64;
+static const struct _rcbmap_tbl dram_addrmap_pre_d_128;
+static const struct _rcbmap_tbl dram_addrmap_d_e_64;
+static const struct _rcbmap_tbl dram_addrmap_d_e_128;
+static const struct _rcbmap_tbl dram_addrmap_f_64;
+static const struct _rcbmap_tbl dram_addrmap_f_128;
+
+/* Managing row/column/bank tables */
+static const struct _rcbmap_tbldesc {
int nmodes;
- const struct csrcb_map_tbl *rcbmap;
-} rcbmaps[] = {
+ const struct _rcbmap_tbl *rcbmap;
+} rcbmap_tbls[] = {
{ 7, &dram_addrmap_pre_d_64 },
{ 7, &dram_addrmap_pre_d_128 },
{ 11, &dram_addrmap_d_e_64 },
{ 11, &dram_addrmap_d_e_128 },
+ { 12, &dram_addrmap_f_64 },
+ { 12, &dram_addrmap_f_128 },
};
/*
- * Lookup the Chip-Select Bank Address Mode Encoding table for a given
- * chip revision and chip-select mode.
+ * =========== Bank swizzling information ====================================
*/
-const struct bankaddr_mode *
-rct_bankaddr_mode(uint_t mcrev, uint_t csmode)
-{
- int i;
- const struct bam_desc *bdp = bankaddr_modes;
-
- for (i = 0; i < sizeof (bankaddr_modes) / sizeof (struct bam_desc);
- i++, bdp++) {
- if (bdp->rev == mcrev && csmode < bdp->nmodes)
- return (&bdp->modetbl[csmode]);
- }
+/* Individual table declarations */
+struct _bnkswzl_tbl {
+ uint_t swzt_revmask; /* revision to which this applies */
+ int swzt_width; /* MC mode (64 or 128) */
+ const struct rct_bnkswzlinfo swzt_bits;
+};
- return (NULL);
-}
+static const struct _bnkswzl_tbl bnswzl_info_e_64;
+static const struct _bnkswzl_tbl bnswzl_info_e_128;
+static const struct _bnkswzl_tbl bnswzl_info_f_64;
+static const struct _bnkswzl_tbl bnswzl_info_f_128;
+
+/* Managing bank swizzle tables */
+static const struct _bnkswzl_tbl *bnkswzl_tbls[] = {
+ &bnswzl_info_e_64,
+ &bnswzl_info_e_128,
+ &bnswzl_info_f_64,
+ &bnswzl_info_f_128,
+};
/*
- * Lookup the DRAM Address Mapping table for a given chip revision, access
- * width, bank-swizzle and chip-select mode.
+ * ======================================================================
+ * | Tables reflecting those in the BKDG |
+ * ======================================================================
*/
-const struct csrcb_map *
-rct_rcbmap(uint_t mcrev, int width, uint_t csmode)
-{
- const struct csrcb_map_tbl *rcbm;
- int i;
-
- for (i = 0; i < sizeof (rcbmaps) / sizeof (struct rcbmap_desc); i++) {
- rcbm = rcbmaps[i].rcbmap;
- if (rcbm->mt_rev == mcrev && rcbm->mt_width == width &&
- csmode < rcbmaps[i].nmodes)
- return (&rcbm->mt_csmap[csmode]);
- }
-
- return (NULL);
-}
/*
- * DRAM Address Mapping in Interleaving Mode - BKDG 3.29 section 3.5.6.2.
+ * DRAM Address Mapping in Interleaving Mode
*
* Chip-select interleave is performed by addressing across the columns
* of the first row of internal bank-select 0 on a chip-select, then the
@@ -113,9 +122,8 @@ rct_rcbmap(uint_t mcrev, int width, uint_t csmode)
* moving on to the next row of this chip-select we then rotate across
* other chip-selects in the interleave. The row/column/bank mappings
* described elsewhere in this file show that a DRAM InputAddr breaks down
- * as follows (example is the first line of table 7 which is for a 32MB
- * chip-select requiring 25 bits to address all of it) for the non-interleaved
- * case:
+ * as follows, using an example for CS Mode 0000 revision CG and earlier 64-bit
+ * mode; the cs size is 32MB, requiring 25 bits to address all of it.
*
* chip-selection bits | offset within chip-select bits |
* | row bits | bank bits | column bits | - |
@@ -131,125 +139,19 @@ rct_rcbmap(uint_t mcrev, int width, uint_t csmode)
* bits 15, 14 and 13. We swap the chosen bits with the least significant
* high order chip-selection bits.
*
- * Tables 13-16 of BKDG 3.5.6.2 really just describe the above. Working
+ * The BKDG interleave tables really just describe the above. Working
* out the high-order bits to swap is easy since that is derived directly
* from the chip-select size. The low-order bits depend on the device
* parameters since we need to target the least significant row address bits -
- * but we have that information from the rcbmaps since the first row bit
+ * but we have that information from the rcbmap_tbls since the first row bit
* simply follows the last bank address bit.
- *
- * Short version: we will do tables 13 to 16 programatically rather than
- * replicating those tables.
*/
/*
- * Yet another highbit function. This really needs to go to common source.
- * Returns range 0 to 64 inclusive;
- */
-static int
-topbit(uint64_t i)
-{
- int h = 1;
-
- if (i == 0)
- return (0);
-
- if (i & 0xffffffff00000000ULL) {
- h += 32;
- i >>= 32;
- }
-
- if (i & 0xffff0000) {
- h += 16;
- i >>= 16;
- }
-
- if (i & 0xff00) {
- h += 8;
- i >>= 8;
- }
-
- if (i & 0xf0) {
- h += 4;
- i >>= 4;
- }
-
- if (i & 0xc) {
- h += 2;
- i >>= 2;
- }
-
- if (i & 0x2)
- h += 1;
-
- return (h);
-}
-
-void
-rct_csintlv_bits(uint_t mcrev, int width, uint_t csmode, int factor,
- struct csintlv_desc *csid)
-{
- int i, lstbnkbit;
- size_t csz;
- const struct bankaddr_mode *bam;
- const struct csrcb_map *rcm;
-
- /*
- * 8-way cs interleave for some large cs sizes in 128-bit mode is
- * not implemented.
- */
- if (factor == 8 && width == 128 &&
- ((mcrev == MC_REV_PRE_D && csmode == 0x6) ||
- (mcrev == MC_REV_D_E && (csmode == 0x9 || csmode == 0xa)))) {
- csid->csi_factor = 0;
- return;
- }
-
- if ((bam = rct_bankaddr_mode(mcrev, csmode)) == NULL ||
- (rcm = rct_rcbmap(mcrev, width, csmode)) == NULL) {
- csid->csi_factor = 0;
- return;
- }
-
- csz = MC_CS_SIZE(bam, width);
-
- switch (factor) {
- case 2:
- csid->csi_nbits = 1;
- break;
- case 4:
- csid->csi_nbits = 2;
- break;
- case 8:
- csid->csi_nbits = 3;
- break;
- default:
- csid->csi_factor = 0;
- return;
- }
-
- csid->csi_hibit = topbit(csz) - 1;
-
- lstbnkbit = 0;
- for (i = 0; i < MC_RC_BANKBITS; i++) {
- /* first bank arg for a bit is "real" bank bit */
- if (rcm->csrcb_bankargs[i][0] > lstbnkbit)
- lstbnkbit = rcm->csrcb_bankargs[i][0];
- }
-
- /* first row bit is immediately after last bank bit */
- csid->csi_lobit = lstbnkbit + 1;
-
- csid->csi_factor = factor;
-}
-
-
-/*
* General notes for CS Bank Address Mode Encoding tables.
*
- * These are the tables of BKDG 3.29 section 3.5.6. They are indexed
- * by chip-select mode. Where the numbers of rows and columns is
- * ambiguous (as it is for a number of rev CG and earlier cases)
+ * These are indexed by chip-select mode. Where the numbers of rows and
+ * columns is ambiguous (as it is for a number of rev CG and earlier cases)
* the bam_config should be initialized to 1 and the numbers of rows
* and columns should be the maximums.
*/
@@ -257,7 +159,7 @@ rct_csintlv_bits(uint_t mcrev, int width, uint_t csmode, int factor,
/*
* Chip Select Bank Address Mode Encoding for rev CG and earlier.
*/
-static const struct bankaddr_mode bankaddr_modes_pre_d[] = {
+static const struct rct_bnkaddrmode bnkaddr_tbls_pre_d[] = {
{ /* 000 */
32, 12, 8
},
@@ -265,16 +167,16 @@ static const struct bankaddr_mode bankaddr_modes_pre_d[] = {
64, 12, 9
},
{ /* 010 */
- 128, 13, 10, 1
+ 128, 13, 10, 1 /* AMBIG */
},
{ /* 011 */
- 256, 13, 11, 1
+ 256, 13, 11, 1 /* AMBIG */
},
{ /* 100 */
- 512, 14, 11, 1
+ 512, 14, 11, 1 /* AMBIG */
},
{ /* 101 */
- 1024, 14, 12, 1
+ 1024, 14, 12, 1 /* AMBIG */
},
{ /* 110 */
2048, 14, 12
@@ -284,7 +186,7 @@ static const struct bankaddr_mode bankaddr_modes_pre_d[] = {
/*
* Chip Select Bank Address Mode Encoding for revs D and E.
*/
-static const struct bankaddr_mode bankaddr_modes_d_e[] = {
+static const struct rct_bnkaddrmode bnkaddr_tbls_d_e[] = {
{ /* 0000 */
32, 12, 8
},
@@ -321,6 +223,49 @@ static const struct bankaddr_mode bankaddr_modes_d_e[] = {
};
/*
+ * Chip Select Bank Address Mode Encoding for rev F
+ */
+static const struct rct_bnkaddrmode bnkaddr_tbls_f[] = {
+ { /* 0000 */
+ 128, 13, 9
+ },
+ { /* 0001 */
+ 256, 13, 10
+ },
+ { /* 0010 */
+ 512, 14, 10
+ },
+ { /* 0011 */
+ 512, 13, 11
+ },
+ { /* 0100 */
+ 512, 13, 10
+ },
+ { /* 0101 */
+ 1024, 14, 10
+ },
+ { /* 0110 */
+ 1024, 14, 11
+ },
+ { /* 0111 */
+ 2048, 15, 10
+ },
+ { /* 1000 */
+ 2048, 14, 11
+ },
+ { /* 1001 */
+ 4096, 15, 11
+ },
+ { /* 1010 */
+ 4096, 16, 10
+ },
+ { /* 1011 */
+ 8192, 16, 11
+ }
+
+};
+
+/*
* General notes on Row/Column/Bank table initialisation.
*
* These are the tables 7, 8, 9, 10, 11 and 12 of BKDG 3.29 section 3.5.6.1.
@@ -360,42 +305,42 @@ static const struct bankaddr_mode bankaddr_modes_d_e[] = {
* Row/Column/Bank address mappings for rev CG in 64-bit mode, no interleave.
* See BKDG 3.29 3.5.6 Table 7.
*/
-static const struct csrcb_map_tbl dram_addrmap_pre_d_64 = {
- MC_REV_PRE_D,
+static const struct _rcbmap_tbl dram_addrmap_pre_d_64 = {
+ MC_REVS_BC,
64,
{
{ /* 000 */
- { { 11 }, { 12 } },
+ 2, { 11, 12 },
{ 19, 20, 21, 22, 23, 24, 13, 14, 15, 16, 17, 18 },
{ 3, 4, 5, 6, 7, 8, 9, 10 }
},
{ /* 001 */
- { { 13 }, { 12 } },
+ 2, { 13, 12 },
{ 19, 20, 21, 22, 23, 24, 25, 14, 15, 16, 17, 18 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11 }
},
{ /* 010 */
- { { 13 }, { 12 } },
+ 2, { 13, 12 },
{ 19, 20, 21, 22, 23, 24, 25, 14, 15, 16, 17, 18, 26 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 26 }
},
{ /* 011 */
- { { 13 }, { 14 } },
+ 2, { 13, 14 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 27 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 27 }
},
{ /* 100 */
- { { 13 }, { 14 } },
+ 2, { 13, 14 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 27, 28 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 28 }
},
{ /* 101 */
- { { 15 }, { 14 } },
+ 2, { 15, 14 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 29, 16, 17, 18, 27, 28 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13, 28 }
},
{ /* 110 */
- { { 15 }, { 14 } },
+ 2, { 15, 14 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 29, 16, 17, 18, 27, 28 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13, 30 }
},
@@ -410,42 +355,42 @@ static const struct csrcb_map_tbl dram_addrmap_pre_d_64 = {
* Row/Column/Bank address mappings for rev CG in 128-bit mode, no interleave.
* See BKDG 3.29 3.5.6 Table 8.
*/
-static const struct csrcb_map_tbl dram_addrmap_pre_d_128 = {
- MC_REV_PRE_D,
+static const struct _rcbmap_tbl dram_addrmap_pre_d_128 = {
+ MC_REVS_BC,
128,
{
{ /* 000 */
- { { 12 }, { 13 } },
+ 2, { 12, 13 },
{ 20, 21, 22, 23, 24, 25, 14, 15, 16, 17, 18, 19 },
{ 4, 5, 6, 7, 8, 9, 10, 11 }
},
{ /* 001 */
- { { 14 }, { 13 } },
+ 2, { 14, 13 },
{ 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12 }
},
{ /* 010 */
- { { 14 }, { 13 } },
+ 2, { 14, 13 },
{ 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 27 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 27 }
},
{ /* 011 */
- { { 14 }, { 15 } },
+ 2, { 14, 15 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 16, 17, 18, 19, 28 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 28 }
},
{ /* 100 */
- { { 14 }, { 15 } },
+ 2, { 14, 15 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 16, 17, 18, 19, 28, 29 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 29 }
},
{ /* 101 */
- { { 16 }, { 15 } },
+ 2, { 16, 15 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 30, 17, 18, 19, 28, 29 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14, 29 }
},
{ /* 110 */
- { { 16 }, { 15 } },
+ 2, { 16, 15 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 30, 17, 18, 19, 28, 29 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14, 31 }
},
@@ -459,62 +404,62 @@ static const struct csrcb_map_tbl dram_addrmap_pre_d_128 = {
* Row/Column/Bank address mappings for rev D/E in 64-bit mode, no interleave.
* See BKDG 3.29 3.5.6 Table 9.
*/
-static const struct csrcb_map_tbl dram_addrmap_d_e_64 = {
- MC_REV_D_E,
+static const struct _rcbmap_tbl dram_addrmap_d_e_64 = {
+ MC_REVS_DE,
64,
{
{ /* 0000 */
- { { 11, 17, 20 }, { 12, 18, 21 } },
+ 2, { 11, 12 },
{ 19, 20, 21, 22, 23, 24, 13, 14, 15, 16, 17, 18 },
{ 3, 4, 5, 6, 7, 8, 9, 10 }
},
{ /* 0001 */
- { { 12, 17, 20 }, { 13, 18, 21 } },
+ 2, { 12, 13 },
{ 19, 20, 21, 22, 23, 24, 25, 14, 15, 16, 17, 18, 26 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11 }
},
{ /* 0010 */
- { { 12, 17, 20 }, { 13, 18, 21 } },
+ 2, { 12, 13 },
{ 19, 20, 21, 22, 23, 24, 25, 14, 15, 16, 17, 18, 26 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11 }
},
{ /* 0011 */
- { { 13, 17, 20 }, { 14, 18, 21 } },
+ 2, { 13, 14 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 27, 28 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
},
{ /* 0100 */
- { { 13, 17, 20 }, { 14, 18, 21 } },
+ 2, { 13, 14 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 27, 28 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
},
{ /* 0101 */
- { { 13, 17, 20 }, { 14, 18, 21 } },
+ 2, { 13, 14 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 27, 28 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
},
{ /* 0110 */
- { { 14, 17, 20 }, { 15, 18, 21 } },
+ 2, { 14, 15 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27, 16, 17, 18, 28, 29 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13 }
},
{ /* 0111 */
- { { 14, 17, 20 }, { 15, 18, 21 } },
+ 2, { 14, 15 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27, 16, 17, 18, 28, 29 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13 }
},
{ /* 1000 */
- { { 14, 17, 20 }, { 15, 18, 21 } },
+ 2, { 14, 15 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27, 16, 17, 18, 28, 29 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13 }
},
{ /* 1001 */
- { { 15, 17, 20 }, { 16, 18, 21 } },
+ 2, { 15, 16 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 17, 18, 29, 30 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13, 14 }
},
{ /* 1010 */
- { { 15, 17, 20 }, { 16, 18, 21 } },
+ 2, { 15, 16 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 17, 18, 29, 30 },
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13, 14 }
},
@@ -528,62 +473,62 @@ static const struct csrcb_map_tbl dram_addrmap_d_e_64 = {
* Row/Column/Bank address mappings for rev D/E in 128-bit mode, no interleave.
* See BKDG 3.29 3.5.6 Table 9.
*/
-static const struct csrcb_map_tbl dram_addrmap_d_e_128 = {
- MC_REV_D_E,
+static const struct _rcbmap_tbl dram_addrmap_d_e_128 = {
+ MC_REVS_DE,
128,
{
{ /* 0000 */
- { { 12, 18, 21 }, { 13, 19, 22 } },
+ 2, { 12, 13 },
{ 20, 21, 22, 23, 24, 25, 14, 15, 16, 17, 18, 19 },
{ 4, 5, 6, 7, 8, 9, 10, 11 }
},
{ /* 0001 */
- { { 13, 18, 21 }, { 14, 19, 22 } },
+ 2, { 13, 14 },
{ 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 27 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12 }
},
{ /* 0010 */
- { { 13, 18, 21 }, { 14, 19, 22 } },
+ 2, { 13, 14 },
{ 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 27 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12 }
},
{ /* 0011 */
- { { 14, 18, 21 }, { 15, 19, 22 } },
+ 2, { 14, 15 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 16, 17, 18, 19, 28, 29 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }
},
{ /* 0100 */
- { { 14, 18, 21 }, { 15, 19, 22 } },
+ 2, { 14, 15 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 16, 17, 18, 19, 28, 29 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }
},
{ /* 0101 */
- { { 14, 18, 21 }, { 15, 19, 22 } },
+ 2, { 14, 15 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 16, 17, 18, 19, 28, 29 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }
},
{ /* 0110 */
- { { 15, 18, 21 }, { 16, 19, 22 } },
+ 2, { 15, 16 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 28, 17, 18, 19, 29, 30 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14 }
},
{ /* 0111 */
- { { 15, 18, 21 }, { 16, 19, 22 } },
+ 2, { 15, 16 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 28, 17, 18, 19, 29, 30 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14 }
},
{ /* 1000 */
- { { 15, 18, 21 }, { 16, 19, 22 } },
+ 2, { 15, 16 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 28, 17, 18, 19, 29, 30 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14 }
},
{ /* 1001 */
- { { 16, 18, 21 }, { 17, 19, 22 } },
+ 2, { 16, 17 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 18, 19, 30, 31 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14, 15 }
},
{ /* 1010 */
- { { 16, 18, 21 }, { 17, 19, 22 } },
+ 2, { 16, 17 },
{ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 18, 19, 30, 31 },
{ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14, 15 }
},
@@ -592,3 +537,382 @@ static const struct csrcb_map_tbl dram_addrmap_d_e_128 = {
*/
}
};
+
+/*
+ * Row/Column/Bank address mappings for revs F/G in 64-bit mode, no interleave.
+ */
+static const struct _rcbmap_tbl dram_addrmap_f_64 = {
+ MC_REVS_FG,
+ 64,
+ {
+ { /* 0000 */
+ 2, { 12, 13 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 14, 15, 16, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11 },
+ },
+ { /* 0001 */
+ 2, { 13, 14 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 15, 16, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 },
+ },
+ { /* 0010 */
+ 2, { 13, 14 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 15, 16, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 },
+ },
+ { /* 0011 */
+ 2, { 14, 15 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 16, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13 },
+ },
+ { /* 0100 */
+ 3, { 13, 14, 15 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 16, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13 },
+ },
+ { /* 0101 */
+ 3, { 13, 14, 15 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 16, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
+ },
+ { /* 0110 */
+ 2, { 14, 15 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 16, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13 },
+ },
+ { /* 0111 */
+ 3, { 13, 14, 15 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 16, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
+ },
+ { /* 1000 */
+ 3, { 14, 15, 16 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13 },
+ },
+ { /* 1001 */
+ 3, { 14, 15, 16 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13 },
+ },
+ { /* 1010 */
+ 3, { 13, 14, 15 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 16, 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
+ },
+ { /* 1011 */
+ 3, { 14, 15, 16 },
+ { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 17 },
+ { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, MC_PC_ALL, 13 },
+ },
+ /*
+ * remainder unused
+ */
+ }
+};
+
+/*
+ * Row/Column/Bank address mappings for revs F/G in 128-bit mode, no interleave.
+ */
+static const struct _rcbmap_tbl dram_addrmap_f_128 = {
+ MC_REVS_FG,
+ 128,
+ {
+ { /* 0000 */
+ 2, { 13, 14 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 15, 16, 17, 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12 },
+ },
+ { /* 0001 */
+ 2, { 14, 15 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 16, 17, 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 },
+ },
+ { /* 0010 */
+ 2, { 14, 15 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 16, 17, 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 },
+ },
+ { /* 0011 */
+ 2, { 15, 16 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 17, 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14 },
+ },
+ { /* 0100 */
+ 3, { 14, 15, 16 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 17, 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 },
+ },
+ { /* 0101 */
+ 3, { 14, 15, 16 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 17, 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 },
+ },
+ { /* 0110 */
+ 2, { 15, 16 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 17, 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14 },
+ },
+ { /* 0111 */
+ 3, { 14, 15, 16 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 17, 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 },
+ },
+ { /* 1000 */
+ 3, { 15, 16, 17 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14 },
+ },
+ { /* 1001 */
+ 3, { 15, 16, 17 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14 },
+ },
+ { /* 1010 */
+ 3, { 14, 15, 16 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 17, 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 },
+ },
+ { /* 1011 */
+ 3, { 15, 16, 17 },
+ { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 18 },
+ { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, MC_PC_ALL, 14 },
+ },
+ /*
+ * remainder unused
+ */
+ }
+};
+
+/*
+ * Bank swizzling is an option in revisions E and later. Each internal-bank-
+ * select address bit is xor'd with two row address bits. Which row
+ * address bits to use is not dependent on bank address mode but on
+ * revision and dram controller width alone.
+ *
+ * While rev E only supports 2 bank address bits, rev F supports 3 but not
+ * all chip-select bank address modes use all 3. These tables will list
+ * the row bits to use in swizzling for the maximum number of supported
+ * bank address bits - the consumer musr determine how many should be
+ * applied (listed in the above row/col/bank tables).
+ */
+
+static const struct _bnkswzl_tbl bnswzl_info_e_64 = {
+ MC_REV_E,
+ 64,
+ {
+ {
+ { 17, 20 }, /* rows bits to swizzle with BA0 */
+ { 18, 21 }, /* rows bits to swizzle with BA1 */
+ /* only 2 bankaddr bits on rev E */
+ }
+ }
+};
+
+static const struct _bnkswzl_tbl bnswzl_info_e_128 = {
+ MC_REV_E,
+ 128,
+ {
+ {
+ { 18, 21 }, /* rows bits to swizzle with BA0 */
+ { 19, 22 }, /* rows bits to swizzle with BA1 */
+ /* only 2 bankaddr bits on rev E */
+ }
+ }
+};
+
+static const struct _bnkswzl_tbl bnswzl_info_f_64 = {
+ MC_REVS_FG,
+ 64,
+ {
+ {
+ { 17, 22 }, /* rows bits to swizzle with BA0 */
+ { 18, 23 }, /* rows bits to swizzle with BA1 */
+ { 19, 24 }, /* rows bits to swizzle with BA2 */
+ }
+ }
+};
+
+static const struct _bnkswzl_tbl bnswzl_info_f_128 = {
+ MC_REVS_FG,
+ 128,
+ {
+ {
+ { 18, 23 }, /* rows bits to swizzle with BA0 */
+ { 19, 24 }, /* rows bits to swizzle with BA1 */
+ { 20, 25 }, /* rows bits to swizzle with BA2 */
+ }
+ }
+};
+
+/*
+ * Yet another highbit function. This really needs to go to common source.
+ * Returns range 0 to 64 inclusive;
+ */
+static int
+topbit(uint64_t i)
+{
+ int h = 1;
+
+ if (i == 0)
+ return (0);
+
+ if (i & 0xffffffff00000000ULL) {
+ h += 32;
+ i >>= 32;
+ }
+
+ if (i & 0xffff0000) {
+ h += 16;
+ i >>= 16;
+ }
+
+ if (i & 0xff00) {
+ h += 8;
+ i >>= 8;
+ }
+
+ if (i & 0xf0) {
+ h += 4;
+ i >>= 4;
+ }
+
+ if (i & 0xc) {
+ h += 2;
+ i >>= 2;
+ }
+
+ if (i & 0x2)
+ h += 1;
+
+ return (h);
+}
+
+/*
+ * Lookup the Chip-Select Bank Address Mode Encoding table for a given
+ * chip revision and chip-select mode.
+ */
+const struct rct_bnkaddrmode *
+rct_bnkaddrmode(uint_t mcrev, uint_t csmode)
+{
+ int i;
+ const struct _bnkaddrmode_tbldesc *bdp = bnkaddr_tbls;
+
+ for (i = 0; i < sizeof (bnkaddr_tbls) /
+ sizeof (struct _bnkaddrmode_tbldesc);
+ i++, bdp++) {
+ if (MC_REV_MATCH(mcrev, bdp->revmask) && csmode < bdp->nmodes)
+ return (&bdp->modetbl[csmode]);
+
+ }
+
+ return (NULL);
+}
+
+/*
+ * Lookup the DRAM Address Mapping table for a given chip revision, access
+ * width, bank-swizzle and chip-select mode.
+ */
+const struct rct_rcbmap *
+rct_rcbmap(uint_t mcrev, int width, uint_t csmode)
+{
+ const struct _rcbmap_tbl *rcbm;
+ int i;
+
+ for (i = 0; i < sizeof (rcbmap_tbls) /
+ sizeof (struct _rcbmap_tbldesc); i++) {
+ rcbm = rcbmap_tbls[i].rcbmap;
+ if (MC_REV_MATCH(mcrev, rcbm->mt_revmask) &&
+ rcbm->mt_width == width && csmode < rcbmap_tbls[i].nmodes)
+ return (&rcbm->mt_csmap[csmode]);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Lookup the bank swizzling information for a given chip revision and
+ * access width.
+ */
+const struct rct_bnkswzlinfo *
+rct_bnkswzlinfo(uint_t mcrev, int width)
+{
+ int i;
+ const struct _bnkswzl_tbl *swztp;
+
+ for (i = 0; i < sizeof (bnkswzl_tbls) /
+ sizeof (struct rcb_bnkswzl_tbl *); i++) {
+ swztp = bnkswzl_tbls[i];
+ if (MC_REV_MATCH(mcrev, swztp->swzt_revmask) &&
+ swztp->swzt_width == width)
+ return (&swztp->swzt_bits);
+ }
+
+ return (NULL);
+}
+
+void
+rct_csintlv_bits(uint_t mcrev, int width, uint_t csmode, int factor,
+ struct rct_csintlv *csid)
+{
+ int i, lstbnkbit;
+ size_t csz;
+ const struct rct_bnkaddrmode *bam;
+ const struct rct_rcbmap *rcm;
+
+ /*
+ * 8-way cs interleave for some large cs sizes in 128-bit mode is
+ * not implemented prior to rev F.
+ */
+ if (factor == 8 && width == 128 &&
+ ((MC_REV_MATCH(mcrev, MC_REVS_BC) && csmode == 0x6) ||
+ (MC_REV_MATCH(mcrev, MC_REVS_DE) &&
+ (csmode == 0x9 || csmode == 0xa)))) {
+ csid->csi_factor = 0;
+ return;
+ }
+
+ if ((bam = rct_bnkaddrmode(mcrev, csmode)) == NULL ||
+ (rcm = rct_rcbmap(mcrev, width, csmode)) == NULL) {
+ csid->csi_factor = 0;
+ return;
+ }
+
+ csz = MC_CS_SIZE(bam, width);
+
+ switch (factor) {
+ case 2:
+ csid->csi_nbits = 1;
+ break;
+ case 4:
+ csid->csi_nbits = 2;
+ break;
+ case 8:
+ csid->csi_nbits = 3;
+ break;
+ default:
+ csid->csi_factor = 0;
+ return;
+ }
+
+ csid->csi_hibit = topbit(csz) - 1;
+
+ /*
+ * The first row bit is immediately after the last bank bit.
+ */
+ lstbnkbit = 0;
+ for (i = 0; i < rcm->rcb_nbankbits; i++)
+ if (rcm->rcb_bankbit[i] > lstbnkbit)
+ lstbnkbit = rcm->rcb_bankbit[i];
+
+ csid->csi_lobit = lstbnkbit + 1;
+
+ csid->csi_factor = factor;
+}
diff --git a/usr/src/common/mc/mc-amd/mcamd_unumtopa.c b/usr/src/common/mc/mc-amd/mcamd_unumtopa.c
index 90c1dcd56f..1b859ef6b4 100644
--- a/usr/src/common/mc/mc-amd/mcamd_unumtopa.c
+++ b/usr/src/common/mc/mc-amd/mcamd_unumtopa.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -38,38 +37,38 @@
#include <mcamd_api.h>
#include <mcamd_err.h>
-extern int mc_offset_to_pa(struct mcamd_hdl *, mcamd_node_t *, mcamd_node_t *,
- uint64_t, uint64_t *);
-
/*
* The submitted unum must have the MC and DIMM numbers and an offset.
* Any cs info it has will not be used - we will reconstruct cs info.
* This is because cs is not in the topology used for diagnosis.
*/
int
-mcamd_unumtopa(struct mcamd_hdl *hdl, mcamd_node_t *root, struct mc_unum *unump,
+mcamd_unumtopa(struct mcamd_hdl *hdl, mcamd_node_t *root, mc_unum_t *unump,
uint64_t *pa)
{
mcamd_node_t *mc, *dimm;
- uint64_t num, hole;
+ uint64_t num, holesz;
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_unumtopa: chip %d "
"mc %d dimm %d offset 0x%llx\n", unump->unum_chip, unump->unum_mc,
unump->unum_dimms[0], unump->unum_offset);
if (!MCAMD_RC_OFFSET_VALID(unump->unum_offset)) {
- mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "Offset invalid\n");
+ mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_unumtopa: offset "
+ "invalid\n");
return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
}
/*
* Search current config for a MC number matching the chip in the
- * unum. MC property num is by chip, not MC on chip.
+ * unum.
*/
for (mc = mcamd_mc_next(hdl, root, NULL); mc != NULL;
mc = mcamd_mc_next(hdl, root, mc)) {
- if (!mcamd_get_numprop(hdl, mc, MCAMD_PROP_NUM, &num) ||
- !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_HOLE, &hole)) {
+ if (!mcamd_get_numprops(hdl,
+ mc, MCAMD_PROP_NUM, &num,
+ mc, MCAMD_PROP_DRAMHOLE_SIZE, &holesz,
+ NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_unumtopa: "
"failed to lookup num, dramhole for MC 0x%p\n", mc);
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
@@ -125,14 +124,12 @@ mcamd_unumtopa(struct mcamd_hdl *hdl, mcamd_node_t *root, struct mc_unum *unump,
* If this MC has a dram address hole just below 4GB then we must
* hoist all address from the hole start upwards by the hole size
*/
- if (hole & MC_DC_HOLE_VALID) {
- uint64_t hsz = (hole & MC_DC_HOLE_OFFSET_MASK) <<
- MC_DC_HOLE_OFFSET_LSHIFT;
- if (*pa >= 0x100000000 - hsz)
- *pa += hsz;
+ if (holesz != 0) {
+ if (*pa >= 0x100000000 - holesz)
+ *pa += holesz;
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_untopa: hoist "
"above dram hole of size 0x%llx to get pa=0x%llx",
- hsz, *pa);
+ holesz, *pa);
}
return (0);
diff --git a/usr/src/lib/fm/topo/libtopo/common/hc_canon.h b/usr/src/lib/fm/topo/libtopo/common/hc_canon.h
index 8da0c69c59..3edb066d32 100644
--- a/usr/src/lib/fm/topo/libtopo/common/hc_canon.h
+++ b/usr/src/lib/fm/topo/libtopo/common/hc_canon.h
@@ -45,12 +45,14 @@ static const char *Hc_canon[] = {
"chip-select",
"cpu",
"dimm",
+ "rank",
"disk",
"hostbridge",
"interconnect",
"chassis",
"ioboard",
"memory-controller",
+ "dram-channel",
"motherboard",
"pcibus",
"pcidev",
diff --git a/usr/src/lib/fm/topo/libtopo/common/mem.c b/usr/src/lib/fm/topo/libtopo/common/mem.c
index c228ff28b3..6d89042372 100644
--- a/usr/src/lib/fm/topo/libtopo/common/mem.c
+++ b/usr/src/lib/fm/topo/libtopo/common/mem.c
@@ -51,8 +51,6 @@ static int mem_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
nvlist_t **);
static int mem_expand(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
nvlist_t **);
-static int mem_asru(topo_mod_t *, tnode_t *, topo_version_t,
- nvlist_t *, nvlist_t **);
#define MEM_VERSION TOPO_VERSION
@@ -69,8 +67,6 @@ static const topo_method_t mem_methods[] = {
TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, mem_unusable },
{ TOPO_METH_EXPAND, TOPO_METH_UNUSABLE_DESC,
TOPO_METH_EXPAND_VERSION, TOPO_STABILITY_INTERNAL, mem_expand },
- { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
- TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, mem_asru },
{ NULL }
};
@@ -138,15 +134,21 @@ mem_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
* PA then use that. Otherwise just format the unum element.
*/
if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0) {
- format = FM_FMRI_SCHEME_MEM ":///"
- FM_FMRI_MEM_UNUM "=%1$s/" FM_FMRI_MEM_OFFSET "=%2$llx";
+ format = FM_FMRI_SCHEME_MEM ":///%1$s/"
+ FM_FMRI_MEM_OFFSET "=%2$llx";
} else if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0) {
- format = FM_FMRI_SCHEME_MEM ":///"
- FM_FMRI_MEM_UNUM "=%1$s/" FM_FMRI_MEM_PHYSADDR "=%2$llx";
+ format = FM_FMRI_SCHEME_MEM ":///%1$s/"
+ FM_FMRI_MEM_PHYSADDR "=%2$llx";
} else
- format = FM_FMRI_SCHEME_MEM ":///" FM_FMRI_MEM_UNUM "=%1$s";
+ format = FM_FMRI_SCHEME_MEM ":///" "%1$s";
- len = snprintf(NULL, 0, format, unum, val);
+ /*
+ * If we have a well-formed unum we step over the hc:/// prefix
+ */
+ if (strncmp(unum, "hc:///", 6) == 0)
+ unum += 6;
+
+ len = snprintf(NULL, 0, format, unum, val) + 1;
buf = topo_mod_zalloc(mod, len);
if (buf == NULL) {
@@ -206,47 +208,3 @@ mem_expand(topo_mod_t *mod, tnode_t *node, topo_version_t version,
{
return (-1);
}
-
-/*ARGSUSED*/
-static int
-mem_asru(topo_mod_t *mod, tnode_t *node, topo_version_t version,
- nvlist_t *in, nvlist_t **out)
-{
- int err;
- uint64_t pa = 0, offset = 0;
- int incl_pa = 0, incl_offset = 0;
- nvlist_t *hcsp = NULL;
- nvlist_t *asru;
- char *cstr;
-
- if (nvlist_lookup_nvlist(in, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) {
- incl_pa = (nvlist_lookup_uint64(hcsp,
- "asru-"FM_FMRI_MEM_PHYSADDR, &pa) == 0);
- incl_offset = (nvlist_lookup_uint64(hcsp,
- "asru-"FM_FMRI_MEM_OFFSET, &offset) == 0);
- }
-
- if (topo_fmri_nvl2str(topo_mod_handle(mod), in, &cstr, &err) < 0)
- return (topo_mod_seterrno(mod, err));
-
- if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) {
- topo_mod_strfree(mod, cstr);
- return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
- }
- err = nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION);
- err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM);
- err |= nvlist_add_string(asru, FM_FMRI_MEM_UNUM, cstr);
- if (incl_pa)
- err |= nvlist_add_uint64(asru, FM_FMRI_MEM_PHYSADDR, pa);
- if (incl_offset)
- err |= nvlist_add_uint64(asru, FM_FMRI_MEM_OFFSET, offset);
- topo_mod_strfree(mod, cstr);
- if (err != 0) {
- nvlist_free(asru);
- return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
- }
-
- *out = asru;
-
- return (0);
-}
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_protocol.c b/usr/src/lib/fm/topo/libtopo/common/topo_protocol.c
index 81ae45e3ca..85ced6fa43 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_protocol.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_protocol.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -39,35 +38,41 @@
#include <libtopo.h>
static int
-topo_asru_compute(topo_hdl_t *thp, const char *scheme, nvlist_t *rsrc,
- nvlist_t **asru)
+topo_compute(tnode_t *node, nvlist_t *stub, const char *method,
+ topo_version_t version, nvlist_t *rsrc, nvlist_t **asru, int *err)
{
- int err;
+ int rc;
+ char *scheme;
+ topo_hdl_t *thp = node->tn_hdl;
tnode_t *rnode;
- if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
- return (ETOPO_METHOD_NOTSUP);
-
- if (topo_method_invoke(rnode, TOPO_METH_ASRU_COMPUTE,
- TOPO_METH_ASRU_COMPUTE_VERSION, rsrc, asru, &err) != 0)
- return (err);
+ /*
+ * First try the originating enumerator for
+ * a compute method. If none is supported, try the
+ * node's scheme-specific enumerator.
+ */
+ if (topo_method_invoke(node, method, version, rsrc, asru, err) == 0)
+ return (0);
- return (0);
-}
+ if (*err != ETOPO_METHOD_NOTSUP)
+ return (-1);
-static int
-topo_fru_compute(topo_hdl_t *thp, const char *scheme, nvlist_t *rsrc,
- nvlist_t **fru)
-{
- int err;
- tnode_t *rnode;
+ if ((rc = nvlist_lookup_string(stub, FM_FMRI_SCHEME, &scheme)) != 0) {
+ if (rc == ENOENT) {
+ *err = ETOPO_FMRI_MALFORM;
+ } else {
+ *err = ETOPO_FMRI_NVL;
+ }
+ return (-1);
+ }
- if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
- return (ETOPO_METHOD_NOTSUP);
+ if ((rnode = topo_hdl_root(thp, scheme)) == NULL) {
+ *err = ETOPO_METHOD_NOTSUP;
+ return (-1);
+ }
- if (topo_method_invoke(rnode, TOPO_METH_FRU_COMPUTE,
- TOPO_METH_FRU_COMPUTE_VERSION, rsrc, fru, &err) != 0)
- return (err);
+ if (topo_method_invoke(rnode, method, version, rsrc, asru, err) != 0)
+ return (-1);
return (0);
}
@@ -77,35 +82,20 @@ topo_node_asru(tnode_t *node, nvlist_t **asru, nvlist_t *priv, int *err)
{
int rc;
nvlist_t *ap;
- char *scheme;
if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU, &ap,
err) != 0)
return (-1);
if (node->tn_fflags & TOPO_ASRU_COMPUTE) {
- if ((rc = nvlist_lookup_string(ap, FM_FMRI_SCHEME, &scheme))
- != 0) {
- if (rc == ENOENT)
- *err = ETOPO_FMRI_MALFORM;
- else
- *err = ETOPO_FMRI_NVL;
- nvlist_free(ap);
- return (-1);
- }
- if ((rc = topo_asru_compute(node->tn_hdl, scheme, priv, asru))
- != 0) {
- nvlist_free(ap);
- *err = rc;
- return (-1);
- }
+ rc = topo_compute(node, ap, TOPO_METH_ASRU_COMPUTE,
+ TOPO_METH_ASRU_COMPUTE_VERSION, priv, asru, err);
nvlist_free(ap);
- return (0);
+ return (rc);
} else {
*asru = ap;
+ return (0);
}
-
- return (0);
}
int
@@ -113,36 +103,20 @@ topo_node_fru(tnode_t *node, nvlist_t **fru, nvlist_t *priv, int *err)
{
int rc;
nvlist_t *fp;
- char *scheme;
if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU, &fp,
err) != 0)
return (-1);
if (node->tn_fflags & TOPO_FRU_COMPUTE) {
- if ((rc = nvlist_lookup_string(fp, FM_FMRI_SCHEME, &scheme))
- != 0) {
- if (rc == ENOENT)
- *err = ETOPO_FMRI_MALFORM;
- else
- *err = ETOPO_FMRI_NVL;
-
- nvlist_free(fp);
- return (-1);
- }
- if ((rc = topo_fru_compute(node->tn_hdl, scheme, priv, fru))
- != 0) {
- nvlist_free(fp);
- *err = rc;
- return (-1);
- }
+ rc = topo_compute(node, fp, TOPO_METH_FRU_COMPUTE,
+ TOPO_METH_FRU_COMPUTE_VERSION, priv, fru, err);
nvlist_free(fp);
- return (0);
+ return (rc);
} else {
*fru = fp;
+ return (0);
}
-
- return (0);
}
int
@@ -220,7 +194,6 @@ topo_node_fru_set(tnode_t *node, nvlist_t *fru, int flag, int *err)
int
topo_node_label_set(tnode_t *node, char *label, int *err)
{
-
/*
* Inherit FRU property from our parent if * not specified
*/
diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/chip.c b/usr/src/lib/fm/topo/modules/i86pc/chip/chip.c
index 869abce891..9563120b61 100644
--- a/usr/src/lib/fm/topo/modules/i86pc/chip/chip.c
+++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip.c
@@ -29,6 +29,7 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
@@ -44,6 +45,7 @@
#include <sys/fm/protocol.h>
#include <sys/systeminfo.h>
#include <sys/mc.h>
+#include <sys/mc_amd.h>
#include <fm/topo_mod.h>
#include "chip.h"
@@ -64,22 +66,74 @@
static int chip_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
topo_instance_t, void *);
+static int mem_asru_compute(topo_mod_t *, tnode_t *, topo_version_t,
+ nvlist_t *, nvlist_t **);
+
const topo_modinfo_t chip_info =
{ "chip", CHIP_VERSION, chip_enum, NULL};
+const topo_method_t rank_methods[] = {
+ { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
+ TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
+ mem_asru_compute },
+ { NULL }
+};
+
+static const struct debugopt {
+ const char *optname;
+ int optval;
+} debugopts[] = {
+ { "err", TOPO_DBG_ERR },
+ { "mod", TOPO_DBG_MOD },
+ { "log", TOPO_DBG_LOG },
+ { "walk", TOPO_DBG_WALK },
+ { "tree", TOPO_DBG_TREE },
+ { "all", TOPO_DBG_ALL }
+};
+
+static nvlist_t *cs_fmri[MC_CHIP_NCS];
+
+static void
+whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[160];
+
+ if (nerr != NULL)
+ ++*nerr;
+
+ va_start(ap, fmt);
+ (void) vsnprintf(buf, sizeof (buf), fmt, ap);
+ va_end(ap);
+
+ topo_mod_dprintf(mod, "%s", buf);
+}
+
int
_topo_init(topo_mod_t *mod)
{
+ const char *debugstr = getenv("TOPOCHPDBG");
chip_t *chip;
+ int i;
+
+ if (debugstr != NULL) {
+ for (i = 0; i < sizeof (debugopts) / sizeof (struct debugopt);
+ i++) {
+ if (strncmp(debugstr, debugopts[i].optname, 4) == 0) {
+ topo_mod_clrdebug(mod);
+ topo_mod_setdebug(mod, debugopts[i].optval);
+ break; /* handle a single option only */
+ }
+ }
+ }
- topo_mod_setdebug(mod, TOPO_DBG_ALL);
topo_mod_dprintf(mod, "initializing chip enumerator\n");
if ((chip = topo_mod_zalloc(mod, sizeof (chip_t))) == NULL)
return (topo_mod_seterrno(mod, EMOD_NOMEM));
if ((chip->chip_kc = kstat_open()) == NULL) {
- topo_mod_dprintf(mod, "kstat_open failed: %s\n",
+ whinge(mod, NULL, "kstat_open failed: %s\n",
strerror(errno));
topo_mod_free(mod, chip, sizeof (chip_t));
return (topo_mod_seterrno(mod, errno));
@@ -94,7 +148,7 @@ _topo_init(topo_mod_t *mod)
}
if (topo_mod_register(mod, &chip_info, (void *)chip) != 0) {
- topo_mod_dprintf(mod, "failed to register hc: "
+ whinge(mod, NULL, "failed to register hc: "
"%s\n", topo_mod_errmsg(mod));
topo_mod_free(mod, chip->chip_cpustats,
(chip->chip_ncpustats + 1) * sizeof (kstat_t *));
@@ -151,6 +205,29 @@ chip_longprop(tnode_t *cnode, kstat_t *ksp, const char *name)
return (-1);
}
+static int
+mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst,
+ nvlist_t **nvl)
+{
+ nvlist_t *args = NULL, *pfmri = NULL;
+ topo_hdl_t *thp = topo_mod_handle(mod);
+ int err;
+
+ if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
+ topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
+ nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
+ nvlist_free(pfmri);
+ nvlist_free(args);
+ return (-1);
+ }
+
+ *nvl = topo_fmri_create(thp, FM_FMRI_SCHEME_HC, name, inst, args, &err);
+ nvlist_free(pfmri);
+ nvlist_free(args);
+
+ return (nvl != NULL ? 0 : -1); /* caller must free nvlist */
+}
+
static nvlist_t *
cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask)
{
@@ -175,86 +252,110 @@ cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask)
return (asru);
}
+static nvlist_t *
+mem_fmri_create(topo_mod_t *mod)
+{
+ nvlist_t *asru;
+
+ if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
+ return (NULL);
+
+ if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 ||
+ nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0) {
+ nvlist_free(asru);
+ return (NULL);
+ }
+
+ return (asru);
+}
+
static int
cpu_create(topo_mod_t *mod, tnode_t *pnode, const char *name, int chipid,
chip_t *chip)
{
kstat_named_t *k;
- topo_hdl_t *thp;
- nvlist_t *fmri, *pfmri, *asru, *args;
+ nvlist_t *fmri, *asru;
tnode_t *cnode;
int i, err, nerr = 0;
+ int coreid, cpuid;
if (topo_node_range_create(mod, pnode, name, 0,
chip->chip_ncpustats) < 0)
return (-1);
- thp = topo_mod_handle(mod);
-
for (i = 0; i <= chip->chip_ncpustats; i++) {
-
if (chip->chip_cpustats[i] == NULL)
continue;
+ /*
+ * The chip_id in the cpu_info kstat numbers the individual
+ * chips from 0 to #chips - 1.
+ */
if ((k = kstat_data_lookup(chip->chip_cpustats[i], "chip_id"))
- == NULL || k->value.l != chipid) {
- ++nerr;
+ == NULL) {
+ whinge(mod, &nerr, "cpu_create: chip_id lookup via "
+ "kstats failed\n");
continue;
}
+ if (k->value.l != chipid)
+ continue; /* not an error */
+
+ /*
+ * The clog_id in the cpu_info kstat numbers the processor
+ * cores of a single chip from 0 to #chips - 1.
+ */
if ((k = kstat_data_lookup(chip->chip_cpustats[i], "clog_id"))
== NULL) {
- ++nerr;
+ whinge(mod, &nerr, "cpu_create: clog_id lookup via "
+ "kstats failed\n");
continue;
}
+ coreid = k->value.l;
- args = pfmri = NULL;
- if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
- topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
- nvlist_add_nvlist(args,
- TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
- nvlist_free(pfmri);
- nvlist_free(args);
- ++nerr;
- continue;
- }
+ if (mkrsrc(mod, pnode, name, coreid, &fmri) != 0) {
+ whinge(mod, &nerr, "cpu_create: mkrsrc failed\n");
+ continue;
+ }
- fmri = topo_fmri_create(thp, FM_FMRI_SCHEME_HC, name,
- (topo_instance_t)k->value.l, args, &err);
- nvlist_free(pfmri);
- nvlist_free(args);
- if (fmri == NULL) {
- ++nerr;
+ /*
+ * The core_id in the cpu_info kstat corresponds to the
+ * processor id used in psradm etc - ie cpuid as used
+ * in a cpu scheme fmri. I'm not making this up!
+ */
+ if ((k = kstat_data_lookup(chip->chip_cpustats[i], "core_id"))
+ == NULL) {
+ whinge(mod, &nerr, "cpu_create: core_id lookup via "
+ "kstats failed\n");
continue;
}
+ cpuid = k->value.l;
- if ((cnode = topo_node_bind(mod, pnode, name, i, fmri,
+ if ((cnode = topo_node_bind(mod, pnode, name, cpuid, fmri,
NULL)) == NULL) {
- ++nerr;
+ whinge(mod, &nerr, "cpu_create: node bind failed\n");
nvlist_free(fmri);
continue;
}
nvlist_free(fmri);
- if ((asru = cpu_fmri_create(mod, i, NULL, 0)) != NULL) {
+ if ((asru = cpu_fmri_create(mod, cpuid, NULL, 0)) != NULL) {
(void) topo_node_asru_set(cnode, asru, 0, &err);
nvlist_free(asru);
} else {
- ++nerr;
+ whinge(mod, &nerr, "cpu_create: cpu_fmri_create "
+ "failed\n");
}
(void) topo_node_fru_set(cnode, NULL, 0, &err);
}
- if (nerr != 0)
- return (-1);
- else
- return (0);
+ return (nerr == 0 ? 0 : -1);
}
static int
-nvprop_add(nvpair_t *nvp, const char *pgname, tnode_t *node)
+nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node)
{
- int err;
+ int err = 0;
char *pname = nvpair_name(nvp);
switch (nvpair_type(nvp)) {
@@ -288,25 +389,66 @@ nvprop_add(nvpair_t *nvp, const char *pgname, tnode_t *node)
}
default:
- return (-1);
+ whinge(mod, &err, "nvprop_add: Can't handle type %d for "
+ "'%s' in property group %s of %s node\n",
+ nvpair_type(nvp), pname, pgname, topo_node_name(node));
+ return (1);
}
}
-nvlist_t *
-mem_fmri_create(topo_mod_t *mod)
+static int
+dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name)
{
- nvlist_t *asru;
+ tnode_t *chnode;
+ nvlist_t *fmri;
+ char *socket;
+ int i, nchan;
+ int err, nerr = 0;
- if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
- return (NULL);
+ /*
+ * We will enumerate the number of channels present even if only
+ * channel A is in use (i.e., running in 64-bit mode). Only
+ * the socket 754 package has a single channel.
+ */
+ if (topo_prop_get_string(pnode, MCT_PGROUP, "socket",
+ &socket, &err) != 0)
+ return (-1);
- if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 ||
- nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0) {
- nvlist_free(asru);
- return (NULL);
+ if (strcmp(socket, "Socket 754") == 0)
+ nchan = 1;
+ else
+ nchan = 2;
+
+ topo_mod_strfree(mod, socket);
+
+ if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0)
+ return (-1);
+
+ for (i = 0; i < nchan; i++) {
+ if (mkrsrc(mod, pnode, name, i, &fmri) != 0) {
+ whinge(mod, &nerr, "dramchan_create: mkrsrc "
+ "failed\n");
+ continue;
+ }
+
+ if ((chnode = topo_node_bind(mod, pnode, name, i, fmri,
+ NULL)) == NULL) {
+ nvlist_free(fmri);
+ whinge(mod, &nerr, "dramchan_create: node bind "
+ "failed\n");
+ continue;
+ }
+
+ nvlist_free(fmri);
+
+ (void) topo_pgroup_create(chnode, CHAN_PGROUP,
+ TOPO_STABILITY_PRIVATE, &err);
+
+ (void) topo_prop_set_string(chnode, CHAN_PGROUP, "channel",
+ TOPO_PROP_SET_ONCE, i == 0 ? "A" : "B", &err);
}
- return (asru);
+ return (nerr == 0 ? 0 : -1);
}
static int
@@ -315,67 +457,218 @@ cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc)
int i, err, nerr = 0;
nvpair_t *nvp;
tnode_t *csnode;
- topo_hdl_t *thp;
nvlist_t *fmri, **csarr = NULL;
- nvlist_t *pfmri, *args;
uint64_t csnum;
uint_t ncs;
- if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0 ||
- ncs == 0)
+ if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0)
return (-1);
+ if (ncs == 0)
+ return (0); /* no chip-selects configured on this node */
+
if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
return (-1);
- thp = topo_mod_handle(mod);
for (i = 0; i < ncs; i++) {
if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
- ++nerr;
+ whinge(mod, &nerr, "cs_create: cs num property "
+ "missing\n");
continue;
}
- args = pfmri = NULL;
- if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
- topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
- nvlist_add_nvlist(args,
- TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
- nvlist_free(pfmri);
- nvlist_free(args);
- ++nerr;
- continue;
- }
- fmri = topo_fmri_create(thp, FM_FMRI_SCHEME_HC, name,
- csnum, args, &err);
- nvlist_free(pfmri);
- nvlist_free(args);
- if (fmri == NULL) {
- ++nerr;
+ if (mkrsrc(mod, pnode, name, csnum, &fmri) != 0) {
+ whinge(mod, &nerr, "cs_create: mkrsrc failed\n");
continue;
}
if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri,
NULL)) == NULL) {
nvlist_free(fmri);
- ++nerr;
+ whinge(mod, &nerr, "cs_create: node bind failed\n");
continue;
}
- nvlist_free(fmri);
+ cs_fmri[csnum] = fmri; /* nvlist will be freed in mc_create */
+
+ (void) topo_node_asru_set(csnode, fmri, 0, &err);
(void) topo_pgroup_create(csnode, CS_PGROUP,
TOPO_STABILITY_PRIVATE, &err);
for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
nvp = nvlist_next_nvpair(csarr[i], nvp)) {
- (void) nvprop_add(nvp, CS_PGROUP, csnode);
+ nerr += nvprop_add(mod, nvp, CS_PGROUP, csnode);
}
}
- if (nerr != 0)
- return (-1);
- else
- return (0);
+ return (nerr == 0 ? 0 : -1);
+}
+
+/*
+ * Registered method for asru computation for rank nodes. The 'node'
+ * argument identifies the node for which we seek an asru. The 'in'
+ * argument is used to select which asru we will return, as follows:
+ *
+ * - the node name must be "dimm" or "rank"
+ * - if 'in' is NULL then return any statically defined asru for this node
+ * - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru
+ * with unum being the hc path to the dimm or rank (this method is called
+ * as part of dynamic asru computation for rank nodes only, but dimm_create
+ * also calls it directly to construct a "mem" scheme asru for a dimm node)
+ * - if 'in' in addition includes an hc-specific member which specifies
+ * asru-physaddr or asru-offset then these are includes in the "mem" scheme
+ * asru as additional membersl physaddr and offset
+ */
+/*ARGSUSED*/
+static int
+mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version,
+ nvlist_t *in, nvlist_t **out)
+{
+ int incl_pa = 0, incl_offset = 0;
+ nvlist_t *hcsp, *asru;
+ uint64_t pa, offset;
+ char *scheme, *unum;
+ int err;
+
+ if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 &&
+ strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0)
+ return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
+
+ if (in == NULL) {
+ if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL,
+ TOPO_PROP_ASRU, out, &err) == 0)
+ return (0);
+ else
+ return (topo_mod_seterrno(mod, err));
+ } else {
+ if (nvlist_lookup_string(in, FM_FMRI_SCHEME, &scheme) != 0 ||
+ strcmp(scheme, FM_FMRI_SCHEME_HC) != 0)
+ return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
+ }
+
+ if (nvlist_lookup_nvlist(in, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) {
+ if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR,
+ &pa) == 0)
+ incl_pa = 1;
+
+ if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET,
+ &offset) == 0)
+ incl_offset = 1;
+ }
+
+ /* use 'in' to obtain resource path; could use node resource */
+ if (topo_fmri_nvl2str(topo_mod_handle(mod), in, &unum, &err) < 0)
+ return (topo_mod_seterrno(mod, err));
+
+ if ((asru = mem_fmri_create(mod)) == NULL) {
+ topo_mod_strfree(mod, unum);
+ return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
+ }
+
+ err = nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum);
+ if (incl_pa)
+ err |= nvlist_add_uint64(asru, FM_FMRI_MEM_PHYSADDR, pa);
+ if (incl_offset)
+ err |= nvlist_add_uint64(asru, FM_FMRI_MEM_OFFSET, offset);
+
+ topo_mod_strfree(mod, unum);
+ if (err != 0) {
+ nvlist_free(asru);
+ return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
+ }
+
+ *out = asru;
+ return (0);
+}
+
+static int
+rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl)
+{
+ uint64_t *csnumarr;
+ char **csnamearr;
+ uint_t ncs, ncsname;
+ tnode_t *ranknode;
+ nvlist_t *fmri, *pfmri = NULL;
+ uint64_t dsz, rsz;
+ int nerr = 0;
+ int err;
+ int i;
+
+ if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr,
+ &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames",
+ &csnamearr, &ncsname) != 0 || ncs != ncsname) {
+ whinge(mod, &nerr, "rank_create: "
+ "csnums/csnames extraction failed\n");
+ return (nerr);
+ }
+
+ if (topo_node_resource(pnode, &pfmri, &err) < 0) {
+ whinge(mod, &nerr, "rank_create: parent fmri lookup "
+ "failed\n");
+ return (nerr);
+ }
+
+ if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) {
+ whinge(mod, &nerr, "rank_create: range create failed\n");
+ nvlist_free(pfmri);
+ return (nerr);
+ }
+
+ if (topo_prop_get_uint64(pnode, DIMM_PGROUP, "size", &dsz, &err) == 0) {
+ rsz = dsz / ncs;
+ } else {
+ whinge(mod, &nerr, "rank_create: parent dimm has no size\n");
+ return (nerr);
+ }
+
+ for (i = 0; i < ncs; i++) {
+ if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, &fmri) < 0) {
+ whinge(mod, &nerr, "rank_create: mkrsrc failed\n");
+ continue;
+ }
+
+ if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i,
+ fmri, NULL)) == NULL) {
+ nvlist_free(fmri);
+ whinge(mod, &nerr, "rank_create: node bind "
+ "failed\n");
+ continue;
+ }
+
+ nvlist_free(fmri);
+
+ (void) topo_node_fru_set(ranknode, pfmri, 0, &err);
+
+ /*
+ * If a rank is faulted the asru is the associated
+ * chip-select, but if a page within a rank is faulted
+ * the asru is just that page. Hence the dual preconstructed
+ * and computed ASRU.
+ */
+ (void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]],
+ TOPO_ASRU_COMPUTE, &err);
+
+ if (topo_method_register(mod, ranknode, rank_methods) < 0)
+ whinge(mod, &nerr, "rank_create: "
+ "topo_method_register failed");
+
+ (void) topo_pgroup_create(ranknode, RANK_PGROUP,
+ TOPO_STABILITY_PRIVATE, &err);
+
+ (void) topo_prop_set_uint64(ranknode, RANK_PGROUP, "size",
+ TOPO_PROP_SET_ONCE, rsz, &err);
+
+ (void) topo_prop_set_string(ranknode, RANK_PGROUP, "csname",
+ TOPO_PROP_SET_ONCE, csnamearr[i], &err);
+
+ (void) topo_prop_set_uint64(ranknode, RANK_PGROUP, "csnum",
+ TOPO_PROP_SET_ONCE, csnumarr[i], &err);
+ }
+
+ nvlist_free(pfmri);
+
+ return (nerr);
}
static int
@@ -385,59 +678,61 @@ dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc)
nvpair_t *nvp;
tnode_t *dimmnode;
nvlist_t *fmri, *asru, **dimmarr = NULL;
- nvlist_t *pfmri, *args;
- uint64_t ldimmnum;
+ uint64_t num;
uint_t ndimm;
- topo_hdl_t *thp;
-
- thp = topo_mod_handle(mod);
- if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0 ||
- ndimm == 0)
+ if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) {
+ whinge(mod, NULL, "dimm_create: dimmlist lookup failed\n");
return (-1);
+ }
+
+ if (ndimm == 0)
+ return (0); /* no dimms present on this node */
- if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0)
+ if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) {
+ whinge(mod, NULL, "dimm_create: range create failed\n");
return (-1);
+ }
for (i = 0; i < ndimm; i++) {
- if (nvlist_lookup_uint64(dimmarr[i], "num", &ldimmnum) != 0) {
- ++nerr;
+ if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) {
+ whinge(mod, &nerr, "dimm_create: dimm num property "
+ "missing\n");
continue;
}
- args = pfmri = NULL;
- if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
- topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
- nvlist_add_nvlist(args,
- TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
- nvlist_free(pfmri);
- nvlist_free(args);
- ++nerr;
- continue;
- }
- fmri = topo_fmri_create(thp,
- FM_FMRI_SCHEME_HC, name, ldimmnum, args, &err);
- nvlist_free(pfmri);
- nvlist_free(args);
- if (fmri == NULL) {
- ++nerr;
+ if (mkrsrc(mod, pnode, name, num, &fmri) < 0) {
+ whinge(mod, &nerr, "dimm_create: mkrsrc failed\n");
continue;
}
- if ((dimmnode = topo_node_bind(mod, pnode, name, ldimmnum, fmri,
+ if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri,
NULL)) == NULL) {
nvlist_free(fmri);
- ++nerr;
+ whinge(mod, &nerr, "dimm_create: node bind "
+ "failed\n");
continue;
}
- (void) topo_node_fru_set(dimmnode, fmri, 0, &err);
- if ((asru = mem_fmri_create(mod)) != NULL) {
- (void) topo_node_asru_set(dimmnode, asru,
- TOPO_ASRU_COMPUTE, &err);
+ /*
+ * The asru is static but we prefer to publish it in the
+ * "mem" scheme so call the compute method directly to
+ * perform the conversion.
+ */
+ if (mem_asru_compute(mod, dimmnode,
+ TOPO_METH_ASRU_COMPUTE_VERSION, fmri, &asru) == 0) {
+ (void) topo_node_asru_set(dimmnode, asru, 0, &err);
nvlist_free(asru);
+ } else {
+
+ nvlist_free(fmri);
+ whinge(mod, &nerr, "dimm_create: mem_asru_compute "
+ "failed\n");
+ continue;
}
+ (void) topo_node_fru_set(dimmnode, fmri, 0, &err);
+
nvlist_free(fmri);
(void) topo_pgroup_create(dimmnode, DIMM_PGROUP,
@@ -445,35 +740,19 @@ dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc)
for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
- if (nvprop_add(nvp, DIMM_PGROUP, dimmnode) == 0) {
- continue;
- } else if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY) {
- uint64_t *csnumarr;
- uint_t ncs;
- int i;
-
- if (strcmp(nvpair_name(nvp), "csnums") != 0 ||
- nvpair_value_uint64_array(nvp, &csnumarr,
- &ncs) != 0)
- continue;
-
- for (i = 0; i < ncs; i++) {
- char name[7];
- (void) snprintf(name, sizeof (name),
- "csnum%d", i);
- (void) topo_prop_set_uint64(dimmnode,
- DIMM_PGROUP, name,
- TOPO_PROP_SET_ONCE,
- csnumarr[i], &err);
- }
- }
+ if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY &&
+ strcmp(nvpair_name(nvp), "csnums") == 0 ||
+ nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY &&
+ strcmp(nvpair_name(nvp), "csnames") == 0)
+ continue; /* used in rank_create() */
+
+ nerr += nvprop_add(mod, nvp, DIMM_PGROUP, dimmnode);
}
+
+ nerr += rank_create(mod, dimmnode, dimmarr[i]);
}
- if (nerr != 0)
- return (-1);
- else
- return (0);
+ return (nerr == 0 ? 0 : -1);
}
static nvlist_t *
@@ -481,6 +760,7 @@ mc_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
{
mc_snapshot_info_t mcs;
void *buf = NULL;
+ uint8_t ver;
nvlist_t *nvl;
char path[64];
@@ -490,7 +770,19 @@ mc_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
fd = open(path, O_RDONLY);
if (fd == -1) {
- topo_mod_dprintf(mod, "mc failed to open %s: %s\n",
+ /*
+ * Some v20z and v40z systems may have had the 3rd-party
+ * NWSnps packagae installed which installs a /dev/mc
+ * link. So try again via /devices.
+ */
+ (void) snprintf(path, sizeof (path),
+ "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd",
+ MC_AMD_DEV_OFFSET + id);
+ fd = open(path, O_RDONLY);
+ }
+
+ if (fd == -1) {
+ whinge(mod, NULL, "mc failed to open %s: %s\n",
path, strerror(errno));
return (NULL);
}
@@ -499,7 +791,7 @@ mc_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
(buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
- topo_mod_dprintf(mod, "mc failed to snapshot %s: %s\n",
+ whinge(mod, NULL, "mc failed to snapshot %s: %s\n",
path, strerror(errno));
free(buf);
@@ -510,6 +802,17 @@ mc_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
(void) close(fd);
err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
topo_mod_free(mod, buf, mcs.mcs_size);
+
+ if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) {
+ whinge(mod, NULL, "mc nvlist is not versioned\n");
+ nvlist_free(nvl);
+ return (NULL);
+ } else if (ver != MC_NVLIST_VERS1) {
+ whinge(mod, NULL, "mc nvlist version mismatch\n");
+ nvlist_free(nvl);
+ return (NULL);
+ }
+
return (err ? NULL : nvl);
}
@@ -521,26 +824,16 @@ mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name)
nvlist_t *fmri;
nvpair_t *nvp;
nvlist_t *mc = NULL;
- nvlist_t *pfmri, *args;
- topo_hdl_t *thp;
+ int i;
- thp = topo_mod_handle(mod);
- args = pfmri = NULL;
- if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
- topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
- nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
- nvlist_free(pfmri);
- nvlist_free(args);
+ if (mkrsrc(mod, pnode, name, 0, &fmri) != 0) {
+ whinge(mod, NULL, "mc_create: mkrsrc failed\n");
return (-1);
}
- fmri = topo_fmri_create(thp, FM_FMRI_SCHEME_HC, name, 0, args, &err);
- nvlist_free(pfmri);
- nvlist_free(args);
- if (fmri == NULL)
- return (-1);
if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) {
nvlist_free(fmri);
+ whinge(mod, NULL, "mc_create: node range create failed\n");
return (-1);
}
@@ -554,6 +847,7 @@ mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name)
nvlist_free(mc);
topo_node_range_destroy(pnode, name);
nvlist_free(fmri);
+ whinge(mod, NULL, "mc_create: mc lookup or bind failed\n");
return (-1);
}
@@ -563,21 +857,39 @@ mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name)
/*
* Add memory controller properties
*/
- (void) topo_pgroup_create(mcnode, MC_PGROUP,
+ (void) topo_pgroup_create(mcnode, MCT_PGROUP,
TOPO_STABILITY_PRIVATE, &err);
for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(mc, nvp)) {
- if (nvprop_add(nvp, MC_PGROUP, mcnode) == 0)
+ if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY &&
+ (strcmp(nvpair_name(nvp), "cslist") == 0 ||
+ strcmp(nvpair_name(nvp), "dimmlist") == 0)) {
continue;
- else if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY)
- break;
+ } else if (nvpair_type(nvp) == DATA_TYPE_UINT8 &&
+ strcmp(nvpair_name(nvp), MC_NVLIST_VERSTR) == 0) {
+ continue;
+ } else {
+ if (nvprop_add(mod, nvp, MCT_PGROUP, mcnode) != 0)
+ rc = -1;
+ }
}
- if (dimm_create(mod, mcnode, DIMM_NODE_NAME, mc) != 0 ||
- cs_create(mod, mcnode, CS_NODE_NAME, mc) != 0)
+ if (dramchan_create(mod, mcnode, CHAN_NODE_NAME) != 0 ||
+ cs_create(mod, mcnode, CS_NODE_NAME, mc) != 0 ||
+ dimm_create(mod, mcnode, DIMM_NODE_NAME, mc) != 0)
rc = -1;
+ /*
+ * Free the fmris for the chip-selects allocated in cs_create
+ */
+ for (i = 0; i < MC_CHIP_NCS; i++) {
+ if (cs_fmri[i] != NULL) {
+ nvlist_free(cs_fmri[i]);
+ cs_fmri[i] = NULL;
+ }
+ }
+
nvlist_free(mc);
return (rc);
}
@@ -590,12 +902,9 @@ chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
kstat_t *ksp;
ulong_t *chipmap;
tnode_t *cnode;
- nvlist_t *pfmri, *fmri, *args;
- topo_hdl_t *thp;
-
- thp = topo_mod_handle(mod);
+ nvlist_t *fmri;
- if ((chipmap = topo_mod_zalloc(mod, BT_BITOUL(chip->chip_ncpustats) *
+ if ((chipmap = topo_mod_zalloc(mod, BT_BITOUL(max) *
sizeof (ulong_t))) == NULL)
return (topo_mod_seterrno(mod, EMOD_NOMEM));
@@ -619,7 +928,8 @@ chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
continue;
if ((k = kstat_data_lookup(ksp, "chip_id")) == NULL) {
- ++nerr;
+ whinge(mod, &nerr, "chip_create: chip_id lookup "
+ "via kstats failed\n");
continue;
}
@@ -630,31 +940,19 @@ chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
if (chipid < min || chipid > max)
continue;
- args = pfmri = NULL;
- if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
- topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
- nvlist_add_nvlist(args,
- TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
- nvlist_free(pfmri);
- nvlist_free(args);
- ++nerr;
- continue;
- }
- fmri = topo_fmri_create(thp,
- FM_FMRI_SCHEME_HC, name, chipid, args, &err);
- nvlist_free(pfmri);
- nvlist_free(args);
- if (fmri == NULL) {
- ++nerr;
+ if (mkrsrc(mod, pnode, name, chipid, &fmri) != 0) {
+ whinge(mod, &nerr, "chip_create: mkrsrc failed\n");
continue;
}
if ((cnode = topo_node_bind(mod, pnode, name, chipid, fmri,
NULL)) == NULL) {
- ++nerr;
nvlist_free(fmri);
+ whinge(mod, &nerr, "chip_create: node bind "
+ "failed for chipid %d\n", chipid);
continue;
}
+ BT_SET(chipmap, chipid);
(void) topo_node_fru_set(cnode, fmri, 0, &err);
@@ -667,18 +965,19 @@ chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
(void) chip_longprop(cnode, ksp, CHIP_MODEL);
(void) chip_longprop(cnode, ksp, CHIP_STEPPING);
- if (mc_create(mod, cnode, MC_NODE_NAME) != 0 ||
- cpu_create(mod, cnode, CPU_NODE_NAME, chipid, chip) != 0)
- ++nerr;
+ if (cpu_create(mod, cnode, CPU_NODE_NAME, chipid, chip) != 0 ||
+ mc_create(mod, cnode, MCT_NODE_NAME) != 0)
+ nerr++; /* have whinged elsewhere */
}
- topo_mod_free(mod, chipmap, BT_BITOUL(chip->chip_ncpustats) *
- sizeof (ulong_t));
+ topo_mod_free(mod, chipmap, BT_BITOUL(max) * sizeof (ulong_t));
- if (nerr != 0)
+ if (nerr == 0) {
+ return (0);
+ } else {
(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
-
- return (0);
+ return (-1);
+ }
}
static int
diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/chip.h b/usr/src/lib/fm/topo/modules/i86pc/chip/chip.h
index 269dfa4f37..d1ff5ca3b4 100644
--- a/usr/src/lib/fm/topo/modules/i86pc/chip/chip.h
+++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -39,15 +38,19 @@ extern "C" {
#define CHIP_VERSION TOPO_VERSION
-#define MC_NODE_NAME "memory-controller"
+#define MCT_NODE_NAME "memory-controller"
+#define CHAN_NODE_NAME "dram-channel"
#define CPU_NODE_NAME "cpu"
#define CS_NODE_NAME "chip-select"
#define DIMM_NODE_NAME "dimm"
+#define RANK_NODE_NAME "rank"
#define CHIP_PGROUP "chip-properties"
-#define CS_PGROUP "chip-select-properties"
-#define MC_PGROUP "memory-contoller-properties"
-#define DIMM_PGROUP "dimm-properties"
+#define MCT_PGROUP MCT_NODE_NAME "-properties"
+#define CHAN_PGROUP CHAN_NODE_NAME "-properties"
+#define CS_PGROUP CS_NODE_NAME "-properties"
+#define DIMM_PGROUP DIMM_NODE_NAME "-properties"
+#define RANK_PGROUP RANK_NODE_NAME "-properties"
/*
* CHIP_PGROUP properties
diff --git a/usr/src/pkgdefs/SUNWfmd/prototype_com b/usr/src/pkgdefs/SUNWfmd/prototype_com
index c99e4aca59..4b91587a3a 100644
--- a/usr/src/pkgdefs/SUNWfmd/prototype_com
+++ b/usr/src/pkgdefs/SUNWfmd/prototype_com
@@ -119,6 +119,7 @@ f none usr/lib/locale/C/LC_MESSAGES/PCIEX.mo 444 root bin
d none usr/lib/mdb 755 root sys
d none usr/lib/mdb/proc 755 root sys
f none usr/lib/mdb/proc/fmd.so 555 root sys
+f none usr/lib/mdb/proc/eft.so 555 root sys
d none usr/sbin 755 root bin
f none usr/sbin/fmadm 555 root bin
f none usr/sbin/fmdump 555 root bin
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files
index fac330b5e7..4ce4e25847 100644
--- a/usr/src/uts/i86pc/Makefile.files
+++ b/usr/src/uts/i86pc/Makefile.files
@@ -142,7 +142,9 @@ include $(SRC)/common/mc/mc-amd/Makefile.mcamd
MCAMD_OBJS += \
$(MCAMD_CMN_OBJS) \
mcamd_drv.o \
- mcamd_subr.o
+ mcamd_dimmcfg.o \
+ mcamd_subr.o \
+ mcamd_pcicfg.o
#
# PCI-Express driver modules
diff --git a/usr/src/uts/i86pc/Makefile.workarounds b/usr/src/uts/i86pc/Makefile.workarounds
index 3fa2c63cc5..54e82c9abf 100644
--- a/usr/src/uts/i86pc/Makefile.workarounds
+++ b/usr/src/uts/i86pc/Makefile.workarounds
@@ -48,6 +48,11 @@ WORKAROUND_DEFS += -DOPTERON_ERRATUM_93
WORKAROUND_DEFS += -DOPTERON_ERRATUM_95
#
+# DRAM scrubber must not be enabled on a node with discontiguous cs range
+#
+WORKAROUND_DEFS += -DOPTERON_ERRATUM_99
+
+#
# Compatibility Mode Branches Transfer to Illegal Address
#
WORKAROUND_DEFS += -DOPTERON_ERRATUM_100
diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao.h b/usr/src/uts/i86pc/cpu/amd_opteron/ao.h
index 86953eeb1b..82e58bb638 100644
--- a/usr/src/uts/i86pc/cpu/amd_opteron/ao.h
+++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao.h
@@ -50,6 +50,7 @@ typedef struct ao_data ao_data_t;
typedef struct ao_bank_regs {
uint32_t abr_status;
uint32_t abr_addr;
+ uint32_t abr_misc;
} ao_bank_regs_t;
extern ao_bank_regs_t ao_bank_regs[AMD_MCA_BANK_COUNT];
@@ -139,6 +140,7 @@ typedef struct ao_mca_poll_trace {
typedef struct ao_bank_logout {
uint64_t abl_status; /* Saved MCi_STATUS register */
uint64_t abl_addr; /* Saved MCi_ADDR register */
+ uint64_t abl_misc; /* Saved MCi_MISC register */
} ao_bank_logout_t;
#define AO_ACL_F_PRIV 0x1 /* #mc in kernel mode (else user) */
@@ -164,11 +166,12 @@ typedef struct ao_cpu_logout {
/*
* We store config as inherited from the BIOS to assist in troubleshooting.
+ * The NorthBridge config is stored in the chipshared structure below.
*/
typedef struct ao_bios_cfg {
uint64_t bcfg_bank_ctl[AMD_MCA_BANK_COUNT];
uint64_t bcfg_bank_mask[AMD_MCA_BANK_COUNT];
- uint32_t bcfg_nb_cfg;
+ uint64_t bcfg_bank_misc[AMD_MCA_BANK_COUNT];
} ao_bios_cfg_t;
/*
@@ -184,13 +187,36 @@ typedef struct ao_mca {
} ao_mca_t;
/*
+ * Per-chip state
+ */
+struct ao_chipshared {
+ uint32_t aos_chiprev; /* Chip revision */
+ volatile ulong_t aos_cfgonce; /* Config performed once per chip */
+ kmutex_t aos_nb_poll_lock; /* Keep NB pollers from colliding */
+ uint64_t aos_nb_poll_timestamp; /* Timestamp of last NB poll */
+ int aos_nb_poll_owner; /* The cpuid of current NB poller */
+ uint64_t aos_bcfg_nb_ctl; /* BIOS value of MC4_CTL */
+ uint64_t aos_bcfg_nb_mask; /* BIOS value of MC4_MASK */
+ uint64_t aos_bcfg_nb_misc; /* BIOS value of MC4_MISC */
+ uint32_t aos_bcfg_nb_cfg; /* BIOS value of NB MCA Config */
+ uint32_t aos_bcfg_nb_sparectl; /* BIOS value of Online Spare Control */
+};
+
+/* Bit numbers for aos_cfgonce */
+enum ao_cfgonce_bitnum {
+ AO_CFGONCE_NBMCA
+};
+
+
+/*
* Per-CPU state
*/
struct ao_data {
- ao_mca_t ao_mca; /* MCA state for this CPU */
- cpu_t *ao_cpu; /* link to CPU's cpu_t */
- const cmi_mc_ops_t *ao_mc_ops; /* memory controller ops */
- void *ao_mc_data; /* argument for memory controller ops */
+ ao_mca_t ao_mca; /* MCA state for this CPU */
+ cpu_t *ao_cpu; /* link to CPU's cpu_t */
+ const cmi_mc_ops_t *ao_mc_ops; /* memory controller ops */
+ void *ao_mc_data; /* argument for MC ops */
+ struct ao_chipshared *ao_shared; /* Shared state for the chip */
};
#ifdef _KERNEL
@@ -202,17 +228,18 @@ extern const cmi_ops_t _cmi_ops;
extern void ao_faulted_enter(void *);
extern void ao_faulted_exit(void *);
-extern int ao_scrubber_enable(void *, uint64_t, uint64_t);
+extern int ao_scrubber_enable(void *, uint64_t, uint64_t, int);
extern void ao_mca_post_init(void *);
extern void ao_mca_init(void *);
extern int ao_mca_trap(void *, struct regs *);
extern int ao_mca_inject(void *, cmi_mca_regs_t *, uint_t);
extern void ao_mca_poke(void *);
-extern void ao_mca_poll_init(ao_mca_t *);
+extern void ao_mca_poll_init(ao_data_t *, int);
extern void ao_mca_poll_start(void);
-extern int ao_mca_logout(ao_cpu_logout_t *, struct regs *, int *);
+extern int ao_mca_logout(ao_cpu_logout_t *, struct regs *, int *, int,
+ uint32_t);
extern void ao_mca_drain(void *, const void *, const errorq_elem_t *);
extern nvlist_t *ao_fmri_create(ao_data_t *, nv_alloc_t *);
@@ -224,6 +251,8 @@ extern int ao_mc_unumtopa(ao_data_t *, mc_unum_t *, nvlist_t *, uint64_t *);
extern void ao_pcicfg_write(uint_t, uint_t, uint_t, uint32_t);
extern uint32_t ao_pcicfg_read(uint_t, uint_t, uint_t);
+extern int ao_chip_once(ao_data_t *, enum ao_cfgonce_bitnum);
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c
index e13c672fac..ccbc04fe4c 100644
--- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c
+++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -32,6 +31,7 @@
#include <sys/cmn_err.h>
#include <sys/sysmacros.h>
#include <sys/fm/protocol.h>
+#include <sys/x86_archext.h>
#include "ao.h"
@@ -60,7 +60,7 @@ uint32_t ao_scrub_lo; /* debug stash for system low addr */
uint32_t ao_scrub_hi; /* debug stash for system high addr */
enum {
- AO_SCRUB_DEFAULT, /* retain system default values */
+ AO_SCRUB_BIOSDEFAULT, /* retain system default values */
AO_SCRUB_FIXED, /* assign ao_scrub_rate_* values */
AO_SCRUB_MAX /* assign max of system and tunables */
} ao_scrub_policy = AO_SCRUB_MAX;
@@ -102,12 +102,19 @@ ao_scrubber_max(uint32_t r1, uint32_t cfg, uint32_t mask, uint32_t shift)
* The 'base' parameter is the DRAM Base Address for this chip and is used to
* determine where the scrubber starts. The 'ilen' value is the IntvlEn field
* from the DRAM configuration indicating the node-interleaving configuration.
+ *
+ * Where chip-select sparing is available the DRAM scrub address registers
+ * must not be modified while a swap is in-progress. This can't happen
+ * because we (the amd cpu module) take control of the online spare
+ * away from the BIOS when we perform NB configuration and we complete
+ * that operation before the memory controller driver loads.
*/
int
-ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen)
+ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen, int csdiscontig)
{
ao_data_t *ao = data;
chipid_t chipid = chip_plat_get_chipid(ao->ao_cpu);
+ uint32_t rev = cpuid_getchiprev(ao->ao_cpu);
uint32_t scrubctl, lo, hi;
int rv = 1;
@@ -119,7 +126,7 @@ ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen)
scrubctl = ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBCTL);
cas32(&ao_scrub_bios, 0, scrubctl);
- if (ao_scrub_policy == AO_SCRUB_DEFAULT)
+ if (ao_scrub_policy == AO_SCRUB_BIOSDEFAULT)
return ((scrubctl & AMD_NB_SCRUBCTL_DRAM_MASK) != 0);
scrubctl &= ~AMD_NB_SCRUBCTL_DRAM_MASK;
@@ -166,7 +173,17 @@ ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen)
ao_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_MAX;
}
- if (ao_scrub_policy == AO_SCRUB_MAX) {
+ switch (ao_scrub_policy) {
+ case AO_SCRUB_FIXED:
+ /* Use the system values checked above */
+ break;
+
+ default:
+ cmn_err(CE_WARN, "Unknown ao_scrub_policy value %d - "
+ "using default policy of AO_SCRUB_MAX", ao_scrub_policy);
+ /*FALLTHRU*/
+
+ case AO_SCRUB_MAX:
ao_scrub_rate_dcache =
ao_scrubber_max(ao_scrub_rate_dcache, ao_scrub_bios,
AMD_NB_SCRUBCTL_DC_MASK, AMD_NB_SCRUBCTL_DC_SHIFT);
@@ -178,17 +195,38 @@ ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen)
ao_scrub_rate_dram =
ao_scrubber_max(ao_scrub_rate_dram, ao_scrub_bios,
AMD_NB_SCRUBCTL_DRAM_MASK, AMD_NB_SCRUBCTL_DRAM_SHIFT);
+ break;
+ }
+
+#ifdef OPTERON_ERRATUM_99
+ /*
+ * This erratum applies on revisions D and earlier.
+ *
+ * Do not enable the dram scrubber is the chip-select ranges
+ * for the node are not contiguous.
+ */
+ if (csdiscontig && !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) {
+ cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision "
+ "%s chip because DRAM hole is present on this node",
+ cpuid_getchiprevstr(ao->ao_cpu));
+ ao_scrub_rate_dram = 0;
+ rv = 0;
}
+#endif
#ifdef OPTERON_ERRATUM_101
/*
+ * This erratum applies on revisions D and earlier.
+ *
* If the DRAM Base Address register's IntlvEn field indicates that
* node interleaving is enabled, we must disable the DRAM scrubber
* and return zero to indicate that Solaris should use s/w instead.
*/
- if (ilen != 0) {
- cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled because "
- "DRAM memory is node-interleaved");
+ if (ilen != 0 && ao_scrub_rate_dram != 0 &&
+ !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) {
+ cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision "
+ "%s chip because DRAM memory is node-interleaved",
+ cpuid_getchiprevstr(ao->ao_cpu));
ao_scrub_rate_dram = 0;
rv = 0;
}
diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c
index f07a18c063..06487043d9 100644
--- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c
+++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c
@@ -43,17 +43,20 @@
#include "ao.h"
+static struct ao_chipshared *ao_shared[CHIP_MAX_CHIPS];
+
/*
- * At present this CPU module only supports the features for Athlon64 and
- * Opteron up to and including the Rev E processor. If we detect Rev F or
- * later, return ENOTSUP and let the generic x86 CPU module load instead.
- * Opteron Rev F is currently defined as Family 0xF Model [0x40 .. 0x5F].
+ * This cpu module supports AMD family 0xf revisions B/C/D/E/F/G. If
+ * a family 0xf cpu beyond the rev G model limit is detected then
+ * return ENOTSUP and let the generic x86 CPU module load instead.
*/
-uint_t ao_model_limit = 0x40;
+uint_t ao_model_limit = 0x6f;
static int
ao_init(cpu_t *cp, void **datap)
{
+ uint_t chipid = chip_plat_get_chipid(CPU);
+ struct ao_chipshared *sp, *osp;
ao_data_t *ao;
uint64_t cap;
@@ -70,6 +73,22 @@ ao_init(cpu_t *cp, void **datap)
ao = *datap = kmem_zalloc(sizeof (ao_data_t), KM_SLEEP);
ao->ao_cpu = cp;
+ /*
+ * Allocate the chipshared structure if it appears not to have been
+ * allocated already (by a sibling core). Install the newly
+ * allocated pointer atomically in case a sibling core beats
+ * us to it.
+ */
+ if ((sp = ao_shared[chipid]) == NULL) {
+ sp = kmem_zalloc(sizeof (struct ao_chipshared), KM_SLEEP);
+ osp = atomic_cas_ptr(&ao_shared[chipid], NULL, sp);
+ if (osp != NULL) {
+ kmem_free(sp, sizeof (struct ao_chipshared));
+ sp = osp;
+ }
+ }
+ ao->ao_shared = sp;
+
return (0);
}
diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c
index df7b4d9c40..5b36685e9f 100644
--- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c
+++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c
@@ -44,6 +44,7 @@
#include <sys/mca_x86.h>
#include <sys/mca_amd.h>
#include <sys/mc.h>
+#include <sys/mc_amd.h>
#include <sys/psw.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
@@ -58,37 +59,57 @@
#include "ao.h"
#include "ao_mca_disp.h"
+#define AO_REVS_FG (X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G)
+
errorq_t *ao_mca_queue; /* machine-check ereport queue */
int ao_mca_stack_flag = 0; /* record stack trace in ereports */
int ao_mca_smi_disable = 1; /* attempt to disable SMI polling */
ao_bank_regs_t ao_bank_regs[AMD_MCA_BANK_COUNT] = {
- { AMD_MSR_DC_STATUS, AMD_MSR_DC_ADDR },
- { AMD_MSR_IC_STATUS, AMD_MSR_IC_ADDR },
- { AMD_MSR_BU_STATUS, AMD_MSR_BU_ADDR },
- { AMD_MSR_LS_STATUS, AMD_MSR_LS_ADDR },
- { AMD_MSR_NB_STATUS, AMD_MSR_NB_ADDR }
+ { AMD_MSR_DC_STATUS, AMD_MSR_DC_ADDR, AMD_MSR_DC_MISC },
+ { AMD_MSR_IC_STATUS, AMD_MSR_IC_ADDR, AMD_MSR_IC_MISC },
+ { AMD_MSR_BU_STATUS, AMD_MSR_BU_ADDR, AMD_MSR_BU_MISC },
+ { AMD_MSR_LS_STATUS, AMD_MSR_LS_ADDR, AMD_MSR_LS_MISC },
+ { AMD_MSR_NB_STATUS, AMD_MSR_NB_ADDR, AMD_MSR_NB_MISC }
+};
+
+struct ao_ctl_init {
+ uint32_t ctl_revmask; /* rev(s) to which this applies */
+ uint64_t ctl_bits; /* mca ctl reg bitmask to set */
+};
+
+/*
+ * Additional NB MCA ctl initialization for revs F and G
+ */
+static const struct ao_ctl_init ao_nb_ctl_init[] = {
+ { AO_REVS_FG, AMD_NB_CTL_INIT_REV_FG },
+ { X86_CHIPREV_UNKNOWN, 0 }
};
typedef struct ao_bank_cfg {
uint_t bank_ctl;
uint_t bank_ctl_mask;
- uint64_t bank_ctl_init;
+ uint64_t bank_ctl_init_cmn; /* Common init value */
+ const struct ao_ctl_init *bank_ctl_init_extra; /* Extra for each rev */
+ void (*bank_misc_initfunc)(ao_data_t *, uint32_t);
uint_t bank_status;
uint_t bank_addr;
} ao_bank_cfg_t;
+static void nb_mcamisc_init(ao_data_t *, uint32_t);
+
static const ao_bank_cfg_t ao_bank_cfgs[] = {
- { AMD_MSR_DC_CTL, AMD_MSR_DC_MASK, AMD_DC_CTL_INIT, AMD_MSR_DC_STATUS,
- AMD_MSR_DC_ADDR },
- { AMD_MSR_IC_CTL, AMD_MSR_IC_MASK, AMD_IC_CTL_INIT, AMD_MSR_IC_STATUS,
- AMD_MSR_IC_ADDR },
- { AMD_MSR_BU_CTL, AMD_MSR_BU_MASK, AMD_BU_CTL_INIT, AMD_MSR_BU_STATUS,
- AMD_MSR_BU_ADDR },
- { AMD_MSR_LS_CTL, AMD_MSR_LS_MASK, AMD_LS_CTL_INIT, AMD_MSR_LS_STATUS,
- AMD_MSR_LS_ADDR },
- { AMD_MSR_NB_CTL, AMD_MSR_NB_MASK, AMD_NB_CTL_INIT, AMD_MSR_NB_STATUS,
- AMD_MSR_NB_ADDR }
+ { AMD_MSR_DC_CTL, AMD_MSR_DC_MASK, AMD_DC_CTL_INIT_CMN,
+ NULL, NULL, AMD_MSR_DC_STATUS, AMD_MSR_DC_ADDR },
+ { AMD_MSR_IC_CTL, AMD_MSR_IC_MASK, AMD_IC_CTL_INIT_CMN,
+ NULL, NULL, AMD_MSR_IC_STATUS, AMD_MSR_IC_ADDR },
+ { AMD_MSR_BU_CTL, AMD_MSR_BU_MASK, AMD_BU_CTL_INIT_CMN,
+ NULL, NULL, AMD_MSR_BU_STATUS, AMD_MSR_BU_ADDR },
+ { AMD_MSR_LS_CTL, AMD_MSR_LS_MASK, AMD_LS_CTL_INIT_CMN,
+ NULL, NULL, AMD_MSR_LS_STATUS, AMD_MSR_LS_ADDR },
+ { AMD_MSR_NB_CTL, AMD_MSR_NB_MASK, AMD_NB_CTL_INIT_CMN,
+ &ao_nb_ctl_init[0], nb_mcamisc_init,
+ AMD_MSR_NB_STATUS, AMD_MSR_NB_ADDR }
};
static const ao_error_disp_t ao_disp_unknown = {
@@ -178,7 +199,8 @@ bit_strip(uint16_t *codep, uint16_t mask, uint16_t shift)
bit_strip(codep, AMD_ERRCODE_##name##_MASK, AMD_ERRCODE_##name##_SHIFT)
static int
-ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status)
+ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev,
+ int bankno)
{
uint16_t code = status & AMD_ERRCODE_MASK;
uint8_t extcode = (status & AMD_ERREXT_MASK) >> AMD_ERREXT_SHIFT;
@@ -191,8 +213,11 @@ ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status)
* injection has shown that multiple CE's overwriting each other shows
* AMD_BANK_STAT_CECC and AMD_BANK_STAT_UECC both set to zero. This
* should be clarified in a future BKDG or by the Revision Guide.
+ * This behaviour is fixed in revision F.
*/
- if (status & AMD_BANK_STAT_OVER) {
+ if (bankno == AMD_MCA_BANK_NB &&
+ !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) &&
+ status & AMD_BANK_STAT_OVER) {
stat_mask &= ~AMD_BANK_STAT_CECC;
stat_mask_res &= ~AMD_BANK_STAT_CECC;
}
@@ -227,12 +252,12 @@ ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status)
}
static const ao_error_disp_t *
-ao_disp_match(uint_t bankno, uint64_t status)
+ao_disp_match(uint_t bankno, uint64_t status, uint32_t rev)
{
const ao_error_disp_t *aed;
for (aed = ao_error_disp[bankno]; aed->aed_stat_mask != 0; aed++) {
- if (ao_disp_match_one(aed, status))
+ if (ao_disp_match_one(aed, status, rev, bankno))
return (aed);
}
@@ -260,91 +285,302 @@ ao_pcicfg_read(uint_t chipid, uint_t func, uint_t reg)
}
/*
+ * ao_chip_once returns 1 if the caller should perform the operation for
+ * this chip, or 0 if some other core has already performed the operation.
+ */
+
+int
+ao_chip_once(ao_data_t *ao, enum ao_cfgonce_bitnum what)
+{
+ return (atomic_set_long_excl(&ao->ao_shared->aos_cfgonce, what) == 0 ?
+ 1 : 0);
+}
+
+/*
* Setup individual bank detectors after stashing their bios settings.
+ * The 'donb' argument indicates whether this core should configured
+ * the shared NorthBridhe MSRs.
*/
static void
-ao_bank_cfg(ao_mca_t *mca)
+ao_bank_cfg(ao_data_t *ao, uint32_t rev, int donb)
{
- ao_bios_cfg_t *bioscfg = &mca->ao_mca_bios_cfg;
+ ao_mca_t *mca = &ao->ao_mca;
+ struct ao_chipshared *aos = ao->ao_shared;
+ ao_bios_cfg_t *bcfg = &mca->ao_mca_bios_cfg;
const ao_bank_cfg_t *bankcfg = ao_bank_cfgs;
+ const struct ao_ctl_init *extrap;
+ uint64_t mcictl;
int i;
for (i = 0; i < AMD_MCA_BANK_COUNT; i++, bankcfg++) {
- bioscfg->bcfg_bank_ctl[i] = rdmsr(bankcfg->bank_ctl);
- bioscfg->bcfg_bank_mask[i] = rdmsr(bankcfg->bank_ctl_mask);
- wrmsr(bankcfg->bank_ctl, bankcfg->bank_ctl_init);
+ if (i == AMD_MCA_BANK_NB && donb == 0) {
+ bcfg->bcfg_bank_ctl[i] = 0xbaddcafe;
+ bcfg->bcfg_bank_mask[i] = 0xbaddcafe;
+ continue;
+ } else if (i == AMD_MCA_BANK_NB) {
+ aos->aos_bcfg_nb_ctl = rdmsr(bankcfg->bank_ctl);
+ aos->aos_bcfg_nb_mask = rdmsr(bankcfg->bank_ctl_mask);
+ } else {
+ bcfg->bcfg_bank_ctl[i] = rdmsr(bankcfg->bank_ctl);
+ bcfg->bcfg_bank_mask[i] = rdmsr(bankcfg->bank_ctl_mask);
+ }
+
+ /* Initialize MCi_CTL register for this bank */
+ mcictl = bankcfg->bank_ctl_init_cmn;
+ if ((extrap = bankcfg->bank_ctl_init_extra) != NULL) {
+ while (extrap->ctl_revmask != X86_CHIPREV_UNKNOWN) {
+ if (X86_CHIPREV_MATCH(rev, extrap->ctl_revmask))
+ mcictl |= extrap->ctl_bits;
+ extrap++;
+ }
+ }
+ wrmsr(bankcfg->bank_ctl, mcictl);
+
+ /* Initialize the MCi_MISC register for this bank */
+ if (bankcfg->bank_misc_initfunc != NULL)
+ (bankcfg->bank_misc_initfunc)(ao, rev);
+ }
+}
+
+/*
+ * This knob exists in case any platform has a problem with our default
+ * policy of disabling any interrupt registered in the NB MC4_MISC
+ * register. Setting this may cause Solaris and external entities
+ * who also have an interest in this register to argue over available
+ * telemetry (so setting it is generally not recommended).
+ */
+int ao_nb_cfg_mc4misc_noseize = 0;
+
+/*
+ * The BIOS may have setup to receive SMI on counter overflow. It may also
+ * have locked various fields or made them read-only. We will clear any
+ * SMI request and leave the register locked. We will also clear the
+ * counter and enable counting - while we don't use the counter it is nice
+ * to have it enabled for verification and debug work.
+ */
+static void
+nb_mcamisc_init(ao_data_t *ao, uint32_t rev)
+{
+ uint64_t hwcr, oldhwcr;
+ uint64_t val;
+ int locked;
+
+ if (!X86_CHIPREV_MATCH(rev, AO_REVS_FG))
+ return;
+
+ ao->ao_shared->aos_bcfg_nb_misc = val = rdmsr(AMD_MSR_NB_MISC);
+
+ if (ao_nb_cfg_mc4misc_noseize)
+ return; /* stash BIOS value, but no changes */
+
+ locked = val & AMD_NB_MISC_LOCKED;
+
+ /*
+ * The Valid bit tells us whether the CtrP bit is defined; if it
+ * is the CtrP bit tells us whether an ErrCount field is present.
+ * If not then there is nothing for us to do.
+ */
+ if (!(val & AMD_NB_MISC_VALID) || !(val & AMD_NB_MISC_CTRP))
+ return;
+
+ if (locked) {
+ oldhwcr = rdmsr(MSR_AMD_HWCR);
+ hwcr = oldhwcr | AMD_HWCR_MCI_STATUS_WREN;
+ wrmsr(MSR_AMD_HWCR, hwcr);
}
+
+ val |= AMD_NB_MISC_CNTEN; /* enable ECC error counting */
+ val &= ~AMD_NB_MISC_ERRCOUNT_MASK; /* clear ErrCount */
+ val &= ~AMD_NB_MISC_OVRFLW; /* clear Ovrflw */
+ val &= ~AMD_NB_MISC_INTTYPE_MASK; /* no interrupt on overflow */
+ val |= AMD_NB_MISC_LOCKED;
+
+ wrmsr(AMD_MSR_NB_MISC, val);
+
+ if (locked)
+ wrmsr(MSR_AMD_HWCR, oldhwcr);
}
/*
- * Bits to be added to the NorthBridge (NB) configuration register.
- * See BKDG 3.29 Section 3.6.4.2 for more information.
+ * NorthBridge (NB) Configuration.
+ *
+ * We add and remove bits from the BIOS-configured value, rather than
+ * writing an absolute value. The variables ao_nb_cfg_{add,remove}_cmn and
+ * ap_nb_cfg_{add,remove}_revFG are available for modification via kmdb
+ * and /etc/system. The revision-specific adds and removes are applied
+ * after the common changes, and one write is made to the config register.
+ * These are not intended for watchdog configuration via these variables -
+ * use the watchdog policy below.
+ */
+
+/*
+ * Bits to be added to the NB configuration register - all revs.
+ */
+uint32_t ao_nb_cfg_add_cmn = AMD_NB_CFG_ADD_CMN;
+
+/*
+ * Bits to be cleared from the NB configuration register - all revs.
+ */
+uint32_t ao_nb_cfg_remove_cmn = AMD_NB_CFG_REMOVE_CMN;
+
+/*
+ * Bits to be added to the NB configuration register - revs F and G.
*/
-uint32_t ao_nb_cfg_add =
- AMD_NB_CFG_NBMCATOMSTCPUEN |
- AMD_NB_CFG_DISPCICFGCPUERRRSP |
- AMD_NB_CFG_SYNCONUCECCEN |
- AMD_NB_CFG_CPUECCERREN;
+uint32_t ao_nb_cfg_add_revFG = AMD_NB_CFG_ADD_REV_FG;
/*
- * Bits to be cleared from the NorthBridge (NB) configuration register.
- * See BKDG 3.29 Section 3.6.4.2 for more information.
+ * Bits to be cleared from the NB configuration register - revs F and G.
*/
-uint32_t ao_nb_cfg_remove =
- AMD_NB_CFG_IORDDATERREN |
- AMD_NB_CFG_SYNCONANYERREN |
- AMD_NB_CFG_SYNCONWDOGEN |
- AMD_NB_CFG_IOERRDIS |
- AMD_NB_CFG_IOMSTABORTDIS |
- AMD_NB_CFG_SYNCPKTPROPDIS |
- AMD_NB_CFG_SYNCPKTGENDIS;
+uint32_t ao_nb_cfg_remove_revFG = AMD_NB_CFG_REMOVE_REV_FG;
+
+struct ao_nb_cfg {
+ uint32_t cfg_revmask;
+ uint32_t *cfg_add_p;
+ uint32_t *cfg_remove_p;
+};
+
+static const struct ao_nb_cfg ao_cfg_extra[] = {
+ { AO_REVS_FG, &ao_nb_cfg_add_revFG, &ao_nb_cfg_remove_revFG },
+ { X86_CHIPREV_UNKNOWN, NULL, NULL }
+};
/*
* Bits to be used if we configure the NorthBridge (NB) Watchdog. The watchdog
* triggers a machine check exception when no response to an NB system access
- * occurs within a specified time interval. If the BIOS (i.e. platform design)
- * has enabled the watchdog, we leave its rate alone. If the BIOS has not
- * enabled the watchdog, we enable it and set the rate to one specified below.
- * To disable the watchdog, add the AMD_NB_CFG_WDOGTMRDIS bit to ao_nb_cfg_add.
+ * occurs within a specified time interval.
*/
uint32_t ao_nb_cfg_wdog =
AMD_NB_CFG_WDOGTMRCNTSEL_4095 |
AMD_NB_CFG_WDOGTMRBASESEL_1MS;
+/*
+ * The default watchdog policy is to enable it (at the above rate) if it
+ * is disabled; if it is enabled then we leave it enabled at the rate
+ * chosen by the BIOS.
+ */
+enum {
+ AO_NB_WDOG_LEAVEALONE, /* Don't touch watchdog config */
+ AO_NB_WDOG_DISABLE, /* Always disable watchdog */
+ AO_NB_WDOG_ENABLE_IF_DISABLED, /* If disabled, enable at our rate */
+ AO_NB_WDOG_ENABLE_FORCE_RATE /* Enable and set our rate */
+} ao_nb_watchdog_policy = AO_NB_WDOG_ENABLE_IF_DISABLED;
+
static void
-ao_nb_cfg(ao_mca_t *mca)
+ao_nb_cfg(ao_data_t *ao, uint32_t rev)
{
+ const struct ao_nb_cfg *nbcp = &ao_cfg_extra[0];
uint_t chipid = chip_plat_get_chipid(CPU);
uint32_t val;
- if (chip_plat_get_clogid(CPU) != 0)
- return; /* only configure NB once per CPU */
-
/*
* Read the NorthBridge (NB) configuration register in PCI space,
* modify the settings accordingly, and store the new value back.
*/
- mca->ao_mca_bios_cfg.bcfg_nb_cfg = val =
+ ao->ao_shared->aos_bcfg_nb_cfg = val =
ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_CFG);
- /*
- * If the watchdog was disabled, enable it according to the policy
- * described above. Then apply the ao_nb_cfg_[add|remove] masks.
- */
- if (val & AMD_NB_CFG_WDOGTMRDIS) {
+ switch (ao_nb_watchdog_policy) {
+ case AO_NB_WDOG_LEAVEALONE:
+ break;
+
+ case AO_NB_WDOG_DISABLE:
+ val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
+ val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
+ val |= AMD_NB_CFG_WDOGTMRDIS;
+ break;
+
+ default:
+ cmn_err(CE_NOTE, "ao_nb_watchdog_policy=%d unrecognised, "
+ "using default policy", ao_nb_watchdog_policy);
+ /*FALLTHRU*/
+
+ case AO_NB_WDOG_ENABLE_IF_DISABLED:
+ if (val & AMD_NB_CFG_WDOGTMRDIS)
+ break; /* if enabled leave rate intact */
+ /*FALLTHRU*/
+
+ case AO_NB_WDOG_ENABLE_FORCE_RATE:
val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
val &= ~AMD_NB_CFG_WDOGTMRDIS;
val |= ao_nb_cfg_wdog;
+ break;
}
- val &= ~ao_nb_cfg_remove;
- val |= ao_nb_cfg_add;
+ /*
+ * Now apply bit adds and removes, first those common to all revs
+ * and then the revision-specific ones.
+ */
+ val &= ~ao_nb_cfg_remove_cmn;
+ val |= ao_nb_cfg_add_cmn;
+
+ while (nbcp->cfg_revmask != X86_CHIPREV_UNKNOWN) {
+ if (X86_CHIPREV_MATCH(rev, nbcp->cfg_revmask)) {
+ val &= ~(*nbcp->cfg_remove_p);
+ val |= *nbcp->cfg_add_p;
+ }
+ nbcp++;
+ }
ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_CFG, val);
}
/*
+ * This knob exists in case any platform has a problem with our default
+ * policy of disabling any interrupt registered in the online spare
+ * control register. Setting this may cause Solaris and external entities
+ * who also have an interest in this register to argue over available
+ * telemetry (so setting it is generally not recommended).
+ */
+int ao_nb_cfg_sparectl_noseize = 0;
+
+/*
+ * Setup the online spare control register (revs F and G). We disable
+ * any interrupt registered by the BIOS and zero all error counts.
+ */
+static void
+ao_sparectl_cfg(ao_data_t *ao)
+{
+ uint_t chipid = chip_plat_get_chipid(CPU);
+ union mcreg_sparectl sparectl;
+ int chan, cs;
+
+ ao->ao_shared->aos_bcfg_nb_sparectl = MCREG_VAL32(&sparectl) =
+ ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_SPARECTL);
+
+ if (ao_nb_cfg_sparectl_noseize)
+ return; /* stash BIOS value, but no changes */
+
+ /*
+ * If the BIOS has requested SMI interrupt type for ECC count
+ * overflow for a chip-select or channel force those off.
+ */
+ MCREG_FIELD_revFG(&sparectl, EccErrInt) = 0;
+ MCREG_FIELD_revFG(&sparectl, SwapDoneInt) = 0;
+
+ /* Enable writing to the EccErrCnt field */
+ MCREG_FIELD_revFG(&sparectl, EccErrCntWrEn) = 1;
+
+ /* First write, preparing for writes to EccErrCnt */
+ ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_SPARECTL,
+ MCREG_VAL32(&sparectl));
+
+ /*
+ * Zero EccErrCnt and write this back to all chan/cs combinations.
+ */
+ MCREG_FIELD_revFG(&sparectl, EccErrCnt) = 0;
+ for (chan = 0; chan < MC_CHIP_NDRAMCHAN; chan++) {
+ MCREG_FIELD_revFG(&sparectl, EccErrCntDramChan) = chan;
+
+ for (cs = 0; cs < MC_CHIP_NCS; cs++) {
+ MCREG_FIELD_revFG(&sparectl, EccErrCntDramCs) = cs;
+ ao_pcicfg_write(chipid, AMD_NB_FUNC,
+ AMD_NB_REG_SPARECTL, MCREG_VAL32(&sparectl));
+ }
+ }
+}
+
+/*
* Capture the machine-check exception state into our per-CPU logout area, and
* dispatch a copy of the logout area to our error queue for ereport creation.
* If 'rp' is non-NULL, we're being called from trap context; otherwise we're
@@ -353,25 +589,40 @@ ao_nb_cfg(ao_mca_t *mca)
* The caller is expected to call fm_panic() if we return fatal (non-zero).
*/
int
-ao_mca_logout(ao_cpu_logout_t *acl, struct regs *rp, int *np)
+ao_mca_logout(ao_cpu_logout_t *acl, struct regs *rp, int *np, int skipnb,
+ uint32_t rev)
{
+ uint64_t mcg_status = rdmsr(IA32_MSR_MCG_STATUS);
int i, fatal = 0, n = 0;
acl->acl_timestamp = gethrtime_waitfree();
- acl->acl_mcg_status = rdmsr(IA32_MSR_MCG_STATUS);
+ acl->acl_mcg_status = mcg_status;
acl->acl_ip = rp ? rp->r_pc : 0;
acl->acl_flags = 0;
/*
* Iterate over the banks of machine-check registers, read the address
- * and status registers into the logout area, and clear them as we go.
+ * and status registers into the logout area, and clear status as we go.
+ * Also read the MCi_MISC register is MCi_STATUS.MISCV indicates that
+ * there is valid info there (as it will in revisions F and G for
+ * NorthBridge ECC errors).
*/
for (i = 0; i < AMD_MCA_BANK_COUNT; i++) {
ao_bank_logout_t *abl = &acl->acl_banks[i];
+ if (i == AMD_MCA_BANK_NB && skipnb) {
+ abl->abl_status = 0;
+ continue;
+ }
+
abl->abl_addr = rdmsr(ao_bank_regs[i].abr_addr);
abl->abl_status = rdmsr(ao_bank_regs[i].abr_status);
+ if (abl->abl_status & AMD_BANK_STAT_MISCV)
+ abl->abl_misc = rdmsr(ao_bank_regs[i].abr_misc);
+ else
+ abl->abl_misc = 0;
+
if (abl->abl_status & AMD_BANK_STAT_VALID)
wrmsr(ao_bank_regs[i].abr_status, 0);
}
@@ -386,9 +637,11 @@ ao_mca_logout(ao_cpu_logout_t *acl, struct regs *rp, int *np)
/*
* Clear MCG_STATUS, indicating that machine-check trap processing is
- * complete. Once we do this, another machine-check trap can occur.
+ * complete. Once we do this, another machine-check trap can occur
+ * (if another occurs up to this point then the system will reset).
*/
- wrmsr(IA32_MSR_MCG_STATUS, 0);
+ if (mcg_status & MCG_STATUS_MCIP)
+ wrmsr(IA32_MSR_MCG_STATUS, 0);
/*
* If we took a machine-check trap, then the error is fatal if the
@@ -410,7 +663,7 @@ ao_mca_logout(ao_cpu_logout_t *acl, struct regs *rp, int *np)
if (!(abl->abl_status & AMD_BANK_STAT_VALID))
continue;
- aed = ao_disp_match(i, abl->abl_status);
+ aed = ao_disp_match(i, abl->abl_status, rev);
if ((when = aed->aed_panic_when) != AO_AED_PANIC_NEVER) {
if ((when & AO_AED_PANIC_ALWAYS) ||
((when & AO_AED_PANIC_IFMCE) && rp != NULL)) {
@@ -419,16 +672,39 @@ ao_mca_logout(ao_cpu_logout_t *acl, struct regs *rp, int *np)
}
/*
- * If we are taking a machine-check exception and the overflow
- * bit is set or our context is corrupt, then we must die.
- * NOTE: This code assumes that if the overflow bit is set and
- * we didn't take a #mc exception (i.e. the poller found it),
- * then multiple correctable errors overwrote each other.
- * This will need to change if we eventually use the Opteron
- * Rev E exception mechanism for detecting correctable errors.
+ * If we are taking a machine-check exception and our context
+ * is corrupt, then we must die.
*/
- if (rp != NULL && (abl->abl_status &
- (AMD_BANK_STAT_OVER | AMD_BANK_STAT_PCC)))
+ if (rp != NULL && abl->abl_status & AMD_BANK_STAT_PCC)
+ fatal++;
+
+ /*
+ * The overflow bit is set if the bank detects an error but
+ * the valid bit of its status register is already set
+ * (software has not yet read and cleared it). Enabled
+ * (for mc# reporting) errors overwrite disabled errors,
+ * uncorrectable errors overwrite correctable errors,
+ * uncorrectable errors are not overwritten.
+ *
+ * For the NB detector bank the overflow bit will not be
+ * set for repeated correctable errors on revisions D and
+ * earlier; it will be set on revisions E and later.
+ * On revision E, however, the CorrECC bit does appear
+ * to clear in these circumstances. Since we can enable
+ * machine-check exception on NB correctables we need to
+ * be careful here; we never enable mc# for correctable from
+ * other banks.
+ *
+ * Our solution will be to declare a machine-check exception
+ * fatal if the overflow bit is set except in the case of
+ * revision F on the NB detector bank for which CorrECC
+ * is indicated. Machine-check exception for NB correctables
+ * on rev E is explicitly not supported.
+ */
+ if (rp != NULL && abl->abl_status & AMD_BANK_STAT_OVER &&
+ !(i == AMD_MCA_BANK_NB &&
+ X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) &&
+ abl->abl_status & AMD_BANK_STAT_CECC))
fatal++;
/*
@@ -456,12 +732,11 @@ ao_mca_logout(ao_cpu_logout_t *acl, struct regs *rp, int *np)
}
static uint_t
-ao_ereport_synd(ao_mca_t *mca,
- const ao_bank_logout_t *abl, uint_t *typep, int is_nb)
+ao_ereport_synd(ao_data_t *ao, const ao_bank_logout_t *abl, uint_t *typep,
+ int is_nb)
{
if (is_nb) {
- if ((mca->ao_mca_bios_cfg.bcfg_nb_cfg &
- AMD_NB_CFG_CHIPKILLECCEN) != 0) {
+ if (ao->ao_shared->aos_bcfg_nb_cfg & AMD_NB_CFG_CHIPKILLECCEN) {
*typep = AMD_SYNDTYPE_CHIPKILL;
return (AMD_NB_STAT_CKSYND(abl->abl_status));
} else {
@@ -486,11 +761,12 @@ ao_ereport_create_resource_elem(nvlist_t **nvlp, nv_alloc_t *nva,
(void) nvlist_add_uint64(snvl, FM_FMRI_HC_SPECIFIC_OFFSET,
unump->unum_offset);
- fm_fmri_hc_set(*nvlp, FM_HC_SCHEME_VERSION, NULL, snvl, 4,
+ fm_fmri_hc_set(*nvlp, FM_HC_SCHEME_VERSION, NULL, snvl, 5,
"motherboard", unump->unum_board,
"chip", unump->unum_chip,
"memory-controller", unump->unum_mc,
- "dimm", unump->unum_dimms[dimmnum]);
+ "dimm", unump->unum_dimms[dimmnum],
+ "rank", unump->unum_rank);
fm_nvlist_destroy(snvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
}
@@ -504,7 +780,7 @@ ao_ereport_add_resource(nvlist_t *payload, nv_alloc_t *nva, mc_unum_t *unump)
int i;
for (i = 0; i < MC_UNUM_NDIMM; i++) {
- if (unump->unum_dimms[i] == -1)
+ if (unump->unum_dimms[i] == MC_INVALNUM)
break;
ao_ereport_create_resource_elem(&elems[nelems++], nva,
unump, i);
@@ -522,11 +798,10 @@ ao_ereport_add_logout(ao_data_t *ao, nvlist_t *payload, nv_alloc_t *nva,
const ao_cpu_logout_t *acl, uint_t bankno, const ao_error_disp_t *aed)
{
uint64_t members = aed->aed_ereport_members;
- ao_mca_t *mca = &ao->ao_mca;
const ao_bank_logout_t *abl = &acl->acl_banks[bankno];
uint_t synd, syndtype;
- synd = ao_ereport_synd(mca, abl, &syndtype, bankno == AMD_MCA_BANK_NB);
+ synd = ao_ereport_synd(ao, abl, &syndtype, bankno == AMD_MCA_BANK_NB);
if (members & FM_EREPORT_PAYLOAD_FLAG_BANK_STAT) {
fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BANK_STAT,
@@ -549,6 +824,11 @@ ao_ereport_add_logout(ao_data_t *ao, nvlist_t *payload, nv_alloc_t *nva,
AMD_BANK_STAT_ADDRV) ? B_TRUE : B_FALSE, NULL);
}
+ if (members & FM_EREPORT_PAYLOAD_FLAG_BANK_MISC) {
+ fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BANK_MISC,
+ DATA_TYPE_UINT64, abl->abl_misc, NULL);
+ }
+
if (members & FM_EREPORT_PAYLOAD_FLAG_SYND) {
fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SYND,
DATA_TYPE_UINT16, synd, NULL);
@@ -626,7 +906,7 @@ ao_ereport_post(const ao_cpu_logout_t *acl,
}
/*
- * Create the scheme "cpu" FMRI
+ * Create the "hc" scheme detector FMRI identifying this cpu
*/
detector = ao_fmri_create(ao, nva);
@@ -670,6 +950,7 @@ void
ao_mca_drain(void *ignored, const void *data, const errorq_elem_t *eqe)
{
const ao_cpu_logout_t *acl = data;
+ uint32_t rev = acl->acl_ao->ao_shared->aos_chiprev;
int i;
for (i = 0; i < AMD_MCA_BANK_COUNT; i++) {
@@ -677,19 +958,63 @@ ao_mca_drain(void *ignored, const void *data, const errorq_elem_t *eqe)
const ao_error_disp_t *aed;
if (abl->abl_status & AMD_BANK_STAT_VALID) {
- aed = ao_disp_match(i, abl->abl_status);
+ aed = ao_disp_match(i, abl->abl_status, rev);
ao_ereport_post(acl, i, aed);
}
}
}
+/*
+ * Machine check interrupt handler - we jump here from mcetrap.
+ *
+ * A sibling core may attempt to poll the NorthBridge during the
+ * time we are performing the logout. So we coordinate NB access
+ * of all cores of the same chip via a per-chip lock. If the lock
+ * is held on a sibling core then we spin for it here; if the
+ * lock is held by the thread we have interrupted then we do
+ * not acquire the lock but can proceed safe in the knowledge that
+ * the lock owner can't actually perform any NB accesses. This
+ * requires that threads that take the aos_nb_poll_lock do not
+ * block and that they disable preemption while they hold the lock.
+ * It also requires that the lock be adaptive since mutex_owner does
+ * not work for spin locks.
+ */
+static int ao_mca_path1, ao_mca_path2;
int
ao_mca_trap(void *data, struct regs *rp)
{
ao_data_t *ao = data;
ao_mca_t *mca = &ao->ao_mca;
ao_cpu_logout_t *acl = &mca->ao_mca_logout[AO_MCA_LOGOUT_EXCEPTION];
- return (ao_mca_logout(acl, rp, NULL));
+ kmutex_t *nblock = NULL;
+ int tooklock = 0;
+ int rv;
+
+ if (ao->ao_shared != NULL)
+ nblock = &ao->ao_shared->aos_nb_poll_lock;
+
+ if (nblock && !mutex_owned(nblock)) {
+ /*
+ * The mutex is not owned by the thread we have interrupted
+ * (since the holder may not block or be preempted once the
+ * lock is acquired). We will spin for this adaptive lock.
+ */
+ ++ao_mca_path1;
+ while (!mutex_tryenter(nblock)) {
+ while (mutex_owner(nblock) != NULL)
+ ;
+ }
+ tooklock = 1;
+ } else {
+ ++ao_mca_path2;
+ }
+
+ rv = ao_mca_logout(acl, rp, NULL, 0, ao->ao_shared->aos_chiprev);
+
+ if (tooklock)
+ mutex_exit(&ao->ao_shared->aos_nb_poll_lock);
+
+ return (rv);
}
/*ARGSUSED*/
@@ -716,10 +1041,10 @@ ao_mca_init(void *data)
ao_data_t *ao = data;
ao_mca_t *mca = &ao->ao_mca;
uint64_t cap;
+ uint32_t rev;
+ int donb;
int i;
- ao_mca_poll_init(mca);
-
ASSERT(x86_feature & X86_MCA);
cap = rdmsr(IA32_MSR_MCG_CAP);
ASSERT(cap & MCG_CAP_CTL_P);
@@ -742,9 +1067,50 @@ ao_mca_init(void *data)
for (i = 0; i < AO_MCA_LOGOUT_NUM; i++)
mca->ao_mca_logout[i].acl_ao = ao;
- ao_bank_cfg(mca);
- ao_nb_cfg(mca);
+ /* LINTED: logical expression always true */
+ ASSERT(sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfg_t) ==
+ AMD_MCA_BANK_COUNT);
+
+ rev = ao->ao_shared->aos_chiprev = cpuid_getchiprev(ao->ao_cpu);
+ /*
+ * Must this core perform NB MCA configuration? This must be done
+ * by just one core.
+ */
+ donb = ao_chip_once(ao, AO_CFGONCE_NBMCA);
+
+ /*
+ * Initialize poller data, but don't start polling yet.
+ */
+ ao_mca_poll_init(ao, donb);
+
+ /*
+ * Configure the bank MCi_CTL register to nominate which error
+ * types for each bank will produce a machine-check (we'll poll
+ * for others). Correctable error types mentioned in these MCi_CTL
+ * settings won't actually produce an exception unless an additional
+ * (and undocumented) bit is set elsewhere - the poller must still
+ * handle these.
+ */
+ ao_bank_cfg(ao, rev, donb);
+
+ /*
+ * Modify the MCA NB Configuration Register.
+ */
+ if (donb)
+ ao_nb_cfg(ao, rev);
+
+ /*
+ * Setup the Online Spare Control Register
+ */
+ if (donb && X86_CHIPREV_MATCH(rev, AO_REVS_FG)) {
+ ao_sparectl_cfg(ao);
+ }
+
+ /*
+ * Enable all error reporting banks (icache, dcache, ...). This
+ * enables error detection, as opposed to error reporting above.
+ */
wrmsr(IA32_MSR_MCG_CTL, AMD_MCG_EN_ALL);
/*
@@ -753,8 +1119,12 @@ ao_mca_init(void *data)
* to be updated. If we interpret this state as an actual error, we
* may end up indicting something that's not actually broken.
*/
- for (i = 0; i < sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfg_t); i++)
+ for (i = 0; i < AMD_MCA_BANK_COUNT; i++) {
+ if (!donb)
+ continue;
+
wrmsr(ao_bank_cfgs[i].bank_status, 0ULL);
+ }
wrmsr(IA32_MSR_MCG_STATUS, 0ULL);
membar_producer();
diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in
index 371efbdb37..281f14301e 100644
--- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in
+++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in
@@ -775,3 +775,18 @@ code = 0111 bus gen 1 gen gen lg -
panic = always
flags =
+
+# ---
+
+desc = DRAM Address Parity Error
+error = ereport.cpu.amd.nb.dramaddr_par
+
+mask on = AMD_BANK_STAT_UC
+mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC
+
+# ext type pp t rrrr ii ll tt
+# ------- ------- ------- ------- --------------- ------- ------- -----
+code = 1101 bus obs 0 gen mem lg -
+
+panic = always
+flags =
diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c
index ce15c846d2..6fb4b8a5bd 100644
--- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c
+++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c
@@ -55,8 +55,8 @@ static uint_t ao_mca_poll_trace_always = 1;
static uint_t ao_mca_poll_trace_always = 0;
#endif
-static cyclic_id_t ao_mca_poll_cycid;
-static hrtime_t ao_mca_poll_interval = NANOSEC * 10ULL;
+cyclic_id_t ao_mca_poll_cycid;
+hrtime_t ao_mca_poll_interval = NANOSEC * 10ULL;
static void
ao_mca_poll_trace(ao_mca_t *mca, uint32_t what, uint32_t nerr)
@@ -79,13 +79,20 @@ ao_mca_poll_trace(ao_mca_t *mca, uint32_t what, uint32_t nerr)
if (what == AO_MPT_WHAT_CYC_ERR)
pt->mpt_nerr = MIN(nerr, UINT8_MAX);
- pt->mpt_when = gethrtime();
+ pt->mpt_when = gethrtime_waitfree();
mca->ao_mca_poll_curtrace = next;
}
+/*
+ * Once aos_nb_poll_lock is acquired the caller must not block. The
+ * ao_mca_trap code also requires that once we take the aos_nb_poll_lock
+ * that we do not get preempted so that it can check whether the
+ * thread it has interrupted is the lock owner.
+ */
static void
-ao_mca_poll_common(ao_mca_t *mca, int what)
+ao_mca_poll_common(ao_data_t *ao, int what, int pollnb)
{
+ ao_mca_t *mca = &ao->ao_mca;
ao_cpu_logout_t *acl = &mca->ao_mca_logout[AO_MCA_LOGOUT_POLLER];
int i, n, fatal;
@@ -104,32 +111,88 @@ ao_mca_poll_common(ao_mca_t *mca, int what)
}
}
- fatal = ao_mca_logout(acl, NULL, &n);
+ fatal = ao_mca_logout(acl, NULL, &n, !pollnb,
+ ao->ao_shared->aos_chiprev);
ao_mca_poll_trace(mca, what, n);
if (fatal && cmi_panic_on_uncorrectable_error)
fm_panic("Unrecoverable Machine-Check Error (polled)");
}
+/*
+ * Decide whether the caller should poll the NB. The decision is made
+ * and any poll is performed under protection of the chip-wide aos_nb_poll_lock,
+ * so that assures that no two cores poll the NB at once. To avoid the
+ * NB poll ping-ponging between different detectors we'll normally stick
+ * with the first winner.
+ */
+static int
+ao_mca_nb_pollowner(ao_data_t *ao)
+{
+ uint64_t last = ao->ao_shared->aos_nb_poll_timestamp;
+ uint64_t now = gethrtime_waitfree();
+ int rv = 0;
+
+ ASSERT(MUTEX_HELD(&ao->ao_shared->aos_nb_poll_lock));
+
+ if (now - last > 2 * ao_mca_poll_interval || last == 0) {
+ /* Nominal owner making little progress - we'll take over */
+ ao->ao_shared->aos_nb_poll_owner = CPU->cpu_id;
+ rv = 1;
+ } else if (CPU->cpu_id == ao->ao_shared->aos_nb_poll_owner) {
+ rv = 1;
+ }
+
+ if (rv == 1)
+ ao->ao_shared->aos_nb_poll_timestamp = now;
+
+ return (rv);
+}
+
+/*
+ * Wrapper called from cyclic handler or from an injector poke.
+ * In the former case we are a CYC_LOW_LEVEL handler while in the
+ * latter we're in user context so in both cases we are allowed
+ * to block. Once we acquire the shared and adaptive aos_nb_poll_lock, however,
+ * we must not block or be preempted (see ao_mca_trap).
+ */
static void
-ao_mca_poll_cyclic(void *arg)
+ao_mca_poll_wrapper(void *arg, int what)
{
ao_data_t *ao = arg;
+ int pollnb;
+
+ if (ao == NULL)
+ return;
+
+ mutex_enter(&ao->ao_mca.ao_mca_poll_lock);
+ kpreempt_disable();
+ mutex_enter(&ao->ao_shared->aos_nb_poll_lock);
+
+ if ((pollnb = ao_mca_nb_pollowner(ao)) == 0) {
+ mutex_exit(&ao->ao_shared->aos_nb_poll_lock);
+ kpreempt_enable();
+ }
+
+ ao_mca_poll_common(ao, what, pollnb);
- if (ao != NULL && mutex_tryenter(&ao->ao_mca.ao_mca_poll_lock)) {
- ao_mca_poll_common(&ao->ao_mca, AO_MPT_WHAT_CYC_ERR);
- mutex_exit(&ao->ao_mca.ao_mca_poll_lock);
+ if (pollnb) {
+ mutex_exit(&ao->ao_shared->aos_nb_poll_lock);
+ kpreempt_enable();
}
+ mutex_exit(&ao->ao_mca.ao_mca_poll_lock);
+}
+
+static void
+ao_mca_poll_cyclic(void *arg)
+{
+ ao_mca_poll_wrapper(arg, AO_MPT_WHAT_CYC_ERR);
}
void
ao_mca_poke(void *arg)
{
- ao_data_t *ao = arg;
-
- mutex_enter(&ao->ao_mca.ao_mca_poll_lock);
- ao_mca_poll_common(&ao->ao_mca, AO_MPT_WHAT_POKE_ERR);
- mutex_exit(&ao->ao_mca.ao_mca_poll_lock);
+ ao_mca_poll_wrapper(arg, AO_MPT_WHAT_POKE_ERR);
}
/*ARGSUSED*/
@@ -159,14 +222,26 @@ ao_mca_poll_online(void *arg, cpu_t *cpu, cyc_handler_t *cyh, cyc_time_t *cyt)
static void
ao_mca_poll_offline(void *arg, cpu_t *cpu, void *cyh_arg)
{
- /* nothing to do here */
+ ao_data_t *ao = cpu->cpu_m.mcpu_cmidata;
+
+ /*
+ * Any sibling core may begin to poll NB MCA registers
+ */
+ if (cpu->cpu_id == ao->ao_shared->aos_nb_poll_owner)
+ ao->ao_shared->aos_nb_poll_timestamp = 0;
}
void
-ao_mca_poll_init(ao_mca_t *mca)
+ao_mca_poll_init(ao_data_t *ao, int donb)
{
+ ao_mca_t *mca = &ao->ao_mca;
+
mutex_init(&mca->ao_mca_poll_lock, NULL, MUTEX_DRIVER, NULL);
+ if (donb)
+ mutex_init(&ao->ao_shared->aos_nb_poll_lock, NULL, MUTEX_DRIVER,
+ NULL);
+
if (ao_mca_poll_trace_always) {
mca->ao_mca_poll_trace =
kmem_zalloc(sizeof (ao_mca_poll_trace_t) *
diff --git a/usr/src/uts/i86pc/io/mc/mcamd.h b/usr/src/uts/i86pc/io/mc/mcamd.h
index 330ecfa5b5..3934e156bb 100644
--- a/usr/src/uts/i86pc/io/mc/mcamd.h
+++ b/usr/src/uts/i86pc/io/mc/mcamd.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -37,6 +36,7 @@
#include <sys/ksynch.h>
#include <sys/mc_amd.h>
#include <mcamd_api.h>
+#include <mcamd_dimmcfg.h>
#ifdef __cplusplus
extern "C" {
@@ -45,24 +45,31 @@ extern "C" {
/*
* PCI configuration space functions for the memory controller. Note that
* the function numbers here also serve as the mc_func indices in the mc_t.
+ * We will not attach to function 3 "Miscellaneous Control" pci1022,1103
+ * since the agpgart driver already attaches to that function; instead we
+ * retrieve what function 3 parameters we require via direct PCI Mechanism 1
+ * accesses.
*/
-#define MC_FUNC_HTCONFIG 0 /* unused */
-#define MC_FUNC_HTCONFIG_BINDNM "pci1022,1100" /* unused */
-#define MC_FUNC_ADDRMAP 1
+enum mc_funcnum {
+ MC_FUNC_HTCONFIG = 0,
+#define MC_FUNC_HTCONFIG_BINDNM "pci1022,1100"
+ MC_FUNC_ADDRMAP = 1,
#define MC_FUNC_ADDRMAP_BINDNM "pci1022,1101"
-#define MC_FUNC_DRAMCTL 2
+ MC_FUNC_DRAMCTL = 2,
#define MC_FUNC_DRAMCTL_BINDNM "pci1022,1102"
+ MC_FUNC_MISCCTL = 3
+};
/*
* The memory controller driver attaches to several device nodes, but publishes
* a single minor node. We need to ensure that the minor node can be
* consistently mapped back to a single (and the same) device node, so we need
- * to pick one to be used. We'll use the DRAM Controller device node, as it'll
- * be the last to be attached.
+ * to pick one to be used. We'll use the misc control device node, as it'll
+ * be the last to be attached (since we do not attach function 3)
*/
#define MC_FUNC_DEVIMAP MC_FUNC_DRAMCTL
-#define MC_FUNC_NUM 3
+#define MC_FUNC_NUM 4 /* include MISCCTL, even if no attach */
/*
* The following define the offsets at which various MC registers are
@@ -71,7 +78,8 @@ extern "C" {
*/
/*
- * BKDG 3.29 section 3.4 - MC DRAM base and limit addresses, hole offset.
+ * Function 1 (address mask) offsets for DRAM base, DRAM limit, DRAM hole
+ * registers.
*/
#define MC_AM_REG_NODE_NUM 8 /* Number of DRAM nodes */
#define MC_AM_REG_DRAMBASE_0 0x40 /* Offset for DRAM Base 0 */
@@ -80,8 +88,8 @@ extern "C" {
#define MC_AM_REG_HOLEADDR 0xf0 /* DRAM Hole Address Register */
/*
- * BKDG 3.29 section 3.5 - DRAM contoller chip-select base, mask,
- * DRAM bank address mapping, DRAM configuration.
+ * Function 2 (dram controller) offsets for chip-select base, chip-select mask,
+ * DRAM bank address mapping, DRAM configuration registers.
*/
#define MC_DC_REG_CS_INCR 4 /* incr for CS base and mask */
#define MC_DC_REG_CSBASE_0 0x40 /* 0x40 - 0x5c */
@@ -89,6 +97,15 @@ extern "C" {
#define MC_DC_REG_BANKADDRMAP 0x80
#define MC_DC_REG_DRAMCFGLO 0x90
#define MC_DC_REG_DRAMCFGHI 0x94
+#define MC_DC_REG_DRAMMISC 0xa0
+
+/*
+ * Function 3 (misc control) offset for NB MCA config, scrubber control
+ * and online spare control.
+ */
+#define MC_CTL_REG_NBCFG 0x44 /* MCA NB configuration register */
+#define MC_CTL_REG_SCRUBCTL 0x58 /* Scrub control register */
+#define MC_CTL_REG_SPARECTL 0xb0 /* On-line spare control register */
typedef struct mc_func {
uint_t mcf_instance;
@@ -99,8 +116,6 @@ typedef struct mc_dimm mc_dimm_t;
typedef struct mc_cs mc_cs_t;
typedef struct mc mc_t;
-typedef uint64_t mc_prop_t; /* see mcamd_get_numprop */
-
/*
* Node types for mch_type below. These are used in array indexing.
*/
@@ -123,55 +138,105 @@ struct mc_dimm {
mc_hdr_t mcd_hdr; /* id, pointer to parent */
mc_dimm_t *mcd_next; /* next dimm for this MC */
mc_cs_t *mcd_cs[MC_CHIP_DIMMRANKMAX]; /* associated chip-selects */
- mc_prop_t mcd_num; /* dimm number */
+ const mcdcfg_csl_t *mcd_csl[MC_CHIP_DIMMRANKMAX]; /* cs lines */
+ mcamd_prop_t mcd_num; /* dimm number */
+ mcamd_prop_t mcd_size; /* dimm size in bytes */
};
#define mcd_mc mcd_hdr.mch_mc
+/*
+ * Chip-select properties. If a chip-select is associated with just one
+ * dimm (whether it be on the A or B dram channel) that number will be
+ * in csp_dimmnums[0]; if the chip-select is associated with two dimms
+ * then csp_dimmnums[0] has the dimm from channel A and csp_dimmnums[1] has
+ * the partner dimm from channel B.
+ */
+typedef struct mccs_props {
+ mcamd_prop_t csp_num; /* Chip-select number */
+ mcamd_prop_t csp_base; /* DRAM CS Base */
+ mcamd_prop_t csp_mask; /* DRAM CS Mask */
+ mcamd_prop_t csp_size; /* Chip-select bank size */
+ mcamd_prop_t csp_csbe; /* Chip-select bank enable */
+ mcamd_prop_t csp_spare; /* Spare */
+ mcamd_prop_t csp_testfail; /* TestFail */
+ mcamd_prop_t csp_dimmnums[MC_CHIP_DIMMPERCS]; /* dimm(s) in cs */
+ mcamd_prop_t csp_dimmrank; /* rank # on dimms */
+} mccs_props_t;
+
+/*
+ * Chip-select config register values
+ */
+typedef struct mccs_cfgrefs {
+ mcamd_cfgreg_t csr_csbase; /* Raw CS base reg */
+ mcamd_cfgreg_t csr_csmask; /* Raw CS mask reg */
+} mccs_cfgregs_t;
+
struct mc_cs {
mc_hdr_t mccs_hdr; /* id, pointer to parent */
mc_cs_t *mccs_next; /* Next chip-select of MC */
mc_dimm_t *mccs_dimm[MC_CHIP_DIMMPERCS]; /* dimms for this cs */
- mc_prop_t mccs_num; /* Chip-select number */
- mc_prop_t mccs_base; /* DRAM CS Base */
- mc_prop_t mccs_mask; /* DRAM CS Mask */
- mc_prop_t mccs_size; /* Chip-select bank size */
- mc_prop_t mccs_dimmnums[MC_CHIP_DIMMPERCS];
+ const mcdcfg_csl_t *mccs_csl[MC_CHIP_DIMMPERCS]; /* cs lines */
+ mccs_props_t mccs_props; /* Properties */
+ mccs_cfgregs_t mccs_cfgregs; /* Raw config values */
};
#define mccs_mc mccs_hdr.mch_mc
+/*
+ * Memory controller properties.
+ */
typedef struct mc_props {
- mc_dimm_t *mcp_dimmlist; /* List of all logical DIMMs, */
- mc_dimm_t *mcp_dimmlast; /* linked via mcd_mcnext */
- mc_prop_t mcp_num; /* Associated *chip* number */
- mc_prop_t mcp_rev; /* Chip revision (MC_REV_*) */
- mc_prop_t mcp_base; /* base address for mc's drams */
- mc_prop_t mcp_lim; /* limit address for mc's drams */
- mc_prop_t mcp_dramcfg; /* DRAM config hi, DRAM config lo */
- mc_prop_t mcp_dramhole; /* DRAM Hole Address Register */
- mc_prop_t mcp_ilen; /* interleave enable */
- mc_prop_t mcp_ilsel; /* interleave select */
- mc_prop_t mcp_csbankmap; /* chip-select bank mapping reg */
- mc_prop_t mcp_accwidth; /* dram access width (64 or 128) */
- mc_prop_t mcp_csbank_intlv; /* cs bank interleave factor */
- mc_prop_t mcp_disabled_cs; /* # banks with CSBE clear */
+ mcamd_prop_t mcp_num; /* Associated *chip* number */
+ mcamd_prop_t mcp_rev; /* Chip revision (MC_REV_*) */
+ mcamd_prop_t mcp_base; /* base address for mc's drams */
+ mcamd_prop_t mcp_lim; /* limit address for mc's drams */
+ mcamd_prop_t mcp_ilen; /* interleave enable */
+ mcamd_prop_t mcp_ilsel; /* interleave select */
+ mcamd_prop_t mcp_csintlvfctr; /* cs bank interleave factor */
+ mcamd_prop_t mcp_dramhole_size; /* DRAM Hole Size */
+ mcamd_prop_t mcp_accwidth; /* dram access width (64 or 128) */
+ mcamd_prop_t mcp_csbankmapreg; /* chip-select bank mapping reg */
+ mcamd_prop_t mcp_bnkswzl; /* BankSwizzle enabled */
+ mcamd_prop_t mcp_mod64mux; /* Mismtached DIMMs support enabled */
+ mcamd_prop_t mcp_sparecs; /* cs# replaced by online spare */
+ mcamd_prop_t mcp_badcs; /* cs# replaced by online spare */
} mc_props_t;
+/*
+ * Memory controller config register values
+ */
+typedef struct mc_cfgregs {
+ mcamd_cfgreg_t mcr_drambase;
+ mcamd_cfgreg_t mcr_dramlimit;
+ mcamd_cfgreg_t mcr_dramhole;
+ mcamd_cfgreg_t mcr_dramcfglo;
+ mcamd_cfgreg_t mcr_dramcfghi;
+ mcamd_cfgreg_t mcr_drammisc;
+ mcamd_cfgreg_t mcr_nbcfg;
+ mcamd_cfgreg_t mcr_sparectl;
+} mc_cfgregs_t;
+
struct mc {
mc_hdr_t mc_hdr; /* id */
struct mc *mc_next; /* linear, doubly-linked list */
const char *mc_revname; /* revision name string */
+ uint32_t mc_socket; /* Package type */
uint_t mc_ref; /* reference (attach) count */
mc_func_t mc_funcs[MC_FUNC_NUM]; /* Instance, devinfo, ... */
chip_t *mc_chip; /* Associated chip */
mc_cs_t *mc_cslist; /* All active chip-selects */
mc_cs_t *mc_cslast; /* End of chip-select list */
+ mc_dimm_t *mc_dimmlist; /* List of all logical DIMMs, */
+ mc_dimm_t *mc_dimmlast; /* linked via mcd_mcnext */
mc_props_t mc_props; /* Properties */
+ mc_cfgregs_t mc_cfgregs; /* Raw config values */
+ hrtime_t mc_spareswaptime; /* If initiated by us */
nvlist_t *mc_nvl; /* nvlist for export */
char *mc_snapshot; /* packed nvlist for libmc */
size_t mc_snapshotsz; /* packed nvlist buffer size */
uint_t mc_snapshotgen; /* snapshot generation number */
+ int mc_csdiscontig; /* chip-selects discontiguous */
};
typedef struct mcamd_hdl {
@@ -184,6 +249,7 @@ extern krwlock_t mc_lock;
extern void mcamd_mkhdl(mcamd_hdl_t *);
extern void mcamd_mc_register(struct cpu *);
+extern void mcamd_ereport_post(mc_t *, const char *, mc_unum_t *, uint64_t);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.c b/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.c
new file mode 100644
index 0000000000..e0fa449119
--- /dev/null
+++ b/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.c
@@ -0,0 +1,435 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <mcamd_dimmcfg_impl.h>
+
+/*
+ * We have built a list of the active csbase/csmask pairs, and now we want
+ * to associate those active chip-selects with actual dimms. To achieve this
+ * we must map the csbase/csmask pair to an associated logical DIMM and
+ * chip-select line.
+ *
+ * A logical DIMM comprises up to 2 physical dimms as follows:
+ *
+ * - in 64-bit mode without mismatched dimm support logical DIMMs are
+ * made up of just one physical dimm situated in a "lodimm" slot
+ * on channel A; the corresponding slot on channel B (if there is
+ * a channel B) must be empty or will be disabled if populated.
+ *
+ * - in 64-bit mode with mismatched dimm support a logical DIMM may
+ * be made up of 1 or 2 physical dimms - one on channel A and another
+ * in the corresponding slot on channel B. They are accessed
+ * independently.
+ *
+ * - in 128 bit mode a logical DIMM is made up of two physical dimms -
+ * a pair of one slot on channel A and its partner on channel B.
+ * The lodimm on channel A provides data [63:0] while the updimm
+ * on channel B provides data [127:64]. The two dimms must be
+ * identical in size and organisation (number of ranks etc).
+ *
+ * For our dimm numbering purposes we need go no further than deriving
+ * the logical DIMM number for a given csbase/csmask pair and presence
+ * of quadrank support and mismatched dimms. For logical DIMM number N
+ * (N = 0, 1, 2, or 3):
+ *
+ * - the lodimm, if present, is numbered N * 2
+ * - the updimm, if present, is numbered N * 2 + 1
+ *
+ * Presence is deduced by observed configuration, as above. This numbering
+ * scheme, however, often bears little or no resemblance to how the dimm slots
+ * are physically labelled/silkscreened (which in turn often bears little
+ * resemblance to how SMBIOS data, if available, describes the slot). To
+ * determine slot labels we will map chip-select *line* to slot name in
+ * some hand-crafted tables (which live in the libtopo enumerator and are
+ * built after consulting the board schematics for a platform). To provide
+ * the chip-select line name we will perform some additional gymnastics here
+ * to derive the chip-select line or lines (in 128 bit mode) associated
+ * with a chip-select rank. This is achieved via matching against the
+ * "DRAM CS Base and DRAM CS Mask Registers" with and without mismatched
+ * dimm support tables of the BKDG (tables 5 and 6 of BKDG 3.31 for rev E
+ * and earlier; tables 8 and 9 of BKDG 3.01 for rev F and G).
+ *
+ * The following tables could be implemented programtically, but are more
+ * readily reviewed for correctness presented as tables.
+ */
+
+/* BEGIN CSTYLED */
+
+/*
+ * Revision E and earlier mapping with mismatched dimm support disabled.
+ */
+static const struct mcdcfg_csmapline csmap_nomod64mux_preF[] = {
+ /*
+ * Pkgs base dramconfig ldimm cs A cs B
+ *
+ * Base reg 0 (mask 0)
+ */
+ { SKT_ALL, 0, DCFG_ALL, 0, { { CH_A, 0, 0 }, { CH_B, 0, 0 } } },
+ /*
+ * Base reg 1 (mask 1)
+ */
+ { SKT_ALL, 1, DCFG_ALL, 0, { { CH_A, 0, 1 }, { CH_B, 0, 1 } } },
+ /*
+ * Base reg 2 (mask 2)
+ */
+ { SKT_ALL, 2, DCFG_ALL, 1, { { CH_A, 1, 0 }, { CH_B, 1, 0 } } },
+ /*
+ * Base reg 3 (mask 3)
+ */
+ { SKT_ALL, 3, DCFG_ALL, 1, { { CH_A, 1, 1 }, { CH_B, 1, 1 } } },
+ /*
+ * Base reg 4 (mask 4)
+ */
+ { SKT_940, 4, DCFG_N, 2, { { CH_A, 2, 0 }, { CH_B, 2, 0 } } },
+ { SKT_940, 4, DCFG_R4, 0, { { CH_A, 2, 0 }, { CH_B, 2, 0 } } },
+ /*
+ * Base reg 5 (mask 5)
+ */
+ { SKT_940, 5, DCFG_N, 2, { { CH_A, 2, 1 }, { CH_B, 2, 1 } } },
+ { SKT_940, 5, DCFG_R4, 0, { { CH_A, 2, 1 }, { CH_B, 2, 1 } } },
+ /*
+ * Base reg 6 (mask 6)
+ */
+ { SKT_940, 6, DCFG_N, 3, { { CH_A, 3, 0 }, { CH_B, 3, 0 } } },
+ { SKT_940, 6, DCFG_R4, 1, { { CH_A, 3, 0 }, { CH_B, 3, 0 } } },
+ /*
+ * Base reg 7 (mask 7)
+ */
+ { SKT_940, 7, DCFG_N, 3, { { CH_A, 3, 1 }, { CH_B, 3, 1 } } },
+ { SKT_940, 7, DCFG_R4, 1, { { CH_A, 3, 1 }, { CH_B, 3, 1 } } }
+};
+
+/*
+ * Revision E and earlier mapping with mismatched dimm support.
+ * Mismatched dimm support applies only to the socket 939 package.
+ * Socket 939 does not support registered dimms, so quadrank RDIMMs are
+ * not an issue here.
+ */
+static const struct mcdcfg_csmapline csmap_mod64mux_preF[] = {
+ /*
+ * Pkgs base dramconfig ldimm cs A cs B
+ *
+ * Base reg 0 (mask 0)
+ */
+ { SKT_939, 0, DCFG_N, 0, { { CH_A, 0, 0 } } },
+ /*
+ * Base reg 1 (mask 1)
+ */
+ { SKT_939, 1, DCFG_N, 0, { { CH_A, 0, 1 } } },
+ /*
+ * Base reg 2 (mask 2)
+ */
+ { SKT_939, 2, DCFG_N, 1, { { CH_A, 1, 0 } } },
+ /*
+ * Base reg 3 (mask 3)
+ */
+ { SKT_939, 3, DCFG_N, 1, { { CH_A, 1, 1 } } },
+ /*
+ * Base reg 4 (mask 4)
+ */
+ { SKT_939, 4, DCFG_N, 0, { { CH_B, 0, 0 } } },
+ /*
+ * Base reg 5 (mask 5)
+ */
+ { SKT_939, 5, DCFG_N, 0, { { CH_B, 0, 1 } } },
+ /*
+ * Base reg 6 (mask 6)
+ */
+ { SKT_939, 6, DCFG_N, 1, { { CH_B, 1, 0 } } },
+ /*
+ * Base reg 7 (mask 7)
+ */
+ { SKT_939, 7, DCFG_N, 1, { { CH_B, 1, 1 } } }
+};
+
+/*
+ * Rev F and G csbase/csmask to logical DIMM and cs line mappings.
+ *
+ * We can reduce the tables by a few lines by taking into account which
+ * DIMM types are supported by the different package types:
+ *
+ * Number of dimms of given type supported per dram channel
+ * Package Reg'd DIMM 4-rank reg'd Unbuffered SO-DIMMs
+ * F(1207) 4 2 0 0
+ * AM2 0 0 2 1
+ * S1g1 0 0 0 1
+ */
+
+/*
+ * NPT (rev F & G) mapping with mismatched dimm support disabled.
+ */
+static const struct mcdcfg_csmapline csmap_nomod64mux_fg[] = {
+ /*
+ * Pkgs base dramconfig ldimm cs A cs B
+ *
+ * Base reg 0 (mask 0)
+ */
+ { SKT_NPT, 0, DCFG_ALLNPT, 0, { { CH_A, 0, 0 }, { CH_B, 0, 0 } } },
+ /*
+ * Base reg 1 (mask 0)
+ */
+ { SKT_NPT, 1, DCFG_ALLNPT, 0, { { CH_A, 0, 1 }, { CH_B, 0, 1 } } },
+ /*
+ * Base reg 2 (mask 1)
+ */
+ { AM2F1207, 2, DCFG_N | DCFG_R4, 1, { { CH_A, 1, 0 }, { CH_B, 1, 0 } } },
+ { AM2, 2, DCFG_S4, 0, { { CH_A, 1, 0 }, { CH_B, 1, 0 } } },
+ { S1g1, 2, DCFG_N, 1, { { CH_A, 0, 2 }, { CH_B, 0, 2 } } },
+ { S1g1, 2, DCFG_S4, 0, { { CH_A, 0, 2 }, { CH_B, 0, 2 } } },
+ /*
+ * Base reg 3 (mask 1)
+ */
+ { AM2F1207, 3, DCFG_N | DCFG_R4, 1, { { CH_A, 1, 1 }, { CH_B, 1, 1 } } },
+ { AM2, 3, DCFG_S4, 0, { { CH_A, 0, 3 }, { CH_B, 0, 3 } } },
+ { S1g1, 3, DCFG_N, 1, { { CH_A, 1, 1 }, { CH_B, 1, 1 } } },
+ { S1g1, 3, DCFG_S4, 0, { { CH_A, 0, 3 }, { CH_B, 0, 3 } } },
+ /*
+ * Base reg 4 (mask 2)
+ */
+ { F1207, 4, DCFG_N, 2, { { CH_A, 2, 0 }, { CH_B, 2, 0 } } },
+ { F1207, 4, DCFG_R4, 0, { { CH_A, 2, 0 }, { CH_B, 2, 0 } } },
+ /*
+ * Base reg 5 (mask 2)
+ */
+ { F1207, 5, DCFG_N, 2, { { CH_A, 2, 1 }, { CH_B, 2, 1 } } },
+ { F1207, 5, DCFG_R4, 0, { { CH_A, 2, 1 }, { CH_B, 2, 1 } } },
+ /*
+ * Base reg 6 (mask 3)
+ */
+ { F1207, 6, DCFG_N, 3, { { CH_A, 3, 0 }, { CH_B, 3, 0 } } },
+ { F1207, 6, DCFG_R4, 1, { { CH_A, 3, 0 }, { CH_B, 3, 0 } } },
+ /*
+ * Base reg 7 (mask 3)
+ */
+ { F1207, 7, DCFG_N, 3, { { CH_A, 3, 1 }, { CH_B, 3, 1 } } },
+ { F1207, 7, DCFG_R4, 1, { { CH_A, 3, 1 }, { CH_B, 3, 1 } } }
+};
+
+/*
+ * NPT (rev F & G) mapping with mismatched dimm support.
+ * Mismatched dimm support applies only to the AM2 and S1g1 packages.
+ * AM2 and S1g1 do not support registered dimms.
+ */
+static const struct mcdcfg_csmapline csmap_mod64mux_fg[] = {
+ /*
+ * Pkgs base dramconfig ldimm cs A cs B
+ *
+ * Base reg 0 (mask 0)
+ */
+ { AM2S1g1, 0, DCFG_N | DCFG_S4, 0, { { CH_A, 0, 0 } } },
+ /*
+ * Base reg 1 (mask 0)
+ */
+ { AM2S1g1, 1, DCFG_N | DCFG_S4, 0, { { CH_A, 0, 1 } } },
+ /*
+ * Base reg 2 (mask 1)
+ */
+ { AM2, 2, DCFG_N, 1, { { CH_A, 1, 0 } } },
+ { AM2, 2, DCFG_S4, 0, { { CH_A, 1, 0 } } },
+ { S1g1, 2, DCFG_N, 1, { { CH_A, 0, 2 } } },
+ { S1g1, 2, DCFG_S4, 0, { { CH_A, 0, 2 } } },
+ /*
+ * Base reg 3 (mask 1)
+ */
+ { AM2, 3, DCFG_N, 1, { { CH_A, 1, 1 } } },
+ { AM2, 3, DCFG_S4, 0, { { CH_A, 1, 1 } } },
+ { S1g1, 3, DCFG_N, 1, { { CH_A, 0, 3 } } },
+ { S1g1, 3, DCFG_S4, 0, { { CH_A, 0, 3 } } },
+ /*
+ * Base reg 4 (mask 2)
+ */
+ { AM2S1g1, 4, DCFG_N, 2, { { CH_B, 0, 0 } } },
+ { AM2S1g1, 4, DCFG_S4, 1, { { CH_B, 0, 0 } } },
+ /*
+ * Base reg 5 (mask 2)
+ */
+ { AM2S1g1, 5, DCFG_N, 2, { { CH_B, 0, 1 } } },
+ { AM2S1g1, 5, DCFG_S4, 1, { { CH_B, 0, 1 } } },
+ /*
+ * Base reg 6 (mask 3)
+ */
+ { AM2, 6, DCFG_N, 3, { { CH_B, 1, 0 } } },
+ { AM2, 6, DCFG_S4, 1, { { CH_B, 1, 0 } } },
+ { S1g1, 6, DCFG_N, 3, { { CH_B, 0, 2 } } },
+ { S1g1, 6, DCFG_S4, 1, { { CH_B, 0, 2 } } },
+ /*
+ * Base reg 7 (mask 3)
+ */
+ { AM2, 7, DCFG_N, 3, { { CH_B, 1, 1 } } },
+ { AM2, 7, DCFG_S4, 1, { { CH_B, 1, 1 } } },
+ { S1g1, 7, DCFG_N, 3, { { CH_B, 0, 3 } } },
+ { S1g1, 7, DCFG_S4, 1, { { CH_B, 0, 3 } } }
+};
+
+/* END CSTYLED */
+
+#define DCFG_NTBL 4
+
+static const struct {
+ uint32_t revmask; /* applicable chip revs */
+ int mod64mux; /* mismatched support or not */
+ const struct mcdcfg_csmapline *map;
+ int nmapents;
+} csmap_tbls[DCFG_NTBL] = {
+ { MC_REVS_BCDE, 0, &csmap_nomod64mux_preF[0],
+ sizeof (csmap_nomod64mux_preF) / sizeof (struct mcdcfg_csmapline) },
+ { MC_REVS_BCDE, 1, &csmap_mod64mux_preF[0],
+ sizeof (csmap_mod64mux_preF) / sizeof (struct mcdcfg_csmapline) },
+ { MC_REVS_FG, 0, &csmap_nomod64mux_fg[0],
+ sizeof (csmap_nomod64mux_fg) / sizeof (struct mcdcfg_csmapline) },
+ { MC_REVS_FG, 1, &csmap_mod64mux_fg[0],
+ sizeof (csmap_mod64mux_fg) / sizeof (struct mcdcfg_csmapline) }
+};
+
+int
+mcdcfg_lookup(uint32_t rev, int mod64mux, int accwidth, int basenum,
+ uint32_t pkg, int r4, int s4, mcdcfg_rslt_t *rsltp)
+{
+ const struct mcdcfg_csmapline *csm = NULL;
+ int ismux = (mod64mux != 0);
+ int nmapents;
+ int ndimm = (accwidth == 128) ? 2 : 1;
+ int dcfg;
+ int i;
+
+ /*
+ * Validate aspects that the table lookup won't.
+ */
+ if ((accwidth != 64 && accwidth != 128) || (r4 != 0 && s4 != 0))
+ return (-1);
+
+ for (i = 0; i < DCFG_NTBL; i++) {
+ if (MC_REV_MATCH(rev, csmap_tbls[i].revmask) &&
+ ismux == csmap_tbls[i].mod64mux) {
+ csm = csmap_tbls[i].map;
+ nmapents = csmap_tbls[i].nmapents;
+ break;
+ }
+ }
+ if (csm == NULL)
+ return (-1);
+
+ if (r4)
+ dcfg = DCFG_R4;
+ else if (s4)
+ dcfg = DCFG_S4;
+ else
+ dcfg = DCFG_N;
+
+ for (i = 0; i < nmapents; i++, csm++) {
+ if (X86_SOCKET_MATCH(pkg, csm->csm_pkg) &&
+ basenum == csm->csm_basereg &&
+ (dcfg & csm->csm_dimmcfg) != 0)
+ break;
+ }
+ if (i == nmapents)
+ return (-1);
+
+ /*
+ * We return the dimm instance number here for the topology, based
+ * on the AMD Motherboard Design Guide.
+ *
+ * The lodimm/updimm (channel A/B) dimms in a pair are numbered
+ * 0/1, 2/3, 4/5, 6/7 - ie 2 * pairnum and 2 * pairnum + 1 for
+ * pairnum = 0, 1, 2, 3. But we can't use logical dimm number
+ * for pairnum in that calculation, since in the presence of
+ * mismtached dimms logicial dimms 2 and 3 are used for those
+ * dimm modules on the B channel. Instead we number using the
+ * slot number on that dram channel, offsetting those on the B
+ * channel by 1.
+ */
+ rsltp->ldimm = csm->csm_ldimm;
+ rsltp->ndimm = ndimm;
+ for (i = 0; i < ndimm; i++) {
+ rsltp->dimm[i].toponum = 2 * csm->csm_cs[i].csl_slot +
+ (csm->csm_cs[i].csl_chan == CH_B);
+ rsltp->dimm[i].cslp = &csm->csm_cs[i];
+ }
+
+ return (0);
+}
+
+/*
+ * Given a chip-select line and package type return the chip-select line
+ * pin label for that package type.
+ */
+void
+mcdcfg_csname(uint32_t pkg, const mcdcfg_csl_t *cslp, char *buf, int buflen)
+{
+ int csnum;
+
+ switch (pkg) {
+ case X86_SOCKET_754:
+ case X86_SOCKET_940:
+ /*
+ * Format is: MEMCS_L[{0..7}]. That does not identify
+ * a single dimm (since a single chip-select is shared
+ * by both members of a dimm pair in socket 940) so
+ * we tack on some channel identification.
+ */
+ csnum = 2 * cslp->csl_slot + cslp->csl_rank;
+ (void) snprintf(buf, buflen, "MEMCS_L%d (channel %s)", csnum,
+ cslp->csl_chan == 0 ? "A" : "B");
+
+ break;
+
+ case X86_SOCKET_939:
+ /*
+ * Format is: MEMCS_{1,2}{L,H}_L[{1,0}]
+ * {1,2} - dimm pair
+ * {L,H} - lodimm or updimm
+ * {1,0} - rank
+ */
+ (void) snprintf(buf, buflen, "MEMCS_%d%s_L[%d]",
+ cslp->csl_slot + 1,
+ cslp->csl_chan == 0 ? "A" : "B",
+ cslp->csl_rank);
+ break;
+
+ case X86_SOCKET_F1207:
+ case X86_SOCKET_AM2:
+ case X86_SOCKET_S1g1:
+ /*
+ * Format is: M{B,A}{0,1,2,3}_CS_L[{0,1,2,3}]
+ * {B,A} - channel
+ * {0,1,2,3} - slot on channel
+ * {0,1,2,3} - rank
+ */
+ (void) snprintf(buf, buflen, "M%s%d_CS_L[%d]",
+ cslp->csl_chan == 0 ? "A" : "B",
+ cslp->csl_slot,
+ cslp->csl_rank);
+ break;
+
+ default:
+ (void) snprintf(buf, buflen, "Unknown");
+ break;
+ }
+}
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.h b/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.h
new file mode 100644
index 0000000000..006c82f726
--- /dev/null
+++ b/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.h
@@ -0,0 +1,73 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MCAMD_DIMMCFG_H
+#define _MCAMD_DIMMCFG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/mc_amd.h>
+
+typedef struct mcdcfg_csl mcdcfg_csl_t;
+typedef struct mcdcfg_rslt mcdcfg_rslt_t;
+
+/*
+ * Chip-select line representation.
+ */
+struct mcdcfg_csl {
+ uint8_t csl_chan; /* 0 = A, 1 = B */
+ uint8_t csl_slot; /* dimm slot on channel, 0/1/2/3 (pair #) */
+ uint8_t csl_rank; /* dimm rank in slot, 0/1/2/3 */
+};
+
+/*
+ * Results structure for mdcfg_lookup
+ */
+struct mcdcfg_rslt {
+ int ldimm; /* logical DIMM number */
+ int ndimm; /* # of associated physical dimms */
+ struct {
+ int toponum; /* dimm instance in topology */
+ const mcdcfg_csl_t *cslp; /* chip-select parameters */
+ } dimm[MC_CHIP_DIMMPERCS]; /* ndimm entries are valid */
+};
+
+/*
+ * Chip-select line name maximum string length
+ */
+#define MCDCFG_CSNAMELEN 32
+
+extern int mcdcfg_lookup(uint32_t, int, int, int, uint32_t, int, int,
+ mcdcfg_rslt_t *);
+extern void mcdcfg_csname(uint32_t, const mcdcfg_csl_t *, char *, int);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MCAMD_DIMMCFG_H */
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg_impl.h b/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg_impl.h
new file mode 100644
index 0000000000..cc47ddaef0
--- /dev/null
+++ b/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg_impl.h
@@ -0,0 +1,91 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MCAMD_DIMMCFG_IMPL_H
+#define _MCAMD_DIMMCFG_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/x86_archext.h>
+#include <sys/mc_amd.h>
+#include <mcamd_dimmcfg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Defines for csl_chan
+ */
+#define CH_A 0
+#define CH_B 1
+
+/*
+ * Line structure for the tables. We include up to 2 chip-selects per
+ * line - the consumer must use the first in 64-bit mode and both in
+ * 128-bit mode.
+ */
+struct mcdcfg_csmapline {
+ uint32_t csm_pkg; /* applicable package types */
+ uint8_t csm_basereg; /* csbase register number; implies mask */
+ uint8_t csm_dimmcfg; /* bitmask of DIMM_{N,R4,S4} which apply */
+ uint8_t csm_ldimm; /* Logical DIMM number */
+ const struct mcdcfg_csl csm_cs[MC_CHIP_DIMMPERCS];
+};
+
+/*
+ * Defines for use with csm_pkg - pre-NPT packages
+ */
+#define SKT_754 X86_SOCKET_754
+#define SKT_939 X86_SOCKET_939
+#define SKT_940 X86_SOCKET_940
+#define SKT_ALL (X86_SOCKET_754 | X86_SOCKET_939 | X86_SOCKET_940)
+
+/*
+ * Defines for use with csm_pkg - NPT packages
+ */
+#define F1207 X86_SOCKET_F1207
+#define AM2 X86_SOCKET_AM2
+#define S1g1 X86_SOCKET_S1g1
+#define SKT_NPT (X86_SOCKET_S1g1 | X86_SOCKET_AM2 | X86_SOCKET_F1207)
+#define AM2F1207 (X86_SOCKET_AM2 | X86_SOCKET_F1207)
+#define AM2S1g1 (X86_SOCKET_AM2 | X86_SOCKET_S1g1)
+
+/*
+ * Defines for use with csm_dimmcfg
+ */
+#define DCFG_N 0x1 /* Normal */
+#define DCFG_R4 0x2 /* Four-rank registered DIMM */
+#define DCFG_S4 0x4 /* four-rank SO-DIMM (NPT only) */
+#define DCFG_ALL (DCFG_N | DCFG_R4)
+#define DCFG_ALLNPT (DCFG_N | DCFG_R4 | DCFG_S4)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MCAMD_DIMMCFG_IMPL_H */
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_drv.c b/usr/src/uts/i86pc/io/mc/mcamd_drv.c
index 57e7c5ec6a..d91dda26d3 100644
--- a/usr/src/uts/i86pc/io/mc/mcamd_drv.c
+++ b/usr/src/uts/i86pc/io/mc/mcamd_drv.c
@@ -34,7 +34,6 @@
#include <sys/stat.h>
#include <sys/modctl.h>
#include <sys/types.h>
-#include <sys/mc.h>
#include <sys/cpuvar.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
@@ -47,17 +46,33 @@
#include <sys/machsystm.h>
#include <sys/x86_archext.h>
#include <sys/cpu_module.h>
+#include <qsort.h>
+#include <sys/mc.h>
#include <sys/mc_amd.h>
-
#include <mcamd.h>
+#include <mcamd_dimmcfg.h>
+#include <mcamd_pcicfg.h>
#include <mcamd_api.h>
+#include <sys/fm/cpu/AMD.h>
-int mc_quadranksupport = 0; /* set to 1 for a MB with quad rank support */
+/*
+ * Of the 754/939/940 packages, only socket 940 supports quadrank registered
+ * dimms. Unfortunately, no memory-controller register indicates the
+ * presence of quadrank dimm support or presence (i.e., in terms of number
+ * of slots per cpu, and chip-select lines per slot, The following may be set
+ * in /etc/system to indicate the presence of quadrank support on a motherboard.
+ *
+ * There is no need to set this for F(1207) and S1g1.
+ */
+int mc_quadranksupport = 0;
-mc_t *mc_list;
+mc_t *mc_list, *mc_last;
krwlock_t mc_lock;
int mc_hold_attached = 1;
+#define MAX(m, n) ((m) >= (n) ? (m) : (n))
+#define MIN(m, n) ((m) <= (n) ? (m) : (n))
+
static void
mc_snapshot_destroy(mc_t *mc)
{
@@ -68,6 +83,7 @@ mc_snapshot_destroy(mc_t *mc)
kmem_free(mc->mc_snapshot, mc->mc_snapshotsz);
mc->mc_snapshot = NULL;
+ mc->mc_snapshotsz = 0;
mc->mc_snapshotgen++;
}
@@ -87,112 +103,112 @@ mc_snapshot_update(mc_t *mc)
}
static mc_t *
-mc_lookup_func(dev_info_t *dip, int instance, mc_func_t **funcp)
+mc_lookup_by_chipid(int chipid)
{
mc_t *mc;
- int i;
ASSERT(RW_LOCK_HELD(&mc_lock));
for (mc = mc_list; mc != NULL; mc = mc->mc_next) {
- for (i = 0; i < MC_FUNC_NUM; i++) {
- mc_func_t *func = &mc->mc_funcs[i];
- if ((dip != NULL && func->mcf_devi == dip) ||
- (dip == NULL && func->mcf_instance == instance)) {
- if (funcp != NULL)
- *funcp = func;
- return (mc);
- }
- }
+ if (mc->mc_chip->chip_id == chipid)
+ return (mc);
}
return (NULL);
}
-static mc_t *
-mc_lookup_by_devi(dev_info_t *dip, mc_func_t **funcp)
+/*
+ * Read config register pairs into the two arrays provided on the given
+ * handle and at offsets as follows:
+ *
+ * Index Array r1 offset Array r2 offset
+ * 0 r1addr r2addr
+ * 1 r1addr + incr r2addr + incr
+ * 2 r1addr + 2 * incr r2addr + 2 * incr
+ * ...
+ * n - 1 r1addr + (n - 1) * incr r2addr + (n - 1) * incr
+ *
+ * The number of registers to read into the r1 array is r1n; the number
+ * for the r2 array is r2n.
+ */
+static void
+mc_prop_read_pair(mc_pcicfg_hdl_t cfghdl, uint32_t *r1, off_t r1addr,
+ int r1n, uint32_t *r2, off_t r2addr, int r2n, off_t incr)
{
- return (mc_lookup_func(dip, 0, funcp));
-}
+ int i;
-static mc_t *
-mc_lookup_by_instance(int instance, mc_func_t **funcp)
-{
- return (mc_lookup_func(NULL, instance, funcp));
+ for (i = 0; i < MAX(r1n, r2n); i++, r1addr += incr, r2addr += incr) {
+ if (i < r1n)
+ r1[i] = mc_pcicfg_get32(cfghdl, r1addr);
+ if (i < r2n)
+ r2[i] = mc_pcicfg_get32(cfghdl, r2addr);
+ }
}
-static mc_t *
-mc_lookup_by_chipid(int chipid)
-{
- mc_t *mc;
+#define NSKT 6
- ASSERT(RW_LOCK_HELD(&mc_lock));
+static void
+mc_nvl_add_socket(nvlist_t *nvl, mc_t *mc)
+{
+ const char *s = "Unknown";
+ int i;
- for (mc = mc_list; mc != NULL; mc = mc->mc_next) {
- if (mc->mc_chip->chip_id == chipid)
- return (mc);
+ static const struct {
+ uint32_t type;
+ const char *name;
+ } sktnames[NSKT] = {
+ { X86_SOCKET_754, "Socket 754" },
+ { X86_SOCKET_939, "Socket 939" },
+ { X86_SOCKET_940, "Socket 940" },
+ { X86_SOCKET_AM2, "Socket AM2" },
+ { X86_SOCKET_F1207, "Socket F(1207)" },
+ { X86_SOCKET_S1g1, "Socket S1g1" },
+ };
+
+ for (i = 0; i < NSKT; i++) {
+ if (mc->mc_socket == sktnames[i].type) {
+ s = sktnames[i].name;
+ break;
+ }
}
- return (NULL);
+ (void) nvlist_add_string(nvl, "socket", s);
}
-typedef struct mc_rev_map {
- uint_t rm_family;
- uint_t rm_modello;
- uint_t rm_modelhi;
- uint_t rm_rev;
- const char *rm_name;
-} mc_rev_map_t;
-
-static const mc_rev_map_t mc_rev_map[] = {
- { 0xf, 0x00, 0x0f, MC_REV_PRE_D, "B/C/CG" },
- { 0xf, 0x10, 0x1f, MC_REV_D_E, "D" },
- { 0xf, 0x20, 0x3f, MC_REV_D_E, "E" },
- { 0xf, 0x40, 0x5f, MC_REV_F, "F" },
- { 0, 0, 0, MC_REV_UNKNOWN, NULL }
-};
-
-static const mc_rev_map_t *
-mc_revision(chip_t *chp)
+static uint32_t
+mc_ecc_enabled(mc_t *mc)
{
- int rmn = sizeof (mc_rev_map) / sizeof (mc_rev_map[0]);
- const mc_rev_map_t *rm;
- uint8_t family, model;
+ uint32_t rev = mc->mc_props.mcp_rev;
+ union mcreg_nbcfg nbcfg;
- if (chp == NULL)
- return (&mc_rev_map[rmn - 1]);
+ MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg;
- /*
- * For the moment, we assume that both cores in multi-core chips will
- * be of the same revision, so we'll confine our revision check to
- * the first CPU pointed to by this chip.
- */
- family = cpuid_getfamily(chp->chip_cpus);
- model = cpuid_getmodel(chp->chip_cpus);
+ return (MC_REV_MATCH(rev, MC_REVS_BCDE) ?
+ MCREG_FIELD_preF(&nbcfg, EccEn) : MCREG_FIELD_revFG(&nbcfg, EccEn));
+}
- for (rm = mc_rev_map; rm->rm_rev != MC_REV_UNKNOWN; rm++) {
- if (family == rm->rm_family && model >= rm->rm_modello &&
- model <= rm->rm_modelhi)
- break;
- }
+static uint32_t
+mc_ck_enabled(mc_t *mc)
+{
+ uint32_t rev = mc->mc_props.mcp_rev;
+ union mcreg_nbcfg nbcfg;
- return (rm);
+ MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg;
+
+ return (MC_REV_MATCH(rev, MC_REVS_BCDE) ?
+ MCREG_FIELD_preF(&nbcfg, ChipKillEccEn) :
+ MCREG_FIELD_revFG(&nbcfg, ChipKillEccEn));
}
static void
-mc_prop_read_pair(ddi_acc_handle_t cfghdl, uint32_t *r1, off_t r1addr,
- uint32_t *r2, off_t r2addr, int n, off_t incr)
+mc_nvl_add_ecctype(nvlist_t *nvl, mc_t *mc)
{
- int i;
-
- for (i = 0; i < n; i++, r1addr += incr, r2addr += incr) {
- r1[i] = pci_config_get32(cfghdl, r1addr);
- r2[i] = pci_config_get32(cfghdl, r2addr);
- }
+ (void) nvlist_add_string(nvl, "ecc-type", mc_ecc_enabled(mc) ?
+ (mc_ck_enabled(mc) ? "ChipKill 128/16" : "Normal 64/8") : "None");
}
static void
-mc_nvl_add_prop(nvlist_t *nvl, void *node, uint_t code)
+mc_nvl_add_prop(nvlist_t *nvl, void *node, mcamd_propcode_t code, int reqval)
{
int valfound;
uint64_t value;
@@ -201,7 +217,7 @@ mc_nvl_add_prop(nvlist_t *nvl, void *node, uint_t code)
valfound = mcamd_get_numprop(NULL, (mcamd_node_t *)node, code, &value);
ASSERT(name != NULL && valfound);
- if (name != NULL && valfound)
+ if (name != NULL && valfound && (!reqval || value != MC_INVALNUM))
(void) nvlist_add_uint64(nvl, name, value);
}
@@ -215,56 +231,87 @@ mc_nvl_create(mc_t *mc)
int nelem, i;
(void) nvlist_alloc(&mcnvl, NV_UNIQUE_NAME, KM_SLEEP);
- (void) nvlist_add_string(mcnvl, "revname", mc->mc_revname);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_NUM);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_REV);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BASE_ADDR);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_LIM_ADDR);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAM_CONFIG);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAM_HOLE);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAM_ILEN);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAM_ILSEL);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSBANKMAP);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ACCESS_WIDTH);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSBANK_INTLV);
- mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DISABLED_CS);
+ /*
+ * Any changes to member names, types, value semantics or to
+ * whether they are optional or required should result in
+ * a new version number *after* an ARC case since this nvlist_t
+ * is intended to become a contracted interface.
+ */
+ (void) nvlist_add_uint8(mcnvl, MC_NVLIST_VERSTR, MC_NVLIST_VERS1);
+
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_NUM, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_REV, 0);
+ (void) nvlist_add_string(mcnvl, "revname", mc->mc_revname);
+ mc_nvl_add_socket(mcnvl, mc);
+ mc_nvl_add_ecctype(mcnvl, mc);
+
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BASE_ADDR, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_LIM_ADDR, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ILEN, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ILSEL, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSINTLVFCTR, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAMHOLE_SIZE, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ACCESS_WIDTH, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSBANKMAPREG, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BANKSWZL, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_MOD64MUX, 0);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_SPARECS, 1);
+ mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BADCS, 1);
for (nelem = 0; mccs != NULL; mccs = mccs->mccs_next, nelem++) {
nvlist_t **csp = &cslist[nelem];
+ char csname[MCDCFG_CSNAMELEN];
(void) nvlist_alloc(csp, NV_UNIQUE_NAME, KM_SLEEP);
- mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_NUM);
- mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_BASE_ADDR);
- mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_MASK);
- mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_SIZE);
-
- if (mccs->mccs_dimmnums[0] != -1)
- mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_LODIMM);
- if (mccs->mccs_dimmnums[1] != -1)
- mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_UPDIMM);
+ mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_NUM, 0);
+ mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_BASE_ADDR, 0);
+ mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_MASK, 0);
+ mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_SIZE, 0);
+
+ mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_CSDIMM1, 0);
+ mcdcfg_csname(mc->mc_socket, mccs->mccs_csl[0], csname,
+ sizeof (csname));
+ (void) nvlist_add_string(*csp, "dimm1-csname", csname);
+
+ mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_CSDIMM2, 1);
+ if (mccs->mccs_csl[1] != NULL) {
+ mcdcfg_csname(mc->mc_socket, mccs->mccs_csl[1], csname,
+ sizeof (csname));
+ (void) nvlist_add_string(*csp, "dimm2-csname", csname);
+ }
}
(void) nvlist_add_nvlist_array(mcnvl, "cslist", cslist, nelem);
for (i = 0; i < nelem; i++)
nvlist_free(cslist[i]);
- for (nelem = 0, mcd = mc->mc_props.mcp_dimmlist; mcd != NULL;
+ for (nelem = 0, mcd = mc->mc_dimmlist; mcd != NULL;
mcd = mcd->mcd_next, nelem++) {
nvlist_t **dimmp = &dimmlist[nelem];
- int ncs = 0;
uint64_t csnums[MC_CHIP_DIMMRANKMAX];
+ char csname[4][MCDCFG_CSNAMELEN];
+ char *csnamep[4];
+ int ncs = 0;
(void) nvlist_alloc(dimmp, NV_UNIQUE_NAME, KM_SLEEP);
- mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_NUM);
+ mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_NUM, 1);
+ mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_SIZE, 1);
for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) {
- if (mcd->mcd_cs[i] != NULL)
- csnums[ncs++] = mcd->mcd_cs[i]->mccs_num;
+ if (mcd->mcd_cs[i] != NULL) {
+ csnums[ncs] =
+ mcd->mcd_cs[i]->mccs_props.csp_num;
+ mcdcfg_csname(mc->mc_socket, mcd->mcd_csl[i],
+ csname[ncs], MCDCFG_CSNAMELEN);
+ csnamep[ncs] = csname[ncs];
+ ncs++;
+ }
}
(void) nvlist_add_uint64_array(*dimmp, "csnums", csnums, ncs);
+ (void) nvlist_add_string_array(*dimmp, "csnames", csnamep, ncs);
}
(void) nvlist_add_nvlist_array(mcnvl, "dimmlist", dimmlist, nelem);
@@ -274,198 +321,217 @@ mc_nvl_create(mc_t *mc)
return (mcnvl);
}
+/*
+ * Link a dimm to its associated chip-selects and chip-select lines.
+ * Total the size of all ranks of this dimm.
+ */
static void
-mc_dimm_csadd(mc_dimm_t *mcd, mc_cs_t *mccs)
+mc_dimm_csadd(mc_t *mc, mc_dimm_t *mcd, mc_cs_t *mccs, const mcdcfg_csl_t *csl)
{
+ int factor = (mc->mc_props.mcp_accwidth == 128) ? 2 : 1;
+ uint64_t sz = 0;
int i;
+ /* Skip to first unused rank slot */
for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) {
if (mcd->mcd_cs[i] == NULL) {
mcd->mcd_cs[i] = mccs;
+ mcd->mcd_csl[i] = csl;
+ sz += mccs->mccs_props.csp_size / factor;
break;
+ } else {
+ sz += mcd->mcd_cs[i]->mccs_props.csp_size / factor;
}
}
+
ASSERT(i != MC_CHIP_DIMMRANKMAX);
+
+ mcd->mcd_size = sz;
}
+/*
+ * Create a dimm structure and call to link it to its associated chip-selects.
+ */
static mc_dimm_t *
-mc_dimm_create(mc_t *mc, mc_cs_t *mccs, uint_t num)
+mc_dimm_create(mc_t *mc, uint_t num)
{
mc_dimm_t *mcd = kmem_zalloc(sizeof (mc_dimm_t), KM_SLEEP);
mcd->mcd_hdr.mch_type = MC_NT_DIMM;
mcd->mcd_mc = mc;
mcd->mcd_num = num;
- mc_dimm_csadd(mcd, mccs);
return (mcd);
}
/*
- * A chip-select is associated with up to 2 dimms, and a single dimm may
- * have up to 4 associated chip-selects (in the presence of quad-rank support
- * on the motherboard). How we number our dimms is determined by the MC
- * config. This function may be called by multiple chip-selects for the
- * same dimm(s).
+ * The chip-select structure includes an array of dimms associated with
+ * that chip-select. This function fills that array, and also builds
+ * the list of all dimms on this memory controller mc_dimmlist. The
+ * caller has filled a structure with all there is to know about the
+ * associated dimm(s).
*/
static void
-mc_cs_dimmlist_create(mc_t *mc, mc_cs_t *mccs, uint_t *dimm_nums, int ndimm)
+mc_csdimms_create(mc_t *mc, mc_cs_t *mccs, mcdcfg_rslt_t *rsltp)
{
+ mc_dimm_t *found[MC_CHIP_DIMMPERCS];
mc_dimm_t *mcd;
- mc_props_t *mcp = &mc->mc_props;
- int i;
int nfound = 0;
+ int i;
/*
* Has some other chip-select already created this dimm or dimms?
+ * If so then link to the dimm(s) from the mccs_dimm array,
+ * record their topo numbers in the csp_dimmnums array, and link
+ * the dimm(s) to the additional chip-select.
*/
- for (mcd = mcp->mcp_dimmlist; mcd != NULL; mcd = mcd->mcd_next) {
- for (i = 0; i < ndimm; i++) {
- if (mcd->mcd_num == dimm_nums[i]) {
- mccs->mccs_dimm[i] = mcd;
- mccs->mccs_dimmnums[i] = mcd->mcd_num;
- mc_dimm_csadd(mcd, mccs);
- nfound++;
- }
+ for (mcd = mc->mc_dimmlist; mcd != NULL; mcd = mcd->mcd_next) {
+ for (i = 0; i < rsltp->ndimm; i++) {
+ if (mcd->mcd_num == rsltp->dimm[i].toponum)
+ found[nfound++] = mcd;
}
}
- ASSERT(nfound == 0 || nfound == ndimm);
- if (nfound == ndimm)
- return;
-
- for (i = 0; i < ndimm; i++) {
- mcd = mccs->mccs_dimm[i] =
- mc_dimm_create(mc, mccs, dimm_nums[i]);
+ ASSERT(nfound == 0 || nfound == rsltp->ndimm);
+
+ for (i = 0; i < rsltp->ndimm; i++) {
+ if (nfound == 0) {
+ mcd = mc_dimm_create(mc, rsltp->dimm[i].toponum);
+ if (mc->mc_dimmlist == NULL)
+ mc->mc_dimmlist = mcd;
+ else
+ mc->mc_dimmlast->mcd_next = mcd;
+ mc->mc_dimmlast = mcd;
+ } else {
+ mcd = found[i];
+ }
- mccs->mccs_dimmnums[i] = mcd->mcd_num;
+ mccs->mccs_dimm[i] = mcd;
+ mccs->mccs_csl[i] = rsltp->dimm[i].cslp;
+ mccs->mccs_props.csp_dimmnums[i] = mcd->mcd_num;
+ mc_dimm_csadd(mc, mcd, mccs, rsltp->dimm[i].cslp);
- if (mcp->mcp_dimmlist == NULL)
- mcp->mcp_dimmlist = mcd;
- else
- mcp->mcp_dimmlast->mcd_next = mcd;
- mcp->mcp_dimmlast = mcd;
}
+ /* The rank number is constant across all constituent dimm(s) */
+ mccs->mccs_props.csp_dimmrank = rsltp->dimm[0].cslp->csl_rank;
}
/*
- * A placeholder for a future implementation that works this out from
- * smbios or SPD information. For now we will return a value that
- * can be tuned in /etc/system, and the default will cover current Sun systems.
- */
-/*ARGSUSED*/
-static int
-mc_config_quadranksupport(mc_t *mc)
-{
- return (mc_quadranksupport != 0);
-}
-
-/*
- * Create the DIMM structure for this MC. There are a number of unkowns,
- * such as the number of DIMM slots for this MC, the number of chip-select
- * ranks supported for each DIMM, how the slots are labelled etc.
- *
- * SMBIOS information can help with some of this (if the bios implementation is
- * complete and accurate, which is often not the case):
- *
- * . A record is required for each SMB_TYPE_MEMDEVICE slot, whether populated
- * or not. The record should reference the associated SMB_TYPE_MEMARRAY,
- * so we can figure out the number of slots for each MC. In practice some
- * smbios implementations attribute all slots (from multiple chips) to
- * a single memory array.
- *
- * . SMB_TYPE_MEMDEVICEMAP records indicate how a particular SMB_TYPE_MEMDEVICE
- * has been mapped. Some smbios implementation produce rubbish here, or get
- * confused when cs bank interleaving is enabled or disabled, but we can
- * perform some validation of the information before using it. The record
- * information is not well suited to handling cs bank interleaving since
- * it really only provides for a device to have a few contiguos mappings
- * and with cs interleave we have lots of little chunks interleaved across
- * the devices. If we assume that the bios has followed the BKDG algorithm
- * for setting up cs interleaving (which involves assinging contiguous
- * and adjacent ranges to the chip selects and then swapping some
- * base and mask hi and lo bits) then we can attempt to interpret the
- * DEVICEMAP records as being the addresses prior to swapping address/mask
- * bits to establish the interleave - that seems to cover at least some
- * smbios implementations. Even if that assumption appears good it is
- * also not clear which MEMDEVICE records correspond to LODIMMs and which
- * to UPDIMMs in a DIMM pair (128 bit MC mode) - we have to interpret the
- * Device Locator and Bank Locator labels.
- *
- * We also do not know how many chip-select banks reside on individual
- * DIMMs. For instance we cannot distinguish a system that supports 8
- * DIMMs slots per chip (one CS line each, thereby supporting only single-rank
- * DIMMs) vs a system that has just 4 slots per chip and which routes
- * 2 CS lines to each pair (thereby supporting dual rank DIMMs). In each
- * we would discover 8 active chip-selects.
- *
- * So the task of establishing the real DIMM configuration is complex, likely
- * requiring some combination of good SMBIOS data and perhaps our own access
- * to SPD information. Instead we opt for a canonical numbering scheme,
- * derived from the 'AMD Athlon (TM) 64 FX and AMD Opteron (TM) Processors
- * Motherboard Design Guide' (AMD publication #25180).
+ * mc_dimmlist_create is called after we have discovered all enabled
+ * (and spare or testfailed on revs F and G) chip-selects on the
+ * given memory controller. For each chip-select we must derive
+ * the associated dimms, remembering that a chip-select csbase/csmask
+ * pair may be associated with up to 2 chip-select lines (in 128 bit mode)
+ * and that any one dimm may be associated with 1, 2, or 4 chip-selects
+ * depending on whether it is single, dual or quadrank.
*/
static void
mc_dimmlist_create(mc_t *mc)
{
- int mcmode;
+ union mcreg_dramcfg_hi *drcfghip =
+ (union mcreg_dramcfg_hi *)(&mc->mc_cfgregs.mcr_dramcfghi);
+ mc_props_t *mcp = &mc->mc_props;
+ uint32_t rev = mcp->mcp_rev;
mc_cs_t *mccs;
- int quadrank = mc_config_quadranksupport(mc);
- uint_t dimm_nums[MC_CHIP_DIMMPERCS];
- int ldimmno; /* logical DIMM pair number, 0 .. 3 */
+ int r4 = 0, s4 = 0;
- mcmode = mc->mc_props.mcp_dramcfg & MC_DC_DCFG_128 ? 128 : 64;
+ /*
+ * Are we dealing with quadrank registered dimms?
+ *
+ * For socket 940 we can't tell and we'll assume we're not.
+ * This can be over-ridden by the admin in /etc/system by setting
+ * mc_quadranksupport nonzero. A possible optimisation in systems
+ * that export an SMBIOS table would be to count the number of
+ * dimm slots per cpu - more than 4 would indicate no quadrank support
+ * and 4 or fewer would indicate that if we see any of the upper
+ * chip-selects enabled then a quadrank dimm is present.
+ *
+ * For socket F(1207) we can check a bit in the dram config high reg.
+ *
+ * Other socket types do not support registered dimms.
+ */
+ if (mc->mc_socket == X86_SOCKET_940)
+ r4 = mc_quadranksupport != 0;
+ else if (mc->mc_socket == X86_SOCKET_F1207)
+ r4 = MCREG_FIELD_revFG(drcfghip, FourRankRDimm);
+
+ /*
+ * Are we dealing with quadrank SO-DIMMs? These are supported
+ * in AM2 and S1g1 packages only, but in all rev F/G cases we
+ * can detect their presence via a bit in the dram config high reg.
+ */
+ if (MC_REV_MATCH(rev, MC_REVS_FG))
+ s4 = MCREG_FIELD_revFG(drcfghip, FourRankSODimm);
for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) {
- if (quadrank) {
- /*
- * Quad-rank support. We assume that any of cs#
- * 4/5/6/6 that we have discovered active are routed
- * for quad rank support as described in the MB
- * design guide:
- * DIMM0: CS# 0, 1, 4 and 5
- * DIMM1: CS# 2, 3, 6 and 7
- */
- ldimmno = (mccs->mccs_num % 4) /2;
- } else {
- /*
- * DIMM0: CS# 0 and 1
- * DIMM1: CS# 2 and 3
- * DIMM2: CS# 4 and 5
- * DIMM3: CS# 6 and 7
- */
- ldimmno = mccs->mccs_num / 2;
- }
+ mcdcfg_rslt_t rslt;
- if (mcmode == 128) {
- /* 128-bit data width mode - dimms present in pairs */
- dimm_nums[0] = ldimmno * 2; /* LODIMM */
- dimm_nums[1] = ldimmno * 2 + 1; /* UPDIMM */
- } else {
- /* 64-bit data width mode - only even numbered dimms */
- dimm_nums[0] = ldimmno * 2; /* LODIMM */
- }
- mc_cs_dimmlist_create(mc, mccs, dimm_nums,
- mcmode == 128 ? 2 : 1);
+ if (mcdcfg_lookup(rev, mcp->mcp_mod64mux, mcp->mcp_accwidth,
+ mccs->mccs_props.csp_num, mc->mc_socket,
+ r4, s4, &rslt) < 0)
+ continue;
+
+ mc_csdimms_create(mc, mccs, &rslt);
}
}
static mc_cs_t *
-mc_cs_create(mc_t *mc, uint_t num, uint64_t base, uint64_t mask, size_t sz)
+mc_cs_create(mc_t *mc, uint_t num, uint64_t base, uint64_t mask, size_t sz,
+ int csbe, int spare, int testfail)
{
mc_cs_t *mccs = kmem_zalloc(sizeof (mc_cs_t), KM_SLEEP);
+ mccs_props_t *csp = &mccs->mccs_props;
+ int i;
mccs->mccs_hdr.mch_type = MC_NT_CS;
mccs->mccs_mc = mc;
- mccs->mccs_num = num;
- mccs->mccs_base = base;
- mccs->mccs_mask = mask;
- mccs->mccs_size = sz;
+ csp->csp_num = num;
+ csp->csp_base = base;
+ csp->csp_mask = mask;
+ csp->csp_size = sz;
+ csp->csp_csbe = csbe;
+ csp->csp_spare = spare;
+ csp->csp_testfail = testfail;
+
+ for (i = 0; i < MC_CHIP_DIMMPERCS; i++)
+ csp->csp_dimmnums[i] = MC_INVALNUM;
+
+ if (spare)
+ mc->mc_props.mcp_sparecs = num;
return (mccs);
}
/*
+ * For any cs# of this mc marked TestFail generate an ereport with
+ * resource identifying the associated dimm(s).
+ */
+static void
+mc_report_testfails(mc_t *mc)
+{
+ mc_unum_t unum;
+ mc_cs_t *mccs;
+ int i;
+
+ for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) {
+ if (mccs->mccs_props.csp_testfail) {
+ unum.unum_board = 0;
+ unum.unum_chip = mc->mc_chip->chip_id;
+ unum.unum_mc = 0;
+ unum.unum_cs = mccs->mccs_props.csp_num;
+ unum.unum_rank = mccs->mccs_props.csp_dimmrank;
+ unum.unum_offset = MCAMD_RC_INVALID_OFFSET;
+ for (i = 0; i < MC_CHIP_DIMMPERCS; i++)
+ unum.unum_dimms[i] = MC_INVALNUM;
+
+ mcamd_ereport_post(mc, FM_EREPORT_CPU_AMD_MC_TESTFAIL,
+ &unum,
+ FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_MC_TESTFAIL);
+ }
+ }
+}
+
+/*
* Function 1 Configuration - Address Map (see BKDG 3.4.4 DRAM Address Map)
*
* Read the Function 1 Address Map for each potential DRAM node. The Base
@@ -478,112 +544,268 @@ mc_cs_create(mc_t *mc, uint_t num, uint64_t base, uint64_t mask, size_t sz)
* within the [base, limit] range - this must match the pair number.
*/
static void
-mc_mkprops_addrmap(ddi_acc_handle_t cfghdl, mc_t *mc)
+mc_mkprops_addrmap(mc_pcicfg_hdl_t cfghdl, mc_t *mc)
{
- uint32_t base[MC_AM_REG_NODE_NUM], lim[MC_AM_REG_NODE_NUM];
+ union mcreg_drambase base[MC_AM_REG_NODE_NUM];
+ union mcreg_dramlimit lim[MC_AM_REG_NODE_NUM];
mc_props_t *mcp = &mc->mc_props;
+ mc_cfgregs_t *mcr = &mc->mc_cfgregs;
+ union mcreg_dramhole hole;
int i;
- mc_prop_read_pair(cfghdl, base, MC_AM_REG_DRAMBASE_0, lim,
- MC_AM_REG_DRAMLIM_0, MC_AM_REG_NODE_NUM, MC_AM_REG_DRAM_INCR);
+ mc_prop_read_pair(cfghdl,
+ (uint32_t *)base, MC_AM_REG_DRAMBASE_0, MC_AM_REG_NODE_NUM,
+ (uint32_t *)lim, MC_AM_REG_DRAMLIM_0, MC_AM_REG_NODE_NUM,
+ MC_AM_REG_DRAM_INCR);
for (i = 0; i < MC_AM_REG_NODE_NUM; i++) {
/*
* Don't create properties for empty nodes.
*/
- if ((lim[i] & MC_AM_DL_DRAMLIM_MASK) == 0)
+ if (MCREG_FIELD_CMN(&lim[i], DRAMLimiti) == 0)
continue;
/*
- * Don't create properties for DIMM ranges that aren't local
- * to this node.
+ * Skip all nodes but the one that matches the chip id
+ * for the mc currently being attached. Since the chip id
+ * is the HT id this loop and the preceding read of all
+ * base/limit pairs is overkill.
*/
- if ((lim[i] & MC_AM_DL_DSTNODE_MASK) != mc->mc_chip->chip_id)
+ if (MCREG_FIELD_CMN(&lim[i], DstNode) !=
+ mc->mc_chip->chip_id)
continue;
- mcp->mcp_base = MC_AM_DB_DRAMBASE(base[i]);
- mcp->mcp_lim = MC_AM_DL_DRAMLIM(lim[i]);
- mcp->mcp_ilen = (base[i] & MC_AM_DB_INTLVEN_MASK) >>
- MC_AM_DB_INTLVEN_SHIFT;
- mcp->mcp_ilsel = (lim[i] & MC_AM_DL_INTLVSEL_MASK) >>
- MC_AM_DL_INTLVSEL_SHIFT;
+ /*
+ * Stash raw register values for this node
+ */
+ mcr->mcr_drambase = MCREG_VAL32(&base[i]);
+ mcr->mcr_dramlimit = MCREG_VAL32(&lim[i]);
+
+ /*
+ * Derive some "cooked" properties
+ */
+ mcp->mcp_base = MC_DRAMBASE(&base[i]);
+ mcp->mcp_lim = MC_DRAMLIM(&lim[i]);
+ mcp->mcp_ilen = MCREG_FIELD_CMN(&base[i], IntlvEn);
+ mcp->mcp_ilsel = MCREG_FIELD_CMN(&lim[i], IntlvSel);
+
}
/*
* The Function 1 DRAM Hole Address Register tells us which node(s)
* own the DRAM space that is hoisted above 4GB, together with the
- * hole base and offset for this node.
+ * hole base and offset for this node. This was introduced in
+ * revision E.
*/
- mcp->mcp_dramhole = pci_config_get32(cfghdl, MC_AM_REG_HOLEADDR);
+ if (MC_REV_ATLEAST(mc->mc_props.mcp_rev, MC_REV_E)) {
+ MCREG_VAL32(&hole) =
+ mc_pcicfg_get32(cfghdl, MC_AM_REG_HOLEADDR);
+ mcr->mcr_dramhole = MCREG_VAL32(&hole);
+
+ if (MCREG_FIELD_CMN(&hole, DramHoleValid)) {
+ mcp->mcp_dramhole_size = MC_DRAMHOLE_SIZE(&hole);
+ }
+ }
+}
+
+/*
+ * Read some function 3 parameters via PCI Mechanism 1 accesses (which
+ * will serialize any NB accesses).
+ */
+static void
+mc_getmiscctl(mc_t *mc)
+{
+ uint32_t rev = mc->mc_props.mcp_rev;
+ union mcreg_nbcfg nbcfg;
+ union mcreg_sparectl sparectl;
+
+ mc->mc_cfgregs.mcr_nbcfg = MCREG_VAL32(&nbcfg) =
+ mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG);
+
+ if (MC_REV_MATCH(rev, MC_REVS_FG)) {
+ mc->mc_cfgregs.mcr_sparectl = MCREG_VAL32(&sparectl) =
+ mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL,
+ MC_CTL_REG_SPARECTL);
+
+ if (MCREG_FIELD_revFG(&sparectl, SwapDone)) {
+ mc->mc_props.mcp_badcs =
+ MCREG_FIELD_revFG(&sparectl, BadDramCs);
+ }
+ }
}
+static int
+csbasecmp(mc_cs_t **csapp, mc_cs_t **csbpp)
+{
+ uint64_t basea = (*csapp)->mccs_props.csp_base;
+ uint64_t baseb = (*csbpp)->mccs_props.csp_base;
+
+ if (basea == baseb)
+ return (0);
+ else if (basea < baseb)
+ return (-1);
+ else
+ return (1);
+}
+
+/*
+ * The following are for use in simulating TestFail for a chip-select
+ * without poking at the hardware (which tends to get upset if you do
+ * since the BIOS needs to restart to map a failed cs out). For internal
+ * testing only! Note that setting these does not give the full experience -
+ * the select chip-select *is* enabled and can give errors etc and the
+ * patounum logic will get confused.
+ */
+int testfail_mcnum = -1;
+int testfail_csnum = -1;
+
/*
* Function 2 configuration - DRAM Controller
*/
static void
-mc_mkprops_dramctl(ddi_acc_handle_t cfghdl, mc_t *mc)
+mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl, mc_t *mc)
{
- uint32_t base[MC_CHIP_NCS], mask[MC_CHIP_NCS];
- uint64_t dramcfg;
+ union mcreg_csbase base[MC_CHIP_NCS];
+ union mcreg_csmask mask[MC_CHIP_NCS];
+ union mcreg_dramcfg_lo drcfg_lo;
+ union mcreg_dramcfg_hi drcfg_hi;
+ union mcreg_drammisc drmisc;
+ union mcreg_bankaddrmap baddrmap;
mc_props_t *mcp = &mc->mc_props;
- int wide = 0; /* 128-bit access mode? */
+ mc_cfgregs_t *mcr = &mc->mc_cfgregs;
+ int maskdivisor;
+ int wide = 0;
+ uint32_t rev = mc->mc_props.mcp_rev;
int i;
mcamd_hdl_t hdl;
mcamd_mkhdl(&hdl); /* to call into common code */
/*
- * Read Function 2 DRAM Configuration High and Low registers and
- * weld them together into a 64-bit value. The High component
- * is mostly concerned with memory clocks etc and we'll not have
+ * Read Function 2 DRAM Configuration High and Low registers. The High
+ * part is mostly concerned with memory clocks etc and we'll not have
* any use for that. The Low component tells us if ECC is enabled,
* if we're in 64- or 128-bit MC mode, how the upper chip-selects
* are mapped, which chip-select pairs are using x4 parts, etc.
*/
- dramcfg = pci_config_get32(cfghdl, MC_DC_REG_DRAMCFGLO) |
- ((uint64_t)pci_config_get32(cfghdl, MC_DC_REG_DRAMCFGHI) << 32);
- wide = dramcfg & MC_DC_DCFG_128;
+ MCREG_VAL32(&drcfg_lo) = mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMCFGLO);
+ MCREG_VAL32(&drcfg_hi) = mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMCFGHI);
+ mcr->mcr_dramcfglo = MCREG_VAL32(&drcfg_lo);
+ mcr->mcr_dramcfghi = MCREG_VAL32(&drcfg_hi);
- mcp->mcp_dramcfg = dramcfg;
+ /*
+ * Note the DRAM controller width. The 64/128 bit is in a different
+ * bit position for revision F and G.
+ */
+ if (MC_REV_MATCH(rev, MC_REVS_FG)) {
+ wide = MCREG_FIELD_revFG(&drcfg_lo, Width128);
+ } else {
+ wide = MCREG_FIELD_preF(&drcfg_lo, Width128);
+ }
mcp->mcp_accwidth = wide ? 128 : 64;
/*
- * Read Function 2 DRAM Bank Address Mapping. This tells us
- * whether bank swizzle mode is enabled, and also encodes
- * the type of DIMM module in use for each chip-select pair.
+ * Read Function 2 DRAM Controller Miscellaenous Regsiter for those
+ * revs that support it. This include the Mod64Mux indication on
+ * these revs - for rev E it is in DRAM config low.
*/
- mcp->mcp_csbankmap = pci_config_get32(cfghdl, MC_DC_REG_BANKADDRMAP);
+ if (MC_REV_MATCH(rev, MC_REVS_FG)) {
+ mcr->mcr_drammisc = MCREG_VAL32(&drmisc) =
+ mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMMISC);
+ mcp->mcp_mod64mux = MCREG_FIELD_revFG(&drmisc, Mod64Mux);
+ } else if (MC_REV_MATCH(rev, MC_REV_E)) {
+ mcp->mcp_mod64mux = MCREG_FIELD_preF(&drcfg_lo, Mod64BitMux);
+ }
/*
- * Read Function 2 Configuration Registers for DRAM CS Base 0 thru 7
- * and DRAM CS Mask 0 thru 7. The Base registers give us the
- * BaseAddrHi and BaseAddrLo from which the base can be constructed,
- * and whether this chip-select bank is enabled (CSBE). The
- * Mask registers give us AddrMaskHi and AddrMaskLo from which
- * a full mask can be constructed.
+ * Read Function 2 DRAM Bank Address Mapping. This encodes the
+ * type of DIMM module in use for each chip-select pair.
+ * Prior ro revision F it also tells us whether BankSwizzle mode
+ * is enabled - in rev F that has moved to dram config hi register.
*/
- mc_prop_read_pair(cfghdl, base, MC_DC_REG_CSBASE_0, mask,
- MC_DC_REG_CSMASK_0, MC_CHIP_NCS, MC_DC_REG_CS_INCR);
+ mcp->mcp_csbankmapreg = MCREG_VAL32(&baddrmap) =
+ mc_pcicfg_get32(cfghdl, MC_DC_REG_BANKADDRMAP);
/*
- * Create a cs node for each enabled chip-select
+ * Determine whether bank swizzle mode is active. Bank swizzling was
+ * introduced as an option in rev E, but the bit that indicates it
+ * is enabled has moved in revs F/G.
+ */
+ if (MC_REV_MATCH(rev, MC_REV_E)) {
+ mcp->mcp_bnkswzl = MCREG_FIELD_preF(&baddrmap, BankSwizzleMode);
+ } else if (MC_REV_MATCH(rev, MC_REVS_FG)) {
+ mcp->mcp_bnkswzl = MCREG_FIELD_revFG(&drcfg_hi,
+ BankSwizzleMode);
+ }
+
+ /*
+ * Read the DRAM CS Base and DRAM CS Mask registers. Revisions prior
+ * to F have an equal number of base and mask registers; revision F
+ * has twice as many base registers as masks.
+ */
+ maskdivisor = MC_REV_MATCH(rev, MC_REVS_FG) ? 2 : 1;
+
+ mc_prop_read_pair(cfghdl,
+ (uint32_t *)base, MC_DC_REG_CSBASE_0, MC_CHIP_NCS,
+ (uint32_t *)mask, MC_DC_REG_CSMASK_0, MC_CHIP_NCS / maskdivisor,
+ MC_DC_REG_CS_INCR);
+
+ /*
+ * Create a cs node for each enabled chip-select as well as
+ * any appointed online spare chip-selects and for any that have
+ * failed test.
*/
for (i = 0; i < MC_CHIP_NCS; i++) {
mc_cs_t *mccs;
- uint64_t csmask;
+ uint64_t csbase, csmask;
size_t sz;
+ int csbe, spare, testfail;
- if (!(base[i] & MC_DC_CSB_CSBE)) {
- mcp->mcp_disabled_cs++;
- continue;
+ if (MC_REV_MATCH(rev, MC_REVS_FG)) {
+ csbe = MCREG_FIELD_revFG(&base[i], CSEnable);
+ spare = MCREG_FIELD_revFG(&base[i], Spare);
+ testfail = MCREG_FIELD_revFG(&base[i], TestFail);
+ } else {
+ csbe = MCREG_FIELD_preF(&base[i], CSEnable);
+ spare = 0;
+ testfail = 0;
}
- if (mcamd_cs_size(&hdl, (mcamd_node_t *)mc, i, &sz) < 0)
+ /* Testing hook */
+ if (testfail_mcnum != -1 && testfail_csnum != -1 &&
+ mcp->mcp_num == testfail_mcnum && i == testfail_csnum) {
+ csbe = spare = 0;
+ testfail = 1;
+ cmn_err(CE_NOTE, "Pretending MC %d CS %d failed test",
+ testfail_mcnum, testfail_csnum);
+ }
+
+ /*
+ * If the chip-select is not enabled then skip it unless
+ * it is a designated online spare or is marked with TestFail.
+ */
+ if (!csbe && !(spare || testfail))
continue;
- csmask = MC_DC_CSM_CSMASK(mask[i]);
- mccs = mc_cs_create(mc, i, MC_DC_CSB_CSBASE(base[i]), csmask,
- sz);
+ /*
+ * For an enabled or spare chip-select the Bank Address Mapping
+ * register will be valid as will the chip-select mask. The
+ * base will not be valid but we'll read and store it anyway.
+ * We will not know whether the spare is already swapped in
+ * until MC function 3 attaches.
+ */
+ if (csbe || spare) {
+ if (mcamd_cs_size(&hdl, (mcamd_node_t *)mc, i, &sz) < 0)
+ continue;
+ csbase = MC_CSBASE(&base[i], rev);
+ csmask = MC_CSMASK(&mask[i / maskdivisor], rev);
+ } else {
+ sz = 0;
+ csbase = csmask = 0;
+ }
+
+ mccs = mc_cs_create(mc, i, csbase, csmask, sz,
+ csbe, spare, testfail);
if (mc->mc_cslist == NULL)
mc->mc_cslist = mccs;
@@ -591,45 +813,91 @@ mc_mkprops_dramctl(ddi_acc_handle_t cfghdl, mc_t *mc)
mc->mc_cslast->mccs_next = mccs;
mc->mc_cslast = mccs;
+ mccs->mccs_cfgregs.csr_csbase = MCREG_VAL32(&base[i]);
+ mccs->mccs_cfgregs.csr_csmask =
+ MCREG_VAL32(&mask[i / maskdivisor]);
+
/*
* Check for cs bank interleaving - some bits clear in the
* lower mask. All banks must/will have the same lomask bits
* if cs interleaving is active.
*/
- if (!mcp->mcp_csbank_intlv) {
+ if (csbe && !mcp->mcp_csintlvfctr) {
int bitno, ibits = 0;
- for (bitno = MC_DC_CSM_MASKLO_LOBIT;
- bitno <= MC_DC_CSM_MASKLO_HIBIT; bitno++) {
+ for (bitno = MC_CSMASKLO_LOBIT(rev);
+ bitno <= MC_CSMASKLO_HIBIT(rev); bitno++) {
if (!(csmask & (1 << bitno)))
ibits++;
}
- if (ibits > 0)
- mcp->mcp_csbank_intlv = 1 << ibits;
+ mcp->mcp_csintlvfctr = 1 << ibits;
+ }
+ }
+
+ /*
+ * If there is no chip-select interleave on this node determine
+ * whether the chip-select ranks are contiguous or if there
+ * is a hole.
+ */
+ if (mcp->mcp_csintlvfctr == 1) {
+ mc_cs_t *csp[MC_CHIP_NCS];
+ mc_cs_t *mccs;
+ int ncsbe = 0;
+
+ for (mccs = mc->mc_cslist; mccs != NULL;
+ mccs = mccs->mccs_next) {
+ if (mccs->mccs_props.csp_csbe)
+ csp[ncsbe++] = mccs;
+ }
+
+ if (ncsbe != 0) {
+ qsort((void *)csp, ncsbe, sizeof (mc_cs_t *),
+ (int (*)(const void *, const void *))csbasecmp);
+
+ for (i = 1; i < ncsbe; i++) {
+ if (csp[i]->mccs_props.csp_base !=
+ csp[i - 1]->mccs_props.csp_base +
+ csp[i - 1]->mccs_props.csp_size)
+ mc->mc_csdiscontig = 1;
+ }
}
}
+
/*
- * Now that we have discovered all active chip-selects we attempt
- * to divine the associated DIMM configuration.
+ * Since we do not attach to MC function 3 go ahead and read some
+ * config parameters from it now.
+ */
+ mc_getmiscctl(mc);
+
+ /*
+ * Now that we have discovered all enabled/spare/testfail chip-selects
+ * we divine the associated DIMM configuration.
*/
mc_dimmlist_create(mc);
}
typedef struct mc_bind_map {
- const char *bm_bindnm; /* attachment binding name */
- uint_t bm_func; /* PCI config space function number for bind */
- const char *bm_model; /* value for device node model property */
- void (*bm_mkprops)(ddi_acc_handle_t, mc_t *);
+ const char *bm_bindnm; /* attachment binding name */
+ enum mc_funcnum bm_func; /* PCI config space function number for bind */
+ const char *bm_model; /* value for device node model property */
+ void (*bm_mkprops)(mc_pcicfg_hdl_t, mc_t *);
} mc_bind_map_t;
+/*
+ * Do not attach to MC function 3 - agpgart already attaches to that.
+ * Function 3 may be a good candidate for a nexus driver to fan it out
+ * into virtual devices by functionality. We will use pci_mech1_getl
+ * to retrieve the function 3 parameters we require.
+ */
+
static const mc_bind_map_t mc_bind_map[] = {
{ MC_FUNC_HTCONFIG_BINDNM, MC_FUNC_HTCONFIG,
- "AMD Memory Controller (HT Configuration)", NULL },
+ "AMD Memory Controller (HT Configuration)", NULL },
{ MC_FUNC_ADDRMAP_BINDNM, MC_FUNC_ADDRMAP,
- "AMD Memory Controller (Address Map)", mc_mkprops_addrmap },
+ "AMD Memory Controller (Address Map)", mc_mkprops_addrmap },
{ MC_FUNC_DRAMCTL_BINDNM, MC_FUNC_DRAMCTL,
- "AMD Memory Controller (DRAM Controller & HT Trace)",
- mc_mkprops_dramctl },
+ "AMD Memory Controller (DRAM Controller & HT Trace)",
+ mc_mkprops_dramctl },
NULL
};
@@ -657,6 +925,106 @@ mc_close(dev_t dev, int flag, int otyp, cred_t *credp)
return (0);
}
+/*
+ * Enable swap from chip-select csnum to the spare chip-select on this
+ * memory controller (if any).
+ */
+
+int mc_swapdonetime = 30; /* max number of seconds to wait for SwapDone */
+
+static int
+mc_onlinespare(mc_t *mc, int csnum)
+{
+ mc_props_t *mcp = &mc->mc_props;
+ union mcreg_sparectl sparectl;
+ union mcreg_scrubctl scrubctl;
+ mc_cs_t *mccs;
+ hrtime_t tmax;
+ int i = 0;
+
+ ASSERT(RW_WRITE_HELD(&mc_lock));
+
+ if (!MC_REV_MATCH(mcp->mcp_rev, MC_REVS_FG))
+ return (ENOTSUP); /* MC rev does not offer online spare */
+ else if (mcp->mcp_sparecs == MC_INVALNUM)
+ return (ENODEV); /* Supported, but no spare configured */
+ else if (mcp->mcp_badcs != MC_INVALNUM)
+ return (EBUSY); /* Spare already swapped in */
+ else if (csnum == mcp->mcp_sparecs)
+ return (EINVAL); /* Can't spare the spare! */
+
+ for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) {
+ if (mccs->mccs_props.csp_num == csnum)
+ break;
+ }
+ if (mccs == NULL)
+ return (EINVAL); /* nominated bad CS does not exist */
+
+ /*
+ * If the DRAM Scrubber is not enabled then the swap cannot succeed.
+ */
+ MCREG_VAL32(&scrubctl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL,
+ MC_CTL_REG_SCRUBCTL);
+ if (MCREG_FIELD_CMN(&scrubctl, DramScrub) == 0)
+ return (ENODEV); /* DRAM scrubber not enabled */
+
+ /*
+ * Read Online Spare Comtrol Register again, just in case our
+ * state does not reflect reality.
+ */
+ MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL,
+ MC_CTL_REG_SPARECTL);
+
+ if (MCREG_FIELD_revFG(&sparectl, SwapDone))
+ return (EBUSY);
+
+ /* Write to the BadDramCs field */
+ MCREG_FIELD_revFG(&sparectl, BadDramCs) = csnum;
+ mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL,
+ MCREG_VAL32(&sparectl));
+
+ /* And request that the swap to the spare start */
+ MCREG_FIELD_revFG(&sparectl, SwapEn) = 1;
+ mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL,
+ MCREG_VAL32(&sparectl));
+
+ /*
+ * Poll for SwapDone - we have disabled notification by interrupt.
+ * Swap takes "several CPU cycles, depending on the DRAM speed, but
+ * is performed in the background" (Family 0Fh Bios Porting Guide).
+ * We're in a slow ioctl path so there is no harm in waiting around
+ * a bit - consumers of the ioctl must be aware that it may take
+ * a moment. We will poll for up to mc_swapdonetime seconds,
+ * limiting that to 120s.
+ *
+ * The swap is performed by the DRAM scrubber (which must be enabled)
+ * whose scrub rate is accelerated for the duration of the swap.
+ * The maximum swap rate is 40.0ns per 64 bytes, so the maximum
+ * supported cs size of 16GB would take 10.7s at that max rate
+ * of 25000000 scrubs/second.
+ */
+ tmax = gethrtime() + MIN(mc_swapdonetime, 120) * 1000000000ULL;
+ do {
+ if (i++ < 20)
+ delay(drv_usectohz(100000)); /* 0.1s for up to 2s */
+ else
+ delay(drv_usectohz(500000)); /* 0.5s */
+
+ MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc,
+ MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
+ } while (!MCREG_FIELD_revFG(&sparectl, SwapDone) &&
+ gethrtime() < tmax);
+
+ if (!MCREG_FIELD_revFG(&sparectl, SwapDone))
+ return (ETIME); /* Operation timed out */
+
+ mcp->mcp_badcs = csnum;
+ mc->mc_cfgregs.mcr_sparectl = MCREG_VAL32(&sparectl);
+ mc->mc_spareswaptime = gethrtime();
+
+ return (0);
+}
+
/*ARGSUSED*/
static int
mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
@@ -664,7 +1032,8 @@ mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
int rc = 0;
mc_t *mc;
- if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT)
+ if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT &&
+ cmd != MC_IOC_ONLINESPARE_EN)
return (EINVAL);
rw_enter(&mc_lock, RW_READER);
@@ -674,15 +1043,15 @@ mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
return (EINVAL);
}
- if (mc_snapshot_update(mc) < 0) {
- rw_exit(&mc_lock);
- return (EIO);
- }
-
switch (cmd) {
case MC_IOC_SNAPSHOT_INFO: {
mc_snapshot_info_t mcs;
+ if (mc_snapshot_update(mc) < 0) {
+ rw_exit(&mc_lock);
+ return (EIO);
+ }
+
mcs.mcs_size = mc->mc_snapshotsz;
mcs.mcs_gen = mc->mc_snapshotgen;
@@ -693,10 +1062,34 @@ mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
}
case MC_IOC_SNAPSHOT:
+ if (mc_snapshot_update(mc) < 0) {
+ rw_exit(&mc_lock);
+ return (EIO);
+ }
+
if (ddi_copyout(mc->mc_snapshot, (void *)arg, mc->mc_snapshotsz,
mode) < 0)
rc = EFAULT;
break;
+
+ case MC_IOC_ONLINESPARE_EN:
+ if (drv_priv(credp) != 0) {
+ rw_exit(&mc_lock);
+ return (EPERM);
+ }
+
+ if (!rw_tryupgrade(&mc_lock)) {
+ rw_exit(&mc_lock);
+ return (EAGAIN);
+ }
+
+ if ((rc = mc_onlinespare(mc, (int)arg)) == 0) {
+ mc_snapshot_destroy(mc);
+ nvlist_free(mc->mc_nvl);
+ mc->mc_nvl = mc_nvl_create(mc);
+ }
+
+ break;
}
rw_exit(&mc_lock);
@@ -780,36 +1173,51 @@ static mc_t *
mc_create(chipid_t chipid)
{
chip_t *chp = chip_lookup(chipid);
- const mc_rev_map_t *rmp = mc_revision(chp);
mc_t *mc;
ASSERT(RW_WRITE_HELD(&mc_lock));
- if (chp == NULL || rmp->rm_rev == MC_REV_UNKNOWN)
+ if (chp == NULL)
return (NULL);
mc = kmem_zalloc(sizeof (mc_t), KM_SLEEP);
mc->mc_hdr.mch_type = MC_NT_MC;
mc->mc_chip = chp;
- mc->mc_props.mcp_rev = rmp->rm_rev;
- mc->mc_revname = rmp->rm_name;
mc->mc_props.mcp_num = mc->mc_chip->chip_id;
+ mc->mc_props.mcp_sparecs = MC_INVALNUM;
+ mc->mc_props.mcp_badcs = MC_INVALNUM;
- mc->mc_next = mc_list;
- mc_list = mc;
+ /*
+ * We can use the first cpu in the chip_cpus list since all cores
+ * of a chip share the same revision and socket type.
+ */
+ mc->mc_props.mcp_rev = cpuid_getchiprev(chp->chip_cpus);
+ mc->mc_revname = cpuid_getchiprevstr(chp->chip_cpus);
+ mc->mc_socket = cpuid_getsockettype(chp->chip_cpus);
+
+ if (mc_list == NULL)
+ mc_list = mc;
+ if (mc_last != NULL)
+ mc_last->mc_next = mc;
+
+ mc->mc_next = NULL;
+ mc_last = mc;
return (mc);
}
+static int mc_sw_scrub_disabled = 0;
+
static int
mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
- ddi_acc_handle_t hdl;
+ mc_pcicfg_hdl_t cfghdl;
const mc_bind_map_t *bm;
const char *bindnm;
char *unitstr = NULL;
+ enum mc_funcnum func;
long unitaddr;
- int chipid, func, rc;
+ int chipid, rc;
cpu_t *cpu;
mc_t *mc;
@@ -840,13 +1248,14 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
rc = ddi_strtol(unitstr, NULL, 16, &unitaddr);
ASSERT(rc == 0 && unitaddr >= MC_AMD_DEV_OFFSET);
- ddi_prop_free(unitstr);
if (rc != 0 || unitaddr < MC_AMD_DEV_OFFSET) {
cmn_err(CE_WARN, "failed to parse unit address %s for %s\n",
unitstr, bindnm);
+ ddi_prop_free(unitstr);
return (DDI_FAILURE);
}
+ ddi_prop_free(unitstr);
chipid = unitaddr - MC_AMD_DEV_OFFSET;
@@ -899,17 +1308,19 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
dip, "chip-id", mc->mc_chip->chip_id);
if (bm->bm_mkprops != NULL &&
- pci_config_setup(dip, &hdl) == DDI_SUCCESS) {
- bm->bm_mkprops(hdl, mc);
- pci_config_teardown(&hdl);
+ mc_pcicfg_setup(mc, bm->bm_func, &cfghdl) == DDI_SUCCESS) {
+ bm->bm_mkprops(cfghdl, mc);
+ mc_pcicfg_teardown(cfghdl);
}
/*
* If this is the last node to be attached for this memory controller,
- * so create the minor node and set up the properties.
+ * then create the minor node, enable scrubbers, and register with
+ * cpu module(s) for this chip.
*/
if (func == MC_FUNC_DEVIMAP) {
mc_props_t *mcp = &mc->mc_props;
+ int dram_present = 0;
if (ddi_create_minor_node(dip, "mc-amd", S_IFCHR,
mc->mc_chip->chip_id, "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
@@ -919,31 +1330,73 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
/*
* Register the memory controller for every CPU of this chip.
- * Then attempt to enable h/w memory scrubbers for this node.
- * If we are successful, disable the software memory scrubber.
+ *
+ * If there is memory present on this node and ECC is enabled
+ * attempt to enable h/w memory scrubbers for this node.
+ * Note that if the generic cpu module has loaded then this
+ * will have no effect and cmi_scrubber_enable will return
+ * 0. If we are successful in enabling the hardware scrubbers,
+ * disable the software memory scrubber.
*/
- mutex_enter(&cpu_lock);
+ kpreempt_disable(); /* prevent cpu list from changing */
cpu = mc->mc_chip->chip_cpus;
- if (mc->mc_props.mcp_lim != mc->mc_props.mcp_base) {
- rc = cmi_scrubber_enable(cpu, mcp->mcp_base,
- mcp->mcp_ilen);
- } else {
- rc = 0;
- }
-
do {
mcamd_mc_register(cpu);
cpu = cpu->cpu_next_chip;
} while (cpu != mc->mc_chip->chip_cpus);
- mutex_exit(&cpu_lock);
- if (rc)
+ if (mc->mc_props.mcp_lim != mc->mc_props.mcp_base) {
+ /*
+ * This node may map non-dram memory alone, so we
+ * must check for an enabled chip-select to be
+ * sure there is dram present.
+ */
+ mc_cs_t *mccs;
+
+ for (mccs = mc->mc_cslist; mccs != NULL;
+ mccs = mccs->mccs_next) {
+ if (mccs->mccs_props.csp_csbe) {
+ dram_present = 1;
+ break;
+ }
+ }
+ }
+
+ if (dram_present && !mc_ecc_enabled(mc)) {
+ /*
+ * On a single chip system there is no point in
+ * scrubbing if there is no ECC on the single node.
+ * On a multichip system, necessarily Opteron using
+ * registered ECC-capable DIMMs, if there is memory
+ * present on a node but no ECC there then we'll assume
+ * ECC is disabled for all nodes and we will not enable
+ * the scrubber and wll also disable the software
+ * memscrub thread.
+ */
+ rc = 1;
+ } else if (!dram_present) {
+ /* No memory on this node - others decide memscrub */
+ rc = 0;
+ } else {
+ /* There is memory on this node and ECC is enabled */
+ rc = cmi_scrubber_enable(cpu, mcp->mcp_base,
+ mcp->mcp_ilen, mc->mc_csdiscontig);
+ }
+
+ kpreempt_enable();
+
+ if (rc && !mc_sw_scrub_disabled++)
memscrub_disable();
+
+ mc_report_testfails(mc);
}
+ /*
+ * Update nvlist for as far as we have gotten in attach/init.
+ */
nvlist_free(mc->mc_nvl);
mc->mc_nvl = mc_nvl_create(mc);
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_off.in b/usr/src/uts/i86pc/io/mc/mcamd_off.in
index 8e6a8dbc5e..0be05a685f 100644
--- a/usr/src/uts/i86pc/io/mc/mcamd_off.in
+++ b/usr/src/uts/i86pc/io/mc/mcamd_off.in
@@ -2,9 +2,8 @@
\ 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.
+\ 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.
@@ -34,22 +33,36 @@ mc_t
mc_props.mcp_rev MCAMD_MC_OFF_REV
mc_props.mcp_base MCAMD_MC_OFF_BASE_ADDR
mc_props.mcp_lim MCAMD_MC_OFF_LIM_ADDR
- mc_props.mcp_dramcfg MCAMD_MC_OFF_DRAMCFG
- mc_props.mcp_dramhole MCAMD_MC_OFF_DRAMHOLE
- mc_props.mcp_ilen MCAMD_MC_OFF_DRAM_ILEN
- mc_props.mcp_ilsel MCAMD_MC_OFF_DRAM_ILSEL
- mc_props.mcp_csbankmap MCAMD_MC_OFF_CSBANKMAP
+ mc_props.mcp_ilen MCAMD_MC_OFF_ILEN
+ mc_props.mcp_ilsel MCAMD_MC_OFF_ILSEL
+ mc_props.mcp_csintlvfctr MCAMD_MC_OFF_CSINTLVFCTR
+ mc_props.mcp_dramhole_size MCAMD_MC_OFF_DRAMHOLE_SIZE
mc_props.mcp_accwidth MCAMD_MC_OFF_ACCWIDTH
- mc_props.mcp_csbank_intlv MCAMD_MC_OFF_CSBANK_INTLV
- mc_props.mcp_disabled_cs MCAMD_MC_OFF_DISABLED_CS
+ mc_props.mcp_csbankmapreg MCAMD_MC_OFF_CSBANKMAPREG
+ mc_props.mcp_bnkswzl MCAMD_MC_OFF_BNKSWZL
+ mc_props.mcp_mod64mux MCAMD_MC_OFF_MOD64MUX
+ mc_props.mcp_sparecs MCAMD_MC_OFF_SPARECS
+ mc_props.mcp_badcs MCAMD_MC_OFF_BADCS
+ mc_cfgregs.mcr_drambase MCAMD_MC_OFF_DRAMBASE_REG
+ mc_cfgregs.mcr_dramlimit MCAMD_MC_OFF_DRAMLIMIT_REG
+ mc_cfgregs.mcr_dramhole MCAMD_MC_OFF_DRAMHOLE_REG
+ mc_cfgregs.mcr_dramcfglo MCAMD_MC_OFF_DRAMCFGLO_REG
+ mc_cfgregs.mcr_dramcfghi MCAMD_MC_OFF_DRAMCFGHI_REG
mc_cs_t
- mccs_num MCAMD_CS_OFF_NUM
- mccs_base MCAMD_CS_OFF_BASE_ADDR
- mccs_mask MCAMD_CS_OFF_MASK
- mccs_size MCAMD_CS_OFF_SIZE
- mccs_dimmnums MCAMD_CS_OFF_DIMMNUMS
+ mccs_props.csp_num MCAMD_CS_OFF_NUM
+ mccs_props.csp_base MCAMD_CS_OFF_BASE_ADDR
+ mccs_props.csp_mask MCAMD_CS_OFF_MASK
+ mccs_props.csp_size MCAMD_CS_OFF_SIZE
+ mccs_props.csp_csbe MCAMD_CS_OFF_CSBE
+ mccs_props.csp_spare MCAMD_CS_OFF_SPARE
+ mccs_props.csp_testfail MCAMD_CS_OFF_TESTFAIL
+ mccs_props.csp_dimmnums MCAMD_CS_OFF_DIMMNUMS
+ mccs_props.csp_dimmrank MCAMD_CS_OFF_DIMMRANK
+ mccs_cfgregs.csr_csbase MCAMD_CS_OFF_CSBASE_REG
+ mccs_cfgregs.csr_csmask MCAMD_CS_OFF_CSMASK_REG
mc_dimm_t
mcd_num MCAMD_DIMM_OFF_NUM
+ mcd_size MCAMD_DIMM_OFF_SIZE
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.c b/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.c
new file mode 100644
index 0000000000..534666769a
--- /dev/null
+++ b/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.c
@@ -0,0 +1,99 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#include <mcamd_pcicfg.h>
+#include <sys/pci_cfgspace_impl.h>
+
+struct _mc_pcicfg_hdl {
+ mc_t *cfh_mc;
+ enum mc_funcnum cfh_func;
+ ddi_acc_handle_t cfh_hdl;
+};
+
+static int
+mccfgsetup(struct _mc_pcicfg_hdl *hdlp, mc_t *mc, enum mc_funcnum func)
+{
+ hdlp->cfh_mc = mc;
+ hdlp->cfh_func = func;
+
+ if (pci_config_setup(mc->mc_funcs[func].mcf_devi, &hdlp->cfh_hdl) !=
+ DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ return (DDI_SUCCESS);
+}
+
+int
+mc_pcicfg_setup(mc_t *mc, enum mc_funcnum func, mc_pcicfg_hdl_t *cookiep)
+{
+ struct _mc_pcicfg_hdl *hdlp;
+
+ *cookiep = hdlp = kmem_alloc(sizeof (struct _mc_pcicfg_hdl), KM_SLEEP);
+
+ if (mccfgsetup(hdlp, mc, func) == DDI_FAILURE) {
+ kmem_free(hdlp, sizeof (*hdlp));
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+void
+mc_pcicfg_teardown(mc_pcicfg_hdl_t cookie)
+{
+ struct _mc_pcicfg_hdl *hdlp = cookie;
+
+ pci_config_teardown(&hdlp->cfh_hdl);
+ kmem_free(hdlp, sizeof (*hdlp));
+}
+
+uint32_t
+mc_pcicfg_get32(mc_pcicfg_hdl_t cookie, off_t offset)
+{
+ struct _mc_pcicfg_hdl *hdlp = cookie;
+
+ return (pci_config_get32(hdlp->cfh_hdl, offset));
+}
+
+uint32_t
+mc_pcicfg_get32_nohdl(mc_t *mc, enum mc_funcnum func, off_t offset)
+{
+ return (pci_mech1_getl(0, MC_AMD_DEV_OFFSET + mc->mc_chip->chip_id,
+ func, offset));
+}
+
+void
+mc_pcicfg_put32_nohdl(mc_t *mc, enum mc_funcnum func, off_t offset,
+ uint32_t val)
+{
+ pci_mech1_putl(0, MC_AMD_DEV_OFFSET + mc->mc_chip->chip_id,
+ func, offset, val);
+}
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.h b/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.h
new file mode 100644
index 0000000000..65ab6cdb8e
--- /dev/null
+++ b/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.h
@@ -0,0 +1,56 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MCAMD_PCICFG_H
+#define _MCAMD_PCICFG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#include <mcamd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void * mc_pcicfg_hdl_t;
+
+/* MC PCI config where we have attached to an MC dev/function */
+extern int mc_pcicfg_setup(mc_t *, enum mc_funcnum, mc_pcicfg_hdl_t *);
+extern void mc_pcicfg_teardown(mc_pcicfg_hdl_t);
+extern uint32_t mc_pcicfg_get32(mc_pcicfg_hdl_t, off_t);
+
+/* MC PCI config where we have not attached to the dev/function */
+extern uint32_t mc_pcicfg_get32_nohdl(mc_t *, enum mc_funcnum, off_t);
+extern void mc_pcicfg_put32_nohdl(mc_t *, enum mc_funcnum, off_t, uint32_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MCAMD_PCICFG_H */
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_subr.c b/usr/src/uts/i86pc/io/mc/mcamd_subr.c
index 4537de771b..4b4fccf4ec 100644
--- a/usr/src/uts/i86pc/io/mc/mcamd_subr.c
+++ b/usr/src/uts/i86pc/io/mc/mcamd_subr.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -37,6 +36,8 @@
#include <sys/sunddi.h>
#include <sys/varargs.h>
#include <sys/cpu_module_impl.h>
+#include <sys/fm/util.h>
+#include <sys/fm/cpu/AMD.h>
#include <sys/fm/protocol.h>
#include <sys/mc.h>
@@ -45,9 +46,9 @@
int mcamd_debug = 0; /* see mcamd_api.h for MCAMD_DBG_* values */
-struct mc_propmap {
- uint_t mcpm_code;
- uint_t mcpm_offset;
+struct mc_offmap {
+ int mcom_code;
+ uint_t mcom_offset;
};
static uint_t
@@ -144,7 +145,7 @@ mcamd_cs_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last)
/*
* Iterate over all DIMMs of an MC or all DIMMs of a chip-select depending
- * on the node type of 'node'. In the chip-select case we don not have
+ * on the node type of 'node'. In the chip-select case we do not have
* a linked list of associated DIMMs but an array of pointers to them.
*/
/*ARGSUSED*/
@@ -164,7 +165,7 @@ mcamd_dimm_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last)
switch (nt) {
case MC_NT_MC:
mc = node2type(node, MC_NT_MC);
- retval = mc->mc_props.mcp_dimmlist;
+ retval = mc->mc_dimmlist;
break;
case MC_NT_CS:
mccs = node2type(node, MC_NT_CS);
@@ -223,76 +224,175 @@ mcamd_dimm_mc(mcamd_hdl_t *hdl, mcamd_node_t *dnode)
* property lookup does not have to be super-fast - we search linearly
* down the (small) lists.
*/
-static const struct mc_propmap mcamd_mc_propmap[] = {
+static const struct mc_offmap mcamd_mc_offmap[] = {
{ MCAMD_PROP_NUM, MCAMD_MC_OFF_NUM },
{ MCAMD_PROP_REV, MCAMD_MC_OFF_REV },
{ MCAMD_PROP_BASE_ADDR, MCAMD_MC_OFF_BASE_ADDR },
{ MCAMD_PROP_LIM_ADDR, MCAMD_MC_OFF_LIM_ADDR },
- { MCAMD_PROP_DRAM_CONFIG, MCAMD_MC_OFF_DRAMCFG },
- { MCAMD_PROP_DRAM_HOLE, MCAMD_MC_OFF_DRAMHOLE },
- { MCAMD_PROP_DRAM_ILEN, MCAMD_MC_OFF_DRAM_ILEN },
- { MCAMD_PROP_DRAM_ILSEL, MCAMD_MC_OFF_DRAM_ILSEL },
- { MCAMD_PROP_CSBANKMAP, MCAMD_MC_OFF_CSBANKMAP },
+ { MCAMD_PROP_ILEN, MCAMD_MC_OFF_ILEN },
+ { MCAMD_PROP_ILSEL, MCAMD_MC_OFF_ILSEL },
+ { MCAMD_PROP_CSINTLVFCTR, MCAMD_MC_OFF_CSINTLVFCTR },
+ { MCAMD_PROP_DRAMHOLE_SIZE, MCAMD_MC_OFF_DRAMHOLE_SIZE },
{ MCAMD_PROP_ACCESS_WIDTH, MCAMD_MC_OFF_ACCWIDTH },
- { MCAMD_PROP_CSBANK_INTLV, MCAMD_MC_OFF_CSBANK_INTLV },
- { MCAMD_PROP_DISABLED_CS, MCAMD_MC_OFF_DISABLED_CS }
+ { MCAMD_PROP_CSBANKMAPREG, MCAMD_MC_OFF_CSBANKMAPREG },
+ { MCAMD_PROP_BANKSWZL, MCAMD_MC_OFF_BNKSWZL },
+ { MCAMD_PROP_MOD64MUX, MCAMD_MC_OFF_MOD64MUX },
+ { MCAMD_PROP_SPARECS, MCAMD_MC_OFF_SPARECS },
+ { MCAMD_PROP_BADCS, MCAMD_MC_OFF_BADCS },
};
-static const struct mc_propmap mcamd_cs_propmap[] = {
+static const struct mc_offmap mcamd_cs_offmap[] = {
{ MCAMD_PROP_NUM, MCAMD_CS_OFF_NUM },
{ MCAMD_PROP_BASE_ADDR, MCAMD_CS_OFF_BASE_ADDR },
{ MCAMD_PROP_MASK, MCAMD_CS_OFF_MASK },
{ MCAMD_PROP_SIZE, MCAMD_CS_OFF_SIZE },
- { MCAMD_PROP_LODIMM, MCAMD_CS_OFF_DIMMNUMS },
- { MCAMD_PROP_UPDIMM, MCAMD_CS_OFF_DIMMNUMS +
- MCAMD_CS_OFF_DIMMNUMS_INCR }
+ { MCAMD_PROP_CSBE, MCAMD_CS_OFF_CSBE },
+ { MCAMD_PROP_SPARE, MCAMD_CS_OFF_SPARE },
+ { MCAMD_PROP_TESTFAIL, MCAMD_CS_OFF_TESTFAIL },
+ { MCAMD_PROP_CSDIMM1, MCAMD_CS_OFF_DIMMNUMS },
+ { MCAMD_PROP_CSDIMM2, MCAMD_CS_OFF_DIMMNUMS +
+ MCAMD_CS_OFF_DIMMNUMS_INCR },
+ { MCAMD_PROP_DIMMRANK, MCAMD_CS_OFF_DIMMRANK },
};
-static const struct mc_propmap mcamd_dimm_propmap[] = {
+static const struct mc_offmap mcamd_dimm_offmap[] = {
{ MCAMD_PROP_NUM, MCAMD_DIMM_OFF_NUM },
+ { MCAMD_PROP_SIZE, MCAMD_DIMM_OFF_SIZE },
+};
+
+struct nt_offmap {
+ const struct mc_offmap *omp;
+ int mapents;
};
/*ARGSUSED*/
-int
-mcamd_get_numprop(mcamd_hdl_t *hdl, mcamd_node_t *node, uint_t code,
- uint64_t *valp)
+static int
+findoffset(mcamd_hdl_t *hdl, mcamd_node_t *node, struct nt_offmap *arr,
+ int code, uint_t *offset)
{
int i;
mc_hdr_t *mch = (mc_hdr_t *)node;
int nt = mch->mch_type;
- int found = 0;
- const struct mc_propmap *pmp;
- struct mcamd_nt_props {
- const struct mc_propmap *props;
- int numprops;
- } props[] = {
- { mcamd_mc_propmap, /* MC_NT_MC */
- sizeof (mcamd_mc_propmap) / sizeof (struct mc_propmap) },
- { mcamd_cs_propmap, /* MC_NT_CS */
- sizeof (mcamd_cs_propmap) / sizeof (struct mc_propmap) },
- { mcamd_dimm_propmap, /* MC_NT_DIMM */
- sizeof (mcamd_dimm_propmap) / sizeof (struct mc_propmap) },
- };
+ const struct mc_offmap *omp;
- if (mch->mch_type < MC_NT_NTYPES) {
- for (i = 0, pmp = props[nt].props; i < props[nt].numprops;
- i++, pmp++) {
- if (pmp->mcpm_code == code) {
- found = 1;
- break;
- }
+ if (nt > MC_NT_NTYPES || (omp = arr[nt].omp) == NULL)
+ return (0);
+
+ for (i = 0; i < arr[nt].mapents; i++, omp++) {
+ if (omp->mcom_code == code) {
+ *offset = omp->mcom_offset;
+ return (1);
}
}
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+mcamd_get_numprop(mcamd_hdl_t *hdl, mcamd_node_t *node,
+ mcamd_propcode_t code, mcamd_prop_t *valp)
+{
+ int found;
+ uint_t offset;
+
+ struct nt_offmap props[] = {
+ { mcamd_mc_offmap, /* MC_NT_MC */
+ sizeof (mcamd_mc_offmap) / sizeof (struct mc_offmap) },
+ { mcamd_cs_offmap, /* MC_NT_CS */
+ sizeof (mcamd_cs_offmap) / sizeof (struct mc_offmap) },
+ { mcamd_dimm_offmap, /* MC_NT_DIMM */
+ sizeof (mcamd_dimm_offmap) / sizeof (struct mc_offmap) }
+ };
+
+ found = findoffset(hdl, node, &props[0], code, &offset);
ASSERT(found);
- if (found) {
- *valp = *(uint64_t *)((uintptr_t)node + pmp->mcpm_offset);
+
+ if (found)
+ *valp = *(uint64_t *)((uintptr_t)node + offset);
+
+ return (found == 1);
+}
+
+int
+mcamd_get_numprops(mcamd_hdl_t *hdl, ...)
+{
+ va_list ap;
+ mcamd_node_t *node;
+ mcamd_propcode_t code;
+ mcamd_prop_t *valp;
+
+ va_start(ap, hdl);
+ while ((node = va_arg(ap, mcamd_node_t *)) != NULL) {
+ code = va_arg(ap, mcamd_propcode_t);
+ valp = va_arg(ap, mcamd_prop_t *);
+ if (!mcamd_get_numprop(hdl, node, code, valp))
+ return (0);
}
+ va_end(ap);
+ return (1);
+}
+
+static const struct mc_offmap mcreg_offmap[] = {
+ { MCAMD_REG_DRAMBASE, MCAMD_MC_OFF_DRAMBASE_REG },
+ { MCAMD_REG_DRAMLIMIT, MCAMD_MC_OFF_DRAMLIMIT_REG },
+ { MCAMD_REG_DRAMHOLE, MCAMD_MC_OFF_DRAMHOLE_REG },
+ { MCAMD_REG_DRAMCFGLO, MCAMD_MC_OFF_DRAMCFGLO_REG },
+ { MCAMD_REG_DRAMCFGHI, MCAMD_MC_OFF_DRAMCFGHI_REG },
+};
+
+static const struct mc_offmap csreg_offmap[] = {
+ { MCAMD_REG_CSBASE, MCAMD_CS_OFF_CSBASE_REG },
+ { MCAMD_REG_CSMASK, MCAMD_CS_OFF_CSMASK_REG },
+};
+
+/*ARGSUSED*/
+int
+mcamd_get_cfgreg(struct mcamd_hdl *hdl, mcamd_node_t *node,
+ mcamd_regcode_t code, uint32_t *valp)
+{
+ int found;
+ uint_t offset;
+
+ struct nt_offmap regs[] = {
+ { mcreg_offmap, /* MC_NT_MC */
+ sizeof (mcreg_offmap) / sizeof (struct mc_offmap) },
+ { csreg_offmap, /* MC_NT_CS */
+ sizeof (csreg_offmap) / sizeof (struct mc_offmap) },
+ { NULL, 0 } /* MC_NT_DIMM */
+ };
+
+ found = findoffset(hdl, node, &regs[0], code, &offset);
+ ASSERT(found);
+
+ ASSERT(found);
+ if (found)
+ *valp = *(uint32_t *)((uintptr_t)node + offset);
return (found == 1);
}
int
+mcamd_get_cfgregs(mcamd_hdl_t *hdl, ...)
+{
+ va_list ap;
+ mcamd_node_t *node;
+ mcamd_regcode_t code;
+ uint32_t *valp;
+
+ va_start(ap, hdl);
+ while ((node = va_arg(ap, mcamd_node_t *)) != NULL) {
+ code = va_arg(ap, mcamd_regcode_t);
+ valp = va_arg(ap, uint32_t *);
+ if (!mcamd_get_cfgreg(hdl, node, code, valp))
+ return (0);
+ }
+ va_end(ap);
+ return (1);
+}
+
+
+int
mcamd_errno(mcamd_hdl_t *mcamd)
{
return (mcamd->mcamd_errno);
@@ -376,7 +476,7 @@ fmri2unum(nvlist_t *nvl, mc_unum_t *unump)
bzero(unump, sizeof (mc_unum_t));
for (i = 0; i < MC_UNUM_NDIMM; i++)
- unump->unum_dimms[i] = -1;
+ unump->unum_dimms[i] = MC_INVALNUM;
for (i = 0; i < npr; i++) {
char *hcnm, *hcid;
@@ -397,6 +497,8 @@ fmri2unum(nvlist_t *nvl, mc_unum_t *unump)
unump->unum_cs = (int)v;
else if (strcmp(hcnm, "dimm") == 0)
unump->unum_dimms[0] = (int)v;
+ else if (strcmp(hcnm, "rank") == 0)
+ unump->unum_rank = (int)v;
}
unump->unum_offset = offset;
@@ -440,3 +542,121 @@ mcamd_mc_register(cpu_t *cp)
{
cmi_mc_register(cp, &mcamd_mc_ops, NULL);
}
+
+static void
+mc_ereport_dimm_resource(mc_unum_t *unump, nvlist_t *elems[], int *nump)
+{
+ int i;
+
+ for (i = 0; i < MC_UNUM_NDIMM; i++) {
+ if (unump->unum_dimms[i] == MC_INVALNUM)
+ break;
+
+ elems[(*nump)++] = fm_nvlist_create(NULL);
+ fm_fmri_hc_set(elems[i], FM_HC_SCHEME_VERSION, NULL, NULL, 5,
+ "motherboard", unump->unum_board,
+ "chip", unump->unum_chip,
+ "memory-controller", unump->unum_mc,
+ "dimm", unump->unum_dimms[i],
+ "rank", unump->unum_rank);
+ }
+}
+
+static void
+mc_ereport_cs_resource(mc_unum_t *unump, nvlist_t *elems[], int *nump)
+{
+ elems[0] = fm_nvlist_create(NULL);
+ fm_fmri_hc_set(elems[0], FM_HC_SCHEME_VERSION, NULL, NULL, 4,
+ "motherboard", unump->unum_board,
+ "chip", unump->unum_chip,
+ "memory-controller", unump->unum_mc,
+ "chip-select", unump->unum_cs);
+ *nump = 1;
+}
+
+/*
+ * Create the 'resource' payload member from the unum info. If valid
+ * dimm numbers are present in the unum info then create members
+ * identifying the dimm and rank; otherwise if a valid chip-select
+ * number is indicated then create a member identifying the chip-select
+ * topology node.
+ */
+static void
+mc_ereport_add_resource(nvlist_t *payload, mc_unum_t *unump)
+{
+ nvlist_t *elems[MC_UNUM_NDIMM];
+ int nelems = 0;
+ int i;
+
+ if (unump->unum_dimms[0] != MC_INVALNUM)
+ mc_ereport_dimm_resource(unump, elems, &nelems);
+ else if (unump->unum_cs != MC_INVALNUM)
+ mc_ereport_cs_resource(unump, elems, &nelems);
+
+ if (nelems > 0) {
+ fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
+ DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL);
+
+ for (i = 0; i < nelems; i++)
+ fm_nvlist_destroy(elems[i], FM_NVA_FREE);
+ }
+}
+
+static void
+mc_ereport_add_payload(nvlist_t *ereport, uint64_t members, mc_unum_t *unump)
+{
+ if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE &&
+ unump != NULL)
+ mc_ereport_add_resource(ereport, unump);
+}
+
+static nvlist_t *
+mc_fmri_create(mc_t *mc)
+{
+ nvlist_t *nvl = fm_nvlist_create(NULL);
+
+ fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 3,
+ "motherboard", 0,
+ "chip", mc->mc_chip->chip_id,
+ "memory-controller", 0);
+
+ return (nvl);
+}
+
+/*
+ * Simple ereport generator for errors detected by the memory controller.
+ * Posts an ereport of class ereport.cpu.amd.<class_sfx> with a resource nvlist
+ * derived from the given mc_unum_t. There are no other payload members.
+ * The mc argument is used to formulate a detector and this mc should
+ * correspond with that identified in the mc_unum_t.
+ *
+ * There is no control of which members to include the the resulting ereport -
+ * it will be an ereport formed using the given class suffix, detector
+ * indicated as the memory-controller and with a resource generated by
+ * expanding the given mc_unum_t.
+ *
+ * We do not use any special nv allocator here and so this is not suitable
+ * for use during panic. It is intended for use during MC topology
+ * discovery and other controlled circumstances.
+ */
+void
+mcamd_ereport_post(mc_t *mc, const char *class_sfx, mc_unum_t *unump,
+ uint64_t payload)
+{
+ nvlist_t *ereport, *detector;
+ char buf[FM_MAX_CLASS];
+
+ ereport = fm_nvlist_create(NULL);
+ detector = mc_fmri_create(mc);
+
+ (void) snprintf(buf, FM_MAX_CLASS, "%s.%s.%s", FM_ERROR_CPU,
+ "amd", class_sfx);
+ fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
+ fm_ena_generate(gethrtime(), FM_ENA_FMT1), detector, NULL);
+ fm_nvlist_destroy(detector, FM_NVA_FREE);
+
+ mc_ereport_add_payload(ereport, payload, unump);
+
+ (void) fm_ereport_post(ereport, EVCH_TRYHARD);
+ fm_nvlist_destroy(ereport, FM_NVA_FREE);
+}
diff --git a/usr/src/uts/i86pc/mc-amd/Makefile b/usr/src/uts/i86pc/mc-amd/Makefile
index a360c43854..220ed95de3 100644
--- a/usr/src/uts/i86pc/mc-amd/Makefile
+++ b/usr/src/uts/i86pc/mc-amd/Makefile
@@ -58,6 +58,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
# Overrides and additions
#
CPPFLAGS += -I$(SRCDIR) -I$(OBJS_DIR) -I$(SRC)/common/mc/mc-amd
+CPPFLAGS += -I$(SRC)/common/util
CLEANFILES += $(MCAMD_OFF_H)
CLOBBERFILES += $(MCAMD_OFF_H)
diff --git a/usr/src/uts/i86pc/os/cmi.c b/usr/src/uts/i86pc/os/cmi.c
index 3eff9b168b..1b788a4e99 100644
--- a/usr/src/uts/i86pc/os/cmi.c
+++ b/usr/src/uts/i86pc/os/cmi.c
@@ -49,6 +49,12 @@
(cpu)->cpu_m.mcpu_cmidata
/*
+ * If cleared for debugging we will not attempt to load a model-specific
+ * cpu module but will load the generic cpu module instead.
+ */
+int cmi_force_generic = 0;
+
+/*
* If cleared for debugging, we will suppress panicking on fatal hardware
* errors. This should *only* be used for debugging; it use can and will
* cause data corruption if actual hardware errors are detected by the system.
@@ -187,15 +193,18 @@ cmi_load(cpu_t *cp)
mutex_enter(&cmi_load_lock);
- if ((cmi = cmi_load_module(cp)) == NULL || (
- (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 && err != ENOTSUP)) {
+ if (!cmi_force_generic && (
+ ((cmi = cmi_load_module(cp)) == NULL) ||
+ ((err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 &&
+ err != ENOTSUP))) {
cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d",
cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err);
mutex_exit(&cmi_load_lock);
return (-1);
}
- if (err != 0 && ((cmi = cmi_load_generic()) == NULL ||
+ if ((cmi_force_generic || err != 0) &&
+ ((cmi = cmi_load_generic()) == NULL ||
(err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) {
cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d",
cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err);
@@ -231,6 +240,11 @@ cmi_post_init(void)
CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU));
}
+/*
+ * Called just once from start_other_cpus when all processors are started.
+ * This will not be called for each cpu, so the registered op must not
+ * assume it is called as such.
+ */
void
cmi_post_mpstartup(void)
{
@@ -250,9 +264,10 @@ cmi_faulted_exit(cpu_t *cp)
}
int
-cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen)
+cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen, int cscontig)
{
- return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen));
+ return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen,
+ cscontig));
}
void
diff --git a/usr/src/uts/i86pc/os/cpuid.c b/usr/src/uts/i86pc/os/cpuid.c
index 0347c3ca44..45fb59ff73 100644
--- a/usr/src/uts/i86pc/os/cpuid.c
+++ b/usr/src/uts/i86pc/os/cpuid.c
@@ -164,6 +164,12 @@ struct cpuid_info {
#define TM_EDX_FEATURES 2
#define STD_ECX_FEATURES 3
+ /*
+ * Synthesized information, where known.
+ */
+ uint32_t cpi_chiprev; /* See X86_CHIPREV_* in x86_archext.h */
+ const char *cpi_chiprevstr; /* May be NULL if chiprev unknown */
+ uint32_t cpi_socket; /* Chip package/socket type */
};
@@ -216,6 +222,126 @@ static struct cpuid_info cpuid_info0;
#define IS_NEW_F6(cpi) ((cpi->cpi_family == 6) && !IS_LEGACY_P6(cpi))
/*
+ * AMD family 0xf socket types.
+ * First index is 0 for revs B thru E, 1 for F and G.
+ * Second index by (model & 0x3)
+ */
+static uint32_t amd_skts[2][4] = {
+ {
+ X86_SOCKET_754, /* 0b00 */
+ X86_SOCKET_940, /* 0b01 */
+ X86_SOCKET_754, /* 0b10 */
+ X86_SOCKET_939 /* 0b11 */
+ },
+ {
+ X86_SOCKET_S1g1, /* 0b00 */
+ X86_SOCKET_F1207, /* 0b01 */
+ X86_SOCKET_UNKNOWN, /* 0b10 */
+ X86_SOCKET_AM2 /* 0b11 */
+ }
+};
+
+/*
+ * Table for mapping AMD Family 0xf model/stepping combination to
+ * chip "revision" and socket type. Only rm_family 0xf is used at the
+ * moment, but AMD family 0x10 will extend the exsiting revision names
+ * so will likely also use this table.
+ *
+ * The first member of this array that matches a given family, extended model
+ * plus model range, and stepping range will be considered a match.
+ */
+static const struct amd_rev_mapent {
+ uint_t rm_family;
+ uint_t rm_modello;
+ uint_t rm_modelhi;
+ uint_t rm_steplo;
+ uint_t rm_stephi;
+ uint32_t rm_chiprev;
+ const char *rm_chiprevstr;
+ int rm_sktidx;
+} amd_revmap[] = {
+ /*
+ * Rev B includes model 0x4 stepping 0 and model 0x5 stepping 0 and 1.
+ */
+ { 0xf, 0x04, 0x04, 0x0, 0x0, X86_CHIPREV_AMD_F_REV_B, "B", 0 },
+ { 0xf, 0x05, 0x05, 0x0, 0x1, X86_CHIPREV_AMD_F_REV_B, "B", 0 },
+ /*
+ * Rev C0 includes model 0x4 stepping 8 and model 0x5 stepping 8
+ */
+ { 0xf, 0x04, 0x05, 0x8, 0x8, X86_CHIPREV_AMD_F_REV_C0, "C0", 0 },
+ /*
+ * Rev CG is the rest of extended model 0x0 - i.e., everything
+ * but the rev B and C0 combinations covered above.
+ */
+ { 0xf, 0x00, 0x0f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_CG, "CG", 0 },
+ /*
+ * Rev D has extended model 0x1.
+ */
+ { 0xf, 0x10, 0x1f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_D, "D", 0 },
+ /*
+ * Rev E has extended model 0x2.
+ * Extended model 0x3 is unused but available to grow into.
+ */
+ { 0xf, 0x20, 0x3f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_E, "E", 0 },
+ /*
+ * Rev F has extended models 0x4 and 0x5.
+ */
+ { 0xf, 0x40, 0x5f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_F, "F", 1 },
+ /*
+ * Rev G has extended model 0x6.
+ */
+ { 0xf, 0x60, 0x6f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_G, "G", 1 },
+};
+
+static void
+synth_amd_info(struct cpuid_info *cpi)
+{
+ const struct amd_rev_mapent *rmp;
+ uint_t family, model, step;
+ int i;
+
+ /*
+ * Currently only AMD family 0xf uses these fields.
+ */
+ if (cpi->cpi_family != 0xf)
+ return;
+
+ family = cpi->cpi_family;
+ model = cpi->cpi_model;
+ step = cpi->cpi_step;
+
+ for (i = 0, rmp = amd_revmap; i < sizeof (amd_revmap) / sizeof (*rmp);
+ i++, rmp++) {
+ if (family == rmp->rm_family &&
+ model >= rmp->rm_modello && model <= rmp->rm_modelhi &&
+ step >= rmp->rm_steplo && step <= rmp->rm_stephi) {
+ cpi->cpi_chiprev = rmp->rm_chiprev;
+ cpi->cpi_chiprevstr = rmp->rm_chiprevstr;
+ cpi->cpi_socket = amd_skts[rmp->rm_sktidx][model & 0x3];
+ return;
+ }
+ }
+}
+
+static void
+synth_info(struct cpuid_info *cpi)
+{
+ cpi->cpi_chiprev = X86_CHIPREV_UNKNOWN;
+ cpi->cpi_chiprevstr = "Unknown";
+ cpi->cpi_socket = X86_SOCKET_UNKNOWN;
+
+ switch (cpi->cpi_vendor) {
+ case X86_VENDOR_AMD:
+ synth_amd_info(cpi);
+ break;
+
+ default:
+ break;
+
+ }
+}
+
+/*
* Some undocumented ways of patching the results of the cpuid
* instruction to permit running Solaris 10 on future cpus that
* we don't currently support. Could be set to non-zero values
@@ -812,6 +938,11 @@ cpuid_pass1(cpu_t *cpu)
}
}
+ /*
+ * Synthesize chip "revision" and socket type
+ */
+ synth_info(cpi);
+
pass1_done:
cpi->cpi_pass = 1;
return (feature);
@@ -1734,6 +1865,27 @@ cpuid_getstep(cpu_t *cpu)
return (cpu->cpu_m.mcpu_cpi->cpi_step);
}
+uint32_t
+cpuid_getchiprev(struct cpu *cpu)
+{
+ ASSERT(cpuid_checkpass(cpu, 1));
+ return (cpu->cpu_m.mcpu_cpi->cpi_chiprev);
+}
+
+const char *
+cpuid_getchiprevstr(struct cpu *cpu)
+{
+ ASSERT(cpuid_checkpass(cpu, 1));
+ return (cpu->cpu_m.mcpu_cpi->cpi_chiprevstr);
+}
+
+uint32_t
+cpuid_getsockettype(struct cpu *cpu)
+{
+ ASSERT(cpuid_checkpass(cpu, 1));
+ return (cpu->cpu_m.mcpu_cpi->cpi_socket);
+}
+
chipid_t
chip_plat_get_chipid(cpu_t *cpu)
{
@@ -1871,6 +2023,7 @@ cpuid_opteron_erratum(cpu_t *cpu, uint_t erratum)
if (cpi->cpi_vendor != X86_VENDOR_AMD ||
CPI_FAMILY(cpi) == 4 || CPI_FAMILY(cpi) == 5 ||
CPI_FAMILY(cpi) == 6)
+
return (0);
eax = cpi->cpi_std[1].cp_eax;
diff --git a/usr/src/uts/i86pc/sys/cpu_module.h b/usr/src/uts/i86pc/sys/cpu_module.h
index 6b1253881a..65f21a4565 100644
--- a/usr/src/uts/i86pc/sys/cpu_module.h
+++ b/usr/src/uts/i86pc/sys/cpu_module.h
@@ -52,7 +52,7 @@ extern void cmi_post_mpstartup(void);
extern void cmi_faulted_enter(struct cpu *);
extern void cmi_faulted_exit(struct cpu *);
-extern int cmi_scrubber_enable(struct cpu *, uint64_t, uint64_t);
+extern int cmi_scrubber_enable(struct cpu *, uint64_t, uint64_t, int);
extern void cmi_mca_init(void);
extern int cmi_mca_inject(cmi_mca_regs_t *, uint_t);
diff --git a/usr/src/uts/i86pc/sys/cpu_module_impl.h b/usr/src/uts/i86pc/sys/cpu_module_impl.h
index 9438f69e6d..c68f537d64 100644
--- a/usr/src/uts/i86pc/sys/cpu_module_impl.h
+++ b/usr/src/uts/i86pc/sys/cpu_module_impl.h
@@ -49,7 +49,7 @@ typedef struct cmi_ops {
void (*cmi_fini)(void *);
void (*cmi_faulted_enter)(void *);
void (*cmi_faulted_exit)(void *);
- int (*cmi_scrubber_enable)(void *, uint64_t, uint64_t);
+ int (*cmi_scrubber_enable)(void *, uint64_t, uint64_t, int);
void (*cmi_mca_init)(void *);
int (*cmi_mca_trap)(void *, struct regs *);
int (*cmi_mca_inject)(void *, cmi_mca_regs_t *, uint_t);
diff --git a/usr/src/uts/intel/sys/fm/cpu/AMD.h b/usr/src/uts/intel/sys/fm/cpu/AMD.h
index bb7aa427e5..df66719ad3 100644
--- a/usr/src/uts/intel/sys/fm/cpu/AMD.h
+++ b/usr/src/uts/intel/sys/fm/cpu/AMD.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -44,6 +43,7 @@ extern "C" {
#define FM_EREPORT_PAYLOAD_NAME_BANK_NUM "bank-number"
#define FM_EREPORT_PAYLOAD_NAME_ADDR "addr"
#define FM_EREPORT_PAYLOAD_NAME_ADDR_VALID "addr-valid"
+#define FM_EREPORT_PAYLOAD_NAME_BANK_MISC "bank-misc"
#define FM_EREPORT_PAYLOAD_NAME_SYND "syndrome"
#define FM_EREPORT_PAYLOAD_NAME_SYND_TYPE "syndrome-type"
#define FM_EREPORT_PAYLOAD_NAME_IP "ip"
@@ -60,6 +60,7 @@ extern "C" {
#define FM_EREPORT_PAYLOAD_FLAG_PRIV 0x0000000000000080
#define FM_EREPORT_PAYLOAD_FLAG_RESOURCE 0x0000000000000100
#define FM_EREPORT_PAYLOAD_FLAG_STACK 0x0000000000000200
+#define FM_EREPORT_PAYLOAD_FLAG_BANK_MISC 0x0000000000000400
#define FM_EREPORT_PAYLOAD_FLAGS_BANK \
(FM_EREPORT_PAYLOAD_FLAG_BANK_STAT | FM_EREPORT_PAYLOAD_FLAG_BANK_NUM)
@@ -74,6 +75,8 @@ extern "C" {
FM_EREPORT_PAYLOAD_FLAG_PRIV)
#define FM_EREPORT_PAYLOAD_FLAGS_NB \
(FM_EREPORT_PAYLOAD_FLAG_STACK)
+#define FM_EREPORT_PAYLOAD_FLAGS_BANK_MISC \
+ (FM_EREPORT_PAYLOAD_FLAG_BANK_MISC)
#define FM_EREPORT_PAYLOAD_FLAGS_1(f1) \
(FM_EREPORT_PAYLOAD_FLAGS_COMMON | FM_EREPORT_PAYLOAD_FLAGS_##f1)
@@ -83,13 +86,17 @@ extern "C" {
#define FM_EREPORT_PAYLOAD_FLAGS_3(f1, f2, f3) \
(FM_EREPORT_PAYLOAD_FLAGS_COMMON | FM_EREPORT_PAYLOAD_FLAGS_##f1 | \
FM_EREPORT_PAYLOAD_FLAGS_##f2 | FM_EREPORT_PAYLOAD_FLAGS_##f3)
+#define FM_EREPORT_PAYLOAD_FLAGS_4(f1, f2, f3, f4) \
+ (FM_EREPORT_PAYLOAD_FLAGS_COMMON | FM_EREPORT_PAYLOAD_FLAGS_##f1 | \
+ FM_EREPORT_PAYLOAD_FLAGS_##f2 | FM_EREPORT_PAYLOAD_FLAGS_##f3 | \
+ FM_EREPORT_PAYLOAD_FLAGS_##f4)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_SYS_ECC1 \
- FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND)
+ FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_L2_ECC1 \
FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_SYS_ECCM \
- FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND)
+ FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_L2_ECCM \
FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_DATA_ECC1 \
@@ -141,17 +148,17 @@ extern "C" {
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_S_RDE \
FM_EREPORT_PAYLOAD_FLAGS_1(ADDR)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_S_ECC1 \
- FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND)
+ FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_S_ECCM \
- FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND)
+ FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_LS_S_RDE \
FM_EREPORT_PAYLOAD_FLAGS_COMMON
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_MEM_CE \
- FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE)
+ FM_EREPORT_PAYLOAD_FLAGS_4(ADDR, SYND, RESOURCE, BANK_MISC)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_MEM_UE \
- FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE)
+ FM_EREPORT_PAYLOAD_FLAGS_4(ADDR, SYND, RESOURCE, BANK_MISC)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_HT_CRC \
FM_EREPORT_PAYLOAD_FLAGS_COMMON
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_HT_SYNC \
@@ -166,6 +173,10 @@ extern "C" {
FM_EREPORT_PAYLOAD_FLAGS_1(ADDR)
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_WDOG \
FM_EREPORT_PAYLOAD_FLAGS_1(ADDR)
+#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_DRAMADDR_PAR \
+ FM_EREPORT_PAYLOAD_FLAGS_COMMON
+#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_MC_TESTFAIL \
+ FM_EREPORT_PAYLOAD_FLAG_RESOURCE
#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_UNKNOWN \
FM_EREPORT_PAYLOAD_FLAGS_1(ADDR)
@@ -213,6 +224,9 @@ extern "C" {
#define FM_EREPORT_CPU_AMD_NB_GART_WALK "nb.gart_walk"
#define FM_EREPORT_CPU_AMD_NB_RMW "nb.rmw"
#define FM_EREPORT_CPU_AMD_NB_WDOG "nb.wdog"
+#define FM_EREPORT_CPU_AMD_NB_DRAMADDR_PAR "nb.dramaddr_par"
+
+#define FM_EREPORT_CPU_AMD_MC_TESTFAIL "mc.cs_testfail"
#define FM_EREPORT_CPU_AMD_UNKNOWN "unknown"
diff --git a/usr/src/uts/intel/sys/mc.h b/usr/src/uts/intel/sys/mc.h
index 416f323c86..4e1ab003ee 100644
--- a/usr/src/uts/intel/sys/mc.h
+++ b/usr/src/uts/intel/sys/mc.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -39,22 +38,32 @@ extern "C" {
#endif
#define MC_UNUM_NAMLEN 192
-#define MC_UNUM_NDIMM 8
+#define MC_UNUM_NDIMM 2
typedef struct mc_unum {
int unum_board;
int unum_chip;
int unum_mc;
int unum_cs;
+ int unum_rank;
uint64_t unum_offset;
int unum_dimms[MC_UNUM_NDIMM];
} mc_unum_t;
+/*
+ * Invalid marker used in some numeric properties
+ */
+#define MC_INVALNUM ((uint32_t)-1)
+
#define MC_AMD_DEV_OFFSET 24 /* node ID + offset == PCI dev num */
+/*
+ * /dev/mc/mc* ioctl cmds
+ */
#define MC_IOC (0x4d43 << 16)
#define MC_IOC_SNAPSHOT_INFO (MC_IOC | 1)
#define MC_IOC_SNAPSHOT (MC_IOC | 2)
+#define MC_IOC_ONLINESPARE_EN (MC_IOC | 4)
/*
* Prior to requesting a copy of the snapshot, consumers are advised to request
diff --git a/usr/src/uts/intel/sys/mc_amd.h b/usr/src/uts/intel/sys/mc_amd.h
index fba266b14f..c1fc1f2513 100644
--- a/usr/src/uts/intel/sys/mc_amd.h
+++ b/usr/src/uts/intel/sys/mc_amd.h
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -28,6 +27,74 @@
#pragma ident "%Z%%M% %I% %E% SMI"
+#include <sys/mc.h>
+#include <sys/x86_archext.h>
+
+/*
+ * The mc-amd driver exports an nvlist to userland, where the primary
+ * consumer is the "chip" topology enumerator for this platform type which
+ * builds a full topology subtree from this information. Others can use
+ * it, too, but don't depend on it not changing without an ARC contract.
+ *
+ * In the initial mc-amd implementation this nvlist was not versioned;
+ * we'll think of that as version 0 and it may be recognised by the absence
+ * of a "mcamd-nvlist-version member.
+ *
+ * Version 1 is defined as follows. A name in square brackets indicates
+ * that member is optional (only present if the actual value is valid).
+ *
+ * Name Type Description
+ * -------------------- --------------- ---------------------------------------
+ * mcamd-nvlist-version uint8 Exported nvlist version number
+ * num uint64 Chip id of this memory controller
+ * revision uint64 cpuid_getchiprev() result
+ * revname string cpuid_getchiprevstr() result
+ * socket string "Socket 755|939|940|AM2|F(1207)|S1g1"
+ * ecc-type string "ChipKill 128/16" or "Normal 64/8"
+ * base-addr uint64 Node base address
+ * lim-addr uint64 Node limit address
+ * node-ilen uint64 0|1|3|7 for 0/2/4/8 way node interleave
+ * node-ilsel uint64 Node interleave position of this node
+ * cs-intlv-factor uint64 chip-select interleave: 1/2/4/8
+ * dram-hole-size uint64 size in bytes from dram hole addr reg
+ * access-width uint64 MC mode, 64 or 128 bit
+ * bank-mapping uint64 Raw DRAM Bank Address Mapping Register
+ * bankswizzle uint64 1 if bank swizzling enabled; else 0
+ * mismatched-dimm-support uint64 1 if active; else 0
+ * [spare-csnum] uint64 Chip-select pair number of any spare
+ * [bad-csnum] uint64 Chip-select pair number of swapped cs
+ * cslist nvlist array See below; may have 0 members
+ * dimmlist nvlist array See below; may have 0 members
+ *
+ * cslist is an array of nvlist, each as follows:
+ *
+ * Name Type Description
+ * -------------------- --------------- ---------------------------------------
+ * num uint64 Chip-select base/mask pair number
+ * base-addr uint64 Chip-select base address (rel to node)
+ * mask uint64 Chip-select mask
+ * size uint64 Chip-select size in bytes
+ * dimm1-num uint64 First dimm (lodimm if a pair)
+ * dimm1-csname string Socket cs# line name for 1st dimm rank
+ * [dimm2-num] uint64 Second dimm if applicable (updimm)
+ * [dimm2-csname] string Socket cs# line name for 2nd dimm rank
+ *
+ * dimmlist is an array of nvlist, each as follows:
+ *
+ * Name Type Description
+ * -------------------- --------------- ---------------------------------------
+ * num uint64 DIMM instance number
+ * size uint64 DIMM size in bytes
+ * csnums uint64 array CS base/mask pair(s) on this DIMM
+ * csnames string array Socket cs# line name(s) on this DIMM
+ *
+ * The n'th csnums entry corresponds to the n'th csnames entry
+ */
+#define MC_NVLIST_VERSTR "mcamd-nvlist-version"
+#define MC_NVLIST_VERS0 0
+#define MC_NVLIST_VERS1 1
+#define MC_NVLIST_VERS MC_NVLIST_VERS1
+
/*
* Definitions describing various memory controller constant properties and
* the structure of configuration registers.
@@ -38,155 +105,550 @@ extern "C" {
#endif
/*
+ * Constants and feature/revision test macros that are not expected to vary
+ * among different AMD family 0xf processor revisions.
+ */
+
+/*
* Configuration constants
*/
+#define MC_CHIP_MAXNODES 8 /* max number of MCs in system */
#define MC_CHIP_NDIMM 8 /* max dimms per MC */
#define MC_CHIP_NCS 8 /* number of chip-selects per MC */
+#define MC_CHIP_NDRAMCHAN 2 /* maximum number of dram channels */
#define MC_CHIP_DIMMRANKMAX 4 /* largest number of ranks per dimm */
#define MC_CHIP_DIMMPERCS 2 /* max number of dimms per cs */
#define MC_CHIP_DIMMPAIR(csnum) (csnum / MC_CHIP_DIMMPERCS)
+#if MC_CHIP_DIMMPERCS > MC_UNUM_NDIMM
+#error "MC_CHIP_DIMMPERCS exceeds MC_UNUM_NDIMM"
+#endif
+
+/*
+ * MC_REV_* are used a a convenient shorter form of the X86_CHIPREV
+ * counterparts; these must map directly as we fill mcp_rev from
+ * a cpuid_getchiprev call.
+ */
+#define MC_REV_UNKNOWN X86_CHIPREV_UNKNOWN
+#define MC_REV_B X86_CHIPREV_AMD_F_REV_B
+#define MC_REV_C (X86_CHIPREV_AMD_F_REV_C0 | X86_CHIPREV_AMD_F_REV_CG)
+#define MC_REV_D X86_CHIPREV_AMD_F_REV_D
+#define MC_REV_E X86_CHIPREV_AMD_F_REV_E
+#define MC_REV_F X86_CHIPREV_AMD_F_REV_F
+#define MC_REV_G X86_CHIPREV_AMD_F_REV_G
+
/*
- * Encoding of chip version variations that we need to distinguish
+ * The most common groupings for memory controller features.
*/
-#define MC_REV_UNKNOWN -1u /* unknown AMD revision */
-#define MC_REV_PRE_D 0 /* B/C/CG */
-#define MC_REV_D_E 1 /* D or E */
-#define MC_REV_F 2 /* F */
+#define MC_REVS_BC (MC_REV_B | MC_REV_C)
+#define MC_REVS_DE (MC_REV_D | MC_REV_E)
+#define MC_REVS_BCDE (MC_REVS_BC | MC_REVS_DE)
+#define MC_REVS_FG (MC_REV_F | MC_REV_G)
/*
- * BKDG 3.29 section 3.4.4.1 - DRAM base i registers
+ * Is 'rev' included in the 'revmask' bitmask?
*/
-#define MC_AM_DB_DRAMBASE_MASK 0xffff0000
-#define MC_AM_DB_DRAMBASE_LSHFT 8
-#define MC_AM_DB_DRAMBASE(regval) \
- (((uint64_t)(regval) & MC_AM_DB_DRAMBASE_MASK) << \
- MC_AM_DB_DRAMBASE_LSHFT)
-#define MC_AM_DB_INTLVEN_MASK 0x00000700
-#define MC_AM_DB_INTLVEN_SHIFT 8
-#define MC_AM_DB_WE 0x00000002
-#define MC_AM_DB_RE 0x00000001
+#define MC_REV_MATCH(rev, revmask) X86_CHIPREV_MATCH(rev, revmask)
/*
- * BKDG 3.29 section 3.4.4.2 - DRAM limit i registers
+ * Is 'rev' at least revision 'revmin' or greater
*/
-#define MC_AM_DL_DRAMLIM_MASK 0xffff0000
-#define MC_AM_DL_DRAMLIM_SHIFT 16
-#define MC_AM_DL_DRAMLIM_LSHFT 8
-#define MC_AM_DL_DRAMLIM(regval) \
- ((((uint64_t)(regval) & MC_AM_DL_DRAMLIM_MASK) << \
- MC_AM_DL_DRAMLIM_LSHFT) | ((regval) ? \
- ((1 << (MC_AM_DL_DRAMLIM_SHIFT + MC_AM_DL_DRAMLIM_LSHFT)) - 1) : 0))
-#define MC_AM_DL_INTLVSEL_MASK 0x00000700
-#define MC_AM_DL_INTLVSEL_SHIFT 8
-#define MC_AM_DL_DSTNODE_MASK 0x00000007
+#define MC_REV_ATLEAST(rev, minrev) X86_CHIPREV_ATLEAST(rev, minrev)
/*
- * BKDG 3.29 section 3.5.4 - DRAM CS Base Address Registers.
+ * Chip socket types
+ */
+#define MC_SKT_UNKNOWN 0x0
+#define MC_SKT_754 0x1
+#define MC_SKT_939 0x2
+#define MC_SKT_940 0x3
+#define MC_SKT_S1g1 0x4
+#define MC_SKT_AM2 0x5
+#define MC_SKT_F1207 0x6
+
+/*
+ * Memory controller registers are read via PCI config space accesses on
+ * bus 0, device 24 + NodeId, and function as follows:
+ *
+ * Function 0: HyperTransport Technology Configuration
+ * Function 1: Address Map
+ * Function 2: DRAM Controller & HyperTransport Technology Trace Mode
+ * Function 3: Miscellaneous Control
+ *
+ * For a given (bus, device, function) a particular offset selects the
+ * desired register. All registers are 32-bits wide.
*
- * MC_DC_CSB_CSBASE combines the BaseAddrHi and BaseAddrLo into a single
- * uint64_t, shifting them into the dram address bits they describe.
+ * Different family 0xf processor revisions vary slightly in the content
+ * of these configuration registers. The biggest change is with rev F
+ * where DDR2 support has been introduced along with some hardware-controlled
+ * correctable memory error thresholding. Fortunately most of the config info
+ * required by the mc-amd driver is similar across revisions.
+ *
+ * We will try to insulate most of the driver code from config register
+ * details by reading all memory-controller PCI config registers that we
+ * will need at driver attach time for each of functions 0 through 3, and
+ * storing them in a "cooked" form as memory controller properties.
+ * These are to be accessed directly where we have an mc_t to hand, otherwise
+ * through mcamd_get_numprop. As such we expect most/all use of the
+ * structures and macros defined below to be in those attach codepaths.
*/
-#define MC_DC_CSB_BASEHI_MASK 0xffe00000
-#define MC_DC_CSB_BASEHI_LSHFT 4
-#define MC_DC_CSB_BASELO_MASK 0x0000fe00
-#define MC_DC_CSB_BASELO_LSHFT 4
+/*
+ * Registers will be represented as unions, with one fixed-width unsigned
+ * integer member providing access to the raw register value and one or more
+ * structs breaking the register out into bitfields (more than one struct if
+ * the register definitions varies across processor revisions).
+ *
+ * The "raw" union member will always be '_val32'. Use MCREG_VAL32 to
+ * access this member.
+ *
+ * The bitfield structs are all named _fmt_xxx where xxx identifies the
+ * processor revision to which it applies. At this point the only xxx
+ * values in use are:
+ * 'cmn' - applies to all revisions
+ * 'preF' - applies to revisions E and earlier
+ * 'revFG' - applies to revisions F and G
+ * Variants such as 'preD', 'revDE', 'postCG' etc should be introduced
+ * as requirements arise. The MC_REV_* and MC_REV_MATCH etc macros
+ * will also need to grow to match. Use MCREG_FIELD_* to access the
+ * individual bitfields of a register, perhaps using MC_REV_* and MC_REV_MATCH
+ * to decide which revision suffix to provide. Where a bitfield appears
+ * in different revisions but has the same use it should be named identically
+ * (even if the BKDG varies a little) so that the MC_REG_FIELD_* macros
+ * can lookup that member based on revision only.
+ */
-#define MC_DC_CSB_CSBASE(regval) \
- ((((uint64_t)(regval) & MC_DC_CSB_BASEHI_MASK) << \
- MC_DC_CSB_BASEHI_LSHFT) | (((uint64_t)(regval) & \
- MC_DC_CSB_BASELO_MASK) << MC_DC_CSB_BASELO_LSHFT))
+#define _MCREG_FIELD(up, revsuffix, field) ((up)->_fmt_##revsuffix.field)
-#define MC_DC_CSB_CSBE 0x00000001
+#define MCREG_VAL32(up) ((up)->_val32)
+
+#define MCREG_FIELD_CMN(up, field) _MCREG_FIELD(up, cmn, field)
+#define MCREG_FIELD_preF(up, field) _MCREG_FIELD(up, preF, field)
+#define MCREG_FIELD_revFG(up, field) _MCREG_FIELD(up, revFG, field)
/*
- * BKDG 3.29 section 3.5.5 - DRAM CS Mask Registers.
+ * Function 1 - DRAM Address Map: DRAM Base i Registers
*
- * MC_DC_CSM_CSMASK combines the AddrMaskHi and AddrMaskLo into a single
- * uint64_t, shifting them into the dram address bit positions they mask.
- * It also fills the gaps between high and low mask and below the low mask.
- * MC_DC_CSM_UNMASKED_BITS indicates the number of high dram address bits
- * above MC_DC_CSM_MASKHI_HIBIT that cannot be masked.
*/
-#define MC_DC_CSM_MASKHI_MASK 0x3fe00000
-#define MC_DC_CSM_MASKHI_LSHFT 4
-#define MC_DC_CSM_MASKHI_LOBIT 25
-#define MC_DC_CSM_MASKHI_HIBIT 33
-#define MC_DC_CSM_MASKLO_MASK 0x0000fe00
-#define MC_DC_CSM_MASKLO_LOBIT 13
-#define MC_DC_CSM_MASKLO_HIBIT 19
-#define MC_DC_CSM_MASKLO_LSHFT 4
+union mcreg_drambase {
+ uint32_t _val32;
+ struct {
+ uint32_t RE:1; /* 0:0 - Read Enable */
+ uint32_t WE:1; /* 1:1 - Write Enable */
+ uint32_t reserved1:6; /* 7:2 */
+ uint32_t IntlvEn:3; /* 10:8 - Interleave Enable */
+ uint32_t reserved2:5; /* 15:11 */
+ uint32_t DRAMBasei:16; /* 31:16 - Base Addr 39:24 */
+ } _fmt_cmn;
+};
+
+#define MC_DRAMBASE(up) ((uint64_t)MCREG_FIELD_CMN(up, DRAMBasei) << 24)
+
+/*
+ * Function 1 - DRAM Address Map: DRAM Limit i Registers
+ *
+ */
+
+union mcreg_dramlimit {
+ uint32_t _val32;
+ struct {
+ uint32_t DstNode:3; /* 2:0 - Destination Node */
+ uint32_t reserved1:5; /* 7:3 */
+ uint32_t IntlvSel:3; /* 10:8 - Interleave Select */
+ uint32_t reserved2:5; /* 15:11 */
+ uint32_t DRAMLimiti:16; /* 31:16 - Limit Addr 39:24 */
+ } _fmt_cmn;
+};
+
+#define MC_DRAMLIM(up) \
+ ((uint64_t)MCREG_FIELD_CMN(up, DRAMLimiti) << 24 | \
+ (MCREG_FIELD_CMN(up, DRAMLimiti) ? ((1 << 24) - 1) : 0))
+
+/*
+ * Function 1 - DRAM Address Map: DRAM Hole Address Register
+ */
+
+union mcreg_dramhole {
+ uint32_t _val32;
+ struct {
+ uint32_t DramHoleValid:1; /* 0:0 */
+ uint32_t reserved1:7; /* 7:1 */
+ uint32_t DramHoleOffset:8; /* 15:8 */
+ uint32_t reserved2:8; /* 23:16 */
+ uint32_t DramHoleBase:8; /* 31:24 */
+ } _fmt_cmn;
+};
+
+#define MC_DRAMHOLE_SIZE(up) (MCREG_FIELD_CMN(up, DramHoleOffset) << 24)
+
+/*
+ * Function 2 - DRAM Controller: DRAM CS Base Address Registers
+ */
+
+union mcreg_csbase {
+ uint32_t _val32;
+ /*
+ * Register format in revisions E and earlier
+ */
+ struct {
+ uint32_t CSEnable:1; /* 0:0 - CS Bank Enable */
+ uint32_t reserved1:8; /* 8:1 */
+ uint32_t BaseAddrLo:7; /* 15:9 - Base Addr 19:13 */
+ uint32_t reserved2:5; /* 20:16 */
+ uint32_t BaseAddrHi:11; /* 31:21 - Base Addr 35:25 */
+ } _fmt_preF;
+ /*
+ * Register format in revisions F and G
+ */
+ struct {
+ uint32_t CSEnable:1; /* 0:0 - CS Bank Enable */
+ uint32_t Spare:1; /* 1:1 - Spare Rank */
+ uint32_t TestFail:1; /* 2:2 - Memory Test Failed */
+ uint32_t reserved1:2; /* 4:3 */
+ uint32_t BaseAddrLo:9; /* 13:5 - Base Addr 21:13 */
+ uint32_t reserved2:5; /* 18:14 */
+ uint32_t BaseAddrHi:10; /* 28:19 - Base Addr 36:27 */
+ uint32_t reserved3:3; /* 31:39 */
+ } _fmt_revFG;
+};
+
+#define MC_CSBASE(up, rev) (MC_REV_MATCH(rev, MC_REV_F) ? \
+ (uint64_t)MCREG_FIELD_revFG(up, BaseAddrHi) << 27 | \
+ (uint64_t)MCREG_FIELD_revFG(up, BaseAddrLo) << 13 : \
+ (uint64_t)MCREG_FIELD_preF(up, BaseAddrHi) << 25 | \
+ (uint64_t)MCREG_FIELD_preF(up, BaseAddrLo) << 13)
+
+/*
+ * Function 2 - DRAM Controller: DRAM CS Mask Registers
+ */
+
+union mcreg_csmask {
+ uint32_t _val32;
+ /*
+ * Register format in revisions E and earlier
+ */
+ struct {
+ uint32_t reserved1:9; /* 8:0 */
+ uint32_t AddrMaskLo:7; /* 15:9 - Addr Mask 19:13 */
+ uint32_t reserved2:5; /* 20:16 */
+ uint32_t AddrMaskHi:9; /* 29:21 - Addr Mask 33:25 */
+ uint32_t reserved3:2; /* 31:30 */
+ } _fmt_preF;
+ /*
+ * Register format in revisions F and G
+ */
+ struct {
+ uint32_t reserved1:5; /* 4:0 */
+ uint32_t AddrMaskLo:9; /* 13:5 - Addr Mask 21:13 */
+ uint32_t reserved2:5; /* 18:14 */
+ uint32_t AddrMaskHi:10; /* 28:19 - Addr Mask 36:27 */
+ uint32_t reserved3:3; /* 31:29 */
+ } _fmt_revFG;
+};
-#define MC_DC_CSM_MASKFILL 0x1f01fff /* [24:20] and [12:0] */
+#define MC_CSMASKLO_LOBIT(rev) (MC_REV_MATCH(rev, MC_REV_F) ? 13 : 13)
+#define MC_CSMASKLO_HIBIT(rev) (MC_REV_MATCH(rev, MC_REV_F) ? 21 : 19)
-#define MC_DC_CSM_UNMASKED_BITS 2
+#define MC_CSMASKHI_LOBIT(rev) (MC_REV_MATCH(rev, MC_REV_F) ? 27 : 25)
+#define MC_CSMASKHI_HIBIT(rev) (MC_REV_MATCH(rev, MC_REV_F) ? 36 : 33)
-#define MC_DC_CSM_CSMASK(regval) \
- ((((uint64_t)(regval) & MC_DC_CSM_MASKHI_MASK) << \
- MC_DC_CSM_MASKHI_LSHFT) | (((uint64_t)(regval) & \
- MC_DC_CSM_MASKLO_MASK) << MC_DC_CSM_MASKLO_LSHFT) | \
- MC_DC_CSM_MASKFILL)
+#define MC_CSMASK_UNMASKABLE(rev) (MC_REV_MATCH(rev, MC_REV_F) ? 0 : 2)
+
+#define MC_CSMASK(up, rev) (MC_REV_MATCH(rev, MC_REV_F) ? \
+ (uint64_t)MCREG_FIELD_revFG(up, AddrMaskHi) << 27 | \
+ (uint64_t)MCREG_FIELD_revFG(up, AddrMaskLo) << 13 | 0x7c01fff : \
+ (uint64_t)MCREG_FIELD_preF(up, AddrMaskHi) << 25 | \
+ (uint64_t)MCREG_FIELD_preF(up, AddrMaskLo) << 13 | 0x1f01fff)
/*
- * BKDG 3.29 section 3.5.6 - DRAM Bank Address Mapping Register
+ * Function 2 - DRAM Controller: DRAM Bank Address Mapping Registers
*/
+
+union mcreg_bankaddrmap {
+ uint32_t _val32;
+ /*
+ * Register format in revisions E and earlier
+ */
+ struct {
+ uint32_t cs10:4; /* 3:0 - CS1/0 */
+ uint32_t cs32:4; /* 7:4 - CS3/2 */
+ uint32_t cs54:4; /* 11:8 - CS5/4 */
+ uint32_t cs76:4; /* 15:12 - CS7/6 */
+ uint32_t reserved1:14; /* 29:16 */
+ uint32_t BankSwizzleMode:1; /* 30:30 */
+ uint32_t reserved2:1; /* 31:31 */
+ } _fmt_preF;
+ /*
+ * Register format in revisions F and G
+ */
+ struct {
+ uint32_t cs10:4; /* 3:0 - CS1/0 */
+ uint32_t cs32:4; /* 7:4 - CS3/2 */
+ uint32_t cs54:4; /* 11:8 - CS5/4 */
+ uint32_t cs76:4; /* 15:12 - CS7/6 */
+ uint32_t reserved1:16; /* 31:16 */
+ } _fmt_revFG;
+ /*
+ * Accessing all mode encodings as one uint16
+ */
+ struct {
+ uint32_t allcsmodes:16; /* 15:0 */
+ uint32_t pad:16; /* 31:16 */
+ } _fmt_bankmodes;
+};
+
#define MC_DC_BAM_CSBANK_MASK 0x0000000f
#define MC_DC_BAM_CSBANK_SHIFT 4
-#define MC_DC_BAM_CSBANK_SWIZZLE 0x40000000
-
-/*
- * BKDG 3.29 section 3.4.8 - DRAM Hole register, revs E and later
- */
-#define MC_DC_HOLE_VALID 0x00000001
-#define MC_DC_HOLE_OFFSET_MASK 0x0000ff00
-#define MC_DC_HOLE_OFFSET_LSHIFT 16
-
-/*
- * BKDG 3.29 section 3.5.11 - DRAM configuration high and low registers.
- * The following defines may be applied to a uint64_t made by
- * concatenating those two 32-bit registers.
- */
-#define MC_DC_DCFG_DLL_DIS 0x0000000000000001
-#define MC_DC_DCFG_D_DRV 0x0000000000000002
-#define MC_DC_DCFG_QFC_EN 0x0000000000000004
-#define MC_DC_DCFG_DISDQSYS 0x0000000000000008
-#define MC_DC_DCFG_BURST2OPT 0x0000000000000020
-#define MC_DC_DCFG_MOD64BITMUX 0x0000000000000040
-#define MC_DC_DCFG_PWRDWNTRIEN 0x0000000000000080 /* >= rev E */
-#define MC_DC_DCFG_SCRATCHBIT 0x0000000000000080 /* <= rev D */
-#define MC_DC_DCFG_DRAMINIT 0x0000000000000100
-#define MC_DC_DCFG_DUALDIMMEN 0x0000000000000200
-#define MC_DC_DCFG_DRAMENABLE 0x0000000000000400
-#define MC_DC_DCFG_MEMCLRSTATUS 0x0000000000000800
-#define MC_DC_DCFG_ESR 0x0000000000001000
-#define MC_DC_DCFG_SR_S 0x0000000000002000
-#define MC_DC_DCFG_RDWRQBYP_MASK 0x000000000000c000
-#define MC_DC_DCFG_128 0x0000000000010000
-#define MC_DC_DCFG_DIMMECEN 0x0000000000020000
-#define MC_DC_DCFG_UNBUFFDIMM 0x0000000000040000
-#define MC_DC_DCFG_32BYTEEN 0x0000000000080000
-#define MC_DC_DCFG_X4DIMMS_MASK 0x0000000000f00000
-#define MC_DC_DCFG_X4DIMMS_SHIFT 20
-#define MC_DC_DCFG_DISINRCVRS 0x0000000001000000
-#define MC_DC_DCFG_BYPMAX_MASK 0x000000000e000000
-#define MC_DC_DCFG_EN2T 0x0000000010000000
-#define MC_DC_DCFG_UPPERCSMAP 0x0000000020000000
-#define MC_DC_DCFG_PWRDOWNCTL_MASK 0x00000000c0000000
-#define MC_DC_DCFG_ASYNCLAT_MASK 0x0000000f00000000
-#define MC_DC_DCFG_RDPREAMBLE_MASK 0x00000f0000000000
-#define MC_DC_DCFG_MEMDQDRVSTREN_MASK 0x0000600000000000
-#define MC_DC_DCFG_DISABLEJITTER 0x0000800000000000
-#define MC_DC_DCFG_ILD_LMT_MASK 0x0007000000000000
-#define MC_DC_DCFG_ECC_EN 0x0008000000000000
-#define MC_DC_DCFG_MEMCLK_MASK 0x0070000000000000
-#define MC_DC_DCFG_MCR 0x0200000000000000
-#define MC_DC_DCFG_MC0_EN 0x0400000000000000
-#define MC_DC_DCFG_MC1_EN 0x0800000000000000
-#define MC_DC_DCFG_MC2_EN 0x1000000000000000
-#define MC_DC_DCFG_MC3_EN 0x2000000000000000
-#define MC_DC_DCFG_ODDDIVISORCORRECT 0x8000000000000000
+
+#define MC_CSBANKMODE(up, csnum) ((up)->_fmt_bankmodes.allcsmodes >> \
+ MC_DC_BAM_CSBANK_SHIFT * MC_CHIP_DIMMPAIR(csnum) & MC_DC_BAM_CSBANK_MASK)
+
+/*
+ * Function 2 - DRAM Controller: DRAM Configuration Low and High
+ */
+
+union mcreg_dramcfg_lo {
+ uint32_t _val32;
+ /*
+ * Register format in revisions E and earlier.
+ * Bit 7 is a BIOS ScratchBit in revs D and earlier,
+ * PwrDwnTriEn in revision E; we don't use it so
+ * we'll call it ambig1.
+ */
+ struct {
+ uint32_t DLL_Dis:1; /* 0 */
+ uint32_t D_DRV:1; /* 1 */
+ uint32_t QFC_EN:1; /* 2 */
+ uint32_t DisDqsHys:1; /* 3 */
+ uint32_t reserved1:1; /* 4 */
+ uint32_t Burst2Opt:1; /* 5 */
+ uint32_t Mod64BitMux:1; /* 6 */
+ uint32_t ambig1:1; /* 7 */
+ uint32_t DramInit:1; /* 8 */
+ uint32_t DualDimmEn:1; /* 9 */
+ uint32_t DramEnable:1; /* 10 */
+ uint32_t MemClrStatus:1; /* 11 */
+ uint32_t ESR:1; /* 12 */
+ uint32_t SR_S:1; /* 13 */
+ uint32_t RdWrQByp:2; /* 15:14 */
+ uint32_t Width128:1; /* 16 */
+ uint32_t DimmEcEn:1; /* 17 */
+ uint32_t UnBufDimm:1; /* 18 */
+ uint32_t ByteEn32:1; /* 19 */
+ uint32_t x4DIMMs:4; /* 23:20 */
+ uint32_t DisInRcvrs:1; /* 24 */
+ uint32_t BypMax:3; /* 27:25 */
+ uint32_t En2T:1; /* 28 */
+ uint32_t UpperCSMap:1; /* 29 */
+ uint32_t PwrDownCtl:2; /* 31:30 */
+ } _fmt_preF;
+ /*
+ * Register format in revisions F and G
+ */
+ struct {
+ uint32_t InitDram:1; /* 0 */
+ uint32_t ExitSelfRef:1; /* 1 */
+ uint32_t reserved1:2; /* 3:2 */
+ uint32_t DramTerm:2; /* 5:4 */
+ uint32_t reserved2:1; /* 6 */
+ uint32_t DramDrvWeak:1; /* 7 */
+ uint32_t ParEn:1; /* 8 */
+ uint32_t SelRefRateEn:1; /* 9 */
+ uint32_t BurstLength32:1; /* 10 */
+ uint32_t Width128:1; /* 11 */
+ uint32_t x4DIMMs:4; /* 15:12 */
+ uint32_t UnBuffDimm:1; /* 16 */
+ uint32_t reserved3:2; /* 18:17 */
+ uint32_t DimmEccEn:1; /* 19 */
+ uint32_t reserved4:12; /* 31:20 */
+ } _fmt_revFG;
+};
+
+/*
+ * Function 2 - DRAM Controller: DRAM Controller Miscellaneous Data
+ */
+
+union mcreg_drammisc {
+ uint32_t _val32;
+ /*
+ * Register format in revisions F and G
+ */
+ struct {
+ uint32_t reserved2:1; /* 0 */
+ uint32_t DisableJitter:1; /* 1 */
+ uint32_t RdWrQByp:2; /* 3:2 */
+ uint32_t Mod64Mux:1; /* 4 */
+ uint32_t DCC_EN:1; /* 5 */
+ uint32_t ILD_lmt:3; /* 8:6 */
+ uint32_t DramEnabled:1; /* 9 */
+ uint32_t PwrSavingsEn:1; /* 10 */
+ uint32_t reserved1:13; /* 23:11 */
+ uint32_t MemClkDis:8; /* 31:24 */
+ } _fmt_revFG;
+};
+
+union mcreg_dramcfg_hi {
+ uint32_t _val32;
+ /*
+ * Register format in revisions E and earlier.
+ */
+ struct {
+ uint32_t AsyncLat:4; /* 3:0 */
+ uint32_t reserved1:4; /* 7:4 */
+ uint32_t RdPreamble:4; /* 11:8 */
+ uint32_t reserved2:1; /* 12 */
+ uint32_t MemDQDrvStren:2; /* 14:13 */
+ uint32_t DisableJitter:1; /* 15 */
+ uint32_t ILD_lmt:3; /* 18:16 */
+ uint32_t DCC_EN:1; /* 19 */
+ uint32_t MemClk:3; /* 22:20 */
+ uint32_t reserved3:2; /* 24:23 */
+ uint32_t MCR:1; /* 25 */
+ uint32_t MC0_EN:1; /* 26 */
+ uint32_t MC1_EN:1; /* 27 */
+ uint32_t MC2_EN:1; /* 28 */
+ uint32_t MC3_EN:1; /* 29 */
+ uint32_t reserved4:1; /* 30 */
+ uint32_t OddDivisorCorrect:1; /* 31 */
+ } _fmt_preF;
+ /*
+ * Register format in revisions F and G
+ */
+ struct {
+ uint32_t MemClkFreq:3; /* 2:0 */
+ uint32_t MemClkFreqVal:1; /* 3 */
+ uint32_t MaxAsyncLat:4; /* 7:4 */
+ uint32_t reserved1:4; /* 11:8 */
+ uint32_t RDqsEn:1; /* 12 */
+ uint32_t reserved2:1; /* 13 */
+ uint32_t DisDramInterface:1; /* 14 */
+ uint32_t PowerDownEn:1; /* 15 */
+ uint32_t PowerDownMode:1; /* 16 */
+ uint32_t FourRankSODimm:1; /* 17 */
+ uint32_t FourRankRDimm:1; /* 18 */
+ uint32_t reserved3:1; /* 19 */
+ uint32_t SlowAccessMode:1; /* 20 */
+ uint32_t reserved4:1; /* 21 */
+ uint32_t BankSwizzleMode:1; /* 22 */
+ uint32_t undocumented1:1; /* 23 */
+ uint32_t DcqBypassMax:4; /* 27:24 */
+ uint32_t FourActWindow:4; /* 31:28 */
+ } _fmt_revFG;
+};
+
+/*
+ * Function 3 - Miscellaneous Control: Scrub Control Register
+ */
+
+union mcreg_scrubctl {
+ uint32_t _val32;
+ struct {
+ uint32_t DramScrub:5; /* 4:0 */
+ uint32_t reserved3:3; /* 7:5 */
+ uint32_t L2Scrub:5; /* 12:8 */
+ uint32_t reserved2:3; /* 15:13 */
+ uint32_t DcacheScrub:5; /* 20:16 */
+ uint32_t reserved1:11; /* 31:21 */
+ } _fmt_cmn;
+};
+
+/*
+ * Function 3 - Miscellaneous Control: On-Line Spare Control Register
+ */
+
+union mcreg_nbcfg {
+ uint32_t _val32;
+ /*
+ * Register format in revisions E and earlier.
+ */
+ struct {
+ uint32_t CpuEccErrEn:1; /* 0 */
+ uint32_t CpuRdDatErrEn:1; /* 1 */
+ uint32_t SyncOnUcEccEn:1; /* 2 */
+ uint32_t SyncPktGenDis:1; /* 3 */
+ uint32_t SyncPktPropDis:1; /* 4 */
+ uint32_t IoMstAbortDis:1; /* 5 */
+ uint32_t CpuErrDis:1; /* 6 */
+ uint32_t IoErrDis:1; /* 7 */
+ uint32_t WdogTmrDis:1; /* 8 */
+ uint32_t WdogTmrCntSel:3; /* 11:9 */
+ uint32_t WdogTmrBaseSel:2; /* 13:12 */
+ uint32_t LdtLinkSel:2; /* 15:14 */
+ uint32_t GenCrcErrByte0:1; /* 16 */
+ uint32_t GenCrcErrByte1:1; /* 17 */
+ uint32_t reserved1:2; /* 19:18 */
+ uint32_t SyncOnWdogEn:1; /* 20 */
+ uint32_t SyncOnAnyErrEn:1; /* 21 */
+ uint32_t EccEn:1; /* 22 */
+ uint32_t ChipKillEccEn:1; /* 23 */
+ uint32_t IoRdDatErrEn:1; /* 24 */
+ uint32_t DisPciCfgCpuErrRsp:1; /* 25 */
+ uint32_t reserved2:1; /* 26 */
+ uint32_t NbMcaToMstCpuEn:1; /* 27 */
+ uint32_t reserved3:4; /* 31:28 */
+ } _fmt_preF;
+ /*
+ * Register format in revisions F and G
+ */
+ struct {
+ uint32_t CpuEccErrEn:1; /* 0 */
+ uint32_t CpuRdDatErrEn:1; /* 1 */
+ uint32_t SyncOnUcEccEn:1; /* 2 */
+ uint32_t SyncPktGenDis:1; /* 3 */
+ uint32_t SyncPktPropDis:1; /* 4 */
+ uint32_t IoMstAbortDis:1; /* 5 */
+ uint32_t CpuErrDis:1; /* 6 */
+ uint32_t IoErrDis:1; /* 7 */
+ uint32_t WdogTmrDis:1; /* 8 */
+ uint32_t WdogTmrCntSel:3; /* 11:9 */
+ uint32_t WdogTmrBaseSel:2; /* 13:12 */
+ uint32_t LdtLinkSel:2; /* 15:14 */
+ uint32_t GenCrcErrByte0:1; /* 16 */
+ uint32_t GenCrcErrByte1:1; /* 17 */
+ uint32_t reserved1:2; /* 19:18 */
+ uint32_t SyncOnWdogEn:1; /* 20 */
+ uint32_t SyncOnAnyErrEn:1; /* 21 */
+ uint32_t EccEn:1; /* 22 */
+ uint32_t ChipKillEccEn:1; /* 23 */
+ uint32_t IoRdDatErrEn:1; /* 24 */
+ uint32_t DisPciCfgCpuErrRsp:1; /* 25 */
+ uint32_t reserved2:1; /* 26 */
+ uint32_t NbMcaToMstCpuEn:1; /* 27 */
+ uint32_t DisTgtAbtCpuErrRsp:1; /* 28 */
+ uint32_t DisMstAbtCpuErrRsp:1; /* 29 */
+ uint32_t SyncOnDramAdrParErrEn:1; /* 30 */
+ uint32_t reserved3:1; /* 31 */
+
+ } _fmt_revFG;
+};
+
+/*
+ * Function 3 - Miscellaneous Control: On-Line Spare Control Register
+ */
+
+union mcreg_sparectl {
+ uint32_t _val32;
+ /*
+ * Register format in revisions F and G
+ */
+ struct {
+ uint32_t SwapEn:1; /* 0 */
+ uint32_t SwapDone:1; /* 1 */
+ uint32_t reserved1:2; /* 3:2 */
+ uint32_t BadDramCs:3; /* 6:4 */
+ uint32_t reserved2:5; /* 11:7 */
+ uint32_t SwapDoneInt:2; /* 13:12 */
+ uint32_t EccErrInt:2; /* 15:14 */
+ uint32_t EccErrCntDramCs:3; /* 18:16 */
+ uint32_t reserved3:1; /* 19 */
+ uint32_t EccErrCntDramChan:1; /* 20 */
+ uint32_t reserved4:2; /* 22:21 */
+ uint32_t EccErrCntWrEn:1; /* 23 */
+ uint32_t EccErrCnt:4; /* 27:24 */
+ uint32_t reserved5:4; /* 31:28 */
+ } _fmt_revFG;
+};
#ifdef __cplusplus
}
diff --git a/usr/src/uts/intel/sys/mca_amd.h b/usr/src/uts/intel/sys/mca_amd.h
index 1b5b6b599f..6d45691c14 100644
--- a/usr/src/uts/intel/sys/mca_amd.h
+++ b/usr/src/uts/intel/sys/mca_amd.h
@@ -52,26 +52,31 @@ extern "C" {
#define AMD_MSR_DC_MASK 0xc0010044
#define AMD_MSR_DC_STATUS 0x401
#define AMD_MSR_DC_ADDR 0x402
+#define AMD_MSR_DC_MISC 0x403
#define AMD_MSR_IC_CTL 0x404
#define AMD_MSR_IC_MASK 0xc0010045
#define AMD_MSR_IC_STATUS 0x405
#define AMD_MSR_IC_ADDR 0x406
+#define AMD_MSR_IC_MISC 0x407
#define AMD_MSR_BU_CTL 0x408
#define AMD_MSR_BU_MASK 0xc0010046
#define AMD_MSR_BU_STATUS 0x409
#define AMD_MSR_BU_ADDR 0x40a
+#define AMD_MSR_BU_MISC 0x40b
#define AMD_MSR_LS_CTL 0x40c
#define AMD_MSR_LS_MASK 0xc0010047
#define AMD_MSR_LS_STATUS 0x40d
#define AMD_MSR_LS_ADDR 0x40e
+#define AMD_MSR_LS_MISC 0x40f
#define AMD_MSR_NB_CTL 0x410
#define AMD_MSR_NB_MASK 0xc0010048
#define AMD_MSR_NB_STATUS 0x411
#define AMD_MSR_NB_ADDR 0x412
+#define AMD_MSR_NB_MISC 0x413
#define AMD_MCG_EN_DC 0x01
#define AMD_MCG_EN_IC 0x02
@@ -95,7 +100,7 @@ extern "C" {
#define AMD_DC_EN_L1TP 0x00000020ULL
#define AMD_DC_EN_L2TP 0x00000040ULL
-#define AMD_DC_CTL_INIT \
+#define AMD_DC_CTL_INIT_CMN \
(AMD_DC_EN_ECCI | AMD_DC_EN_ECCM | AMD_DC_EN_DECC | AMD_DC_EN_DMTP | \
AMD_DC_EN_DSTP | AMD_DC_EN_L1TP | AMD_DC_EN_L2TP)
@@ -116,7 +121,7 @@ extern "C" {
#define AMD_IC_EN_L2TP 0x00000040ULL
#define AMD_IC_EN_RDDE 0x00000200ULL
-#define AMD_IC_CTL_INIT \
+#define AMD_IC_CTL_INIT_CMN \
(AMD_IC_EN_ECCI | AMD_IC_EN_ECCM | AMD_IC_EN_IDP | AMD_IC_EN_IMTP | \
AMD_IC_EN_ISTP | AMD_IC_EN_L1TP | AMD_IC_EN_L2TP)
@@ -149,7 +154,7 @@ extern "C" {
#define AMD_BU_EN_L2T_ECC1_SCR 0x00040000ULL
#define AMD_BU_EN_L2T_ECCM_SCR 0x00080000ULL
-#define AMD_BU_CTL_INIT \
+#define AMD_BU_CTL_INIT_CMN \
(AMD_BU_EN_S_ECC1_TLB | AMD_BU_EN_S_ECC1_HP | \
AMD_BU_EN_S_ECCM_TLB | AMD_BU_EN_S_ECCM_HP | \
AMD_BU_EN_L2T_PAR_ICDC | AMD_BU_EN_L2T_PAR_TLB | \
@@ -172,7 +177,19 @@ extern "C" {
#define AMD_LS_EN_S_RDE_S 0x00000001ULL
#define AMD_LS_EN_S_RDE_L 0x00000002ULL
-#define AMD_LS_CTL_INIT 0ULL
+#define AMD_LS_CTL_INIT_CMN 0ULL
+
+/*
+ * NorthBridge (NB) MCi_MISC - DRAM Errors Threshold Register.
+ */
+#define AMD_NB_MISC_VALID (0x1ULL << 63)
+#define AMD_NB_MISC_CTRP (0x1ULL << 62)
+#define AMD_NB_MISC_LOCKED (0x1ULL << 61)
+#define AMD_NB_MISC_CNTEN (0x1ULL << 51)
+#define AMD_NB_MISC_INTTYPE (0x1ULL << 49)
+#define AMD_NB_MISC_INTTYPE_MASK (0x3ULL << 49)
+#define AMD_NB_MISC_OVRFLW (0x1ULL << 48)
+#define AMD_NB_MISC_ERRCOUNT_MASK (0xfffULL << 32)
/*
* The Northbridge (NB) is configured using both the standard MCA CTL register
@@ -183,46 +200,80 @@ extern "C" {
* The CTL register can be initialized statically, but portions of the NB CFG
* register must be initialized based on the current machine's configuration.
*
- * The MCA NB Control Register maps to MC4_CTL[31:0].
+ * The MCA NB Control Register maps to MC4_CTL[31:0], but we initialize it
+ * via and MSR write of 64 bits so define all as ULL.
*
*/
-#define AMD_NB_EN_CORRECC 0x00000001
-#define AMD_NB_EN_UNCORRECC 0x00000002
-#define AMD_NB_EN_CRCERR0 0x00000004
-#define AMD_NB_EN_CRCERR1 0x00000008
-#define AMD_NB_EN_CRCERR2 0x00000010
-#define AMD_NB_EN_SYNCPKT0 0x00000020
-#define AMD_NB_EN_SYNCPKT1 0x00000040
-#define AMD_NB_EN_SYNCPKT2 0x00000080
-#define AMD_NB_EN_MSTRABRT 0x00000100
-#define AMD_NB_EN_TGTABRT 0x00000200
-#define AMD_NB_EN_GARTTBLWK 0x00000400
-#define AMD_NB_EN_ATOMICRMW 0x00000800
-#define AMD_NB_EN_WCHDOGTMR 0x00001000
-
-#define AMD_NB_CTL_INIT /* All but GARTTBLWK */ \
+#define AMD_NB_EN_CORRECC 0x00000001ULL
+#define AMD_NB_EN_UNCORRECC 0x00000002ULL
+#define AMD_NB_EN_CRCERR0 0x00000004ULL
+#define AMD_NB_EN_CRCERR1 0x00000008ULL
+#define AMD_NB_EN_CRCERR2 0x00000010ULL
+#define AMD_NB_EN_SYNCPKT0 0x00000020ULL
+#define AMD_NB_EN_SYNCPKT1 0x00000040ULL
+#define AMD_NB_EN_SYNCPKT2 0x00000080ULL
+#define AMD_NB_EN_MSTRABRT 0x00000100ULL
+#define AMD_NB_EN_TGTABRT 0x00000200ULL
+#define AMD_NB_EN_GARTTBLWK 0x00000400ULL
+#define AMD_NB_EN_ATOMICRMW 0x00000800ULL
+#define AMD_NB_EN_WCHDOGTMR 0x00001000ULL
+#define AMD_NB_EN_DRAMPAR 0x00040000ULL /* revs F and G */
+
+#define AMD_NB_CTL_INIT_CMN /* Revs B to G; All but GARTTBLWK */ \
(AMD_NB_EN_CORRECC | AMD_NB_EN_UNCORRECC | \
AMD_NB_EN_CRCERR0 | AMD_NB_EN_CRCERR1 | AMD_NB_EN_CRCERR2 | \
AMD_NB_EN_SYNCPKT0 | AMD_NB_EN_SYNCPKT1 | AMD_NB_EN_SYNCPKT2 | \
AMD_NB_EN_MSTRABRT | AMD_NB_EN_TGTABRT | \
AMD_NB_EN_ATOMICRMW | AMD_NB_EN_WCHDOGTMR)
-#define AMD_NB_CFG_CPUECCERREN 0x00000001
-#define AMD_NB_CFG_CPURDDATERREN 0x00000002
-#define AMD_NB_CFG_SYNCONUCECCEN 0x00000004
-#define AMD_NB_CFG_SYNCPKTGENDIS 0x00000008
-#define AMD_NB_CFG_SYNCPKTPROPDIS 0x00000010
-#define AMD_NB_CFG_IOMSTABORTDIS 0x00000020
-#define AMD_NB_CFG_CPUERRDIS 0x00000040
-#define AMD_NB_CFG_IOERRDIS 0x00000080
-#define AMD_NB_CFG_WDOGTMRDIS 0x00000100
-#define AMD_NB_CFG_SYNCONWDOGEN 0x00100000
-#define AMD_NB_CFG_SYNCONANYERREN 0x00200000
-#define AMD_NB_CFG_ECCEN 0x00400000
-#define AMD_NB_CFG_CHIPKILLECCEN 0x00800000
-#define AMD_NB_CFG_IORDDATERREN 0x01000000
-#define AMD_NB_CFG_DISPCICFGCPUERRRSP 0x02000000
-#define AMD_NB_CFG_NBMCATOMSTCPUEN 0x08000000
+#define AMD_NB_CTL_INIT_REV_FG /* Additional bits for revs F and G */ \
+ AMD_NB_EN_DRAMPAR
+
+/*
+ * NB MCA Configuration register
+ */
+#define AMD_NB_CFG_CPUECCERREN 0x00000001
+#define AMD_NB_CFG_CPURDDATERREN 0x00000002
+#define AMD_NB_CFG_SYNCONUCECCEN 0x00000004
+#define AMD_NB_CFG_SYNCPKTGENDIS 0x00000008
+#define AMD_NB_CFG_SYNCPKTPROPDIS 0x00000010
+#define AMD_NB_CFG_IOMSTABORTDIS 0x00000020
+#define AMD_NB_CFG_CPUERRDIS 0x00000040
+#define AMD_NB_CFG_IOERRDIS 0x00000080
+#define AMD_NB_CFG_WDOGTMRDIS 0x00000100
+#define AMD_NB_CFG_SYNCONWDOGEN 0x00100000
+#define AMD_NB_CFG_SYNCONANYERREN 0x00200000
+#define AMD_NB_CFG_ECCEN 0x00400000
+#define AMD_NB_CFG_CHIPKILLECCEN 0x00800000
+#define AMD_NB_CFG_IORDDATERREN 0x01000000
+#define AMD_NB_CFG_DISPCICFGCPUERRRSP 0x02000000
+#define AMD_NB_CFG_NBMCATOMSTCPUEN 0x08000000
+#define AMD_NB_CFG_DISTGTABTCPUERRRSP 0x10000000
+#define AMD_NB_CFG_DISMSTABTCPUERRRSP 0x20000000
+#define AMD_NB_CFG_SYNCONDRAMADRPARERREN 0x40000000 /* Revs F & G */
+
+/*
+ * We do not initialize the NB config with an absolute value; instead we
+ * selectively add some bits and remove others. Note that
+ * AMD_NB_CFG_{ADD,REMOVE}_{CMN,REV_FG} below are not the whole
+ * story here - additional config is performed regarding the watchdog (see
+ * ao_mca.c for details).
+ */
+#define AMD_NB_CFG_ADD_CMN /* Revs B to G */ \
+ (AMD_NB_CFG_DISPCICFGCPUERRRSP | AMD_NB_CFG_SYNCONUCECCEN | \
+ AMD_NB_CFG_CPUECCERREN)
+
+#define AMD_NB_CFG_REMOVE_CMN /* Revs B to G */ \
+ (AMD_NB_CFG_NBMCATOMSTCPUEN | \
+ AMD_NB_CFG_IORDDATERREN | AMD_NB_CFG_SYNCONANYERREN | \
+ AMD_NB_CFG_SYNCONWDOGEN | AMD_NB_CFG_IOERRDIS | \
+ AMD_NB_CFG_IOMSTABORTDIS | AMD_NB_CFG_SYNCPKTPROPDIS | \
+ AMD_NB_CFG_SYNCPKTGENDIS)
+
+#define AMD_NB_CFG_ADD_REV_FG /* Revs F and G */ \
+ AMD_NB_CFG_SYNCONDRAMADRPARERREN
+
+#define AMD_NB_CFG_REMOVE_REV_FG 0x0 /* Revs F and G */
#define AMD_NB_CFG_WDOGTMRCNTSEL_4095 0x00000000
#define AMD_NB_CFG_WDOGTMRCNTSEL_2047 0x00000200
@@ -283,10 +334,14 @@ extern "C" {
#define AMD_NB_REG_SCRUBADDR_LO 0x5c
#define AMD_NB_REG_SCRUBADDR_HI 0x60
-#define AMD_NB_STAT_LDTLINK_MASK 0x0000007000000000
+#define AMD_NB_REG_SPARECTL 0xb0
+
+#define AMD_NB_STAT_DRAMCHANNEL 0x0000020000000000ULL
+#define AMD_NB_STAT_LDTLINK_MASK 0x0000007000000000ULL
#define AMD_NB_STAT_LDTLINK_SHIFT 4
-#define AMD_NB_STAT_ERRCPU1 0x0000000200000000
-#define AMD_NB_STAT_ERRCPU0 0x0000000100000000
+#define AMD_NB_STAT_ERRCPU1 0x0000000200000000ULL
+#define AMD_NB_STAT_ERRCPU0 0x0000000100000000ULL
+
#define AMD_NB_STAT_CKSYND_MASK 0x00000000ff000000 /* syndrome[15:8] */
#define AMD_NB_STAT_CKSYND_SHIFT (24 - 8) /* shift [31:24] to [15:8] */
@@ -298,8 +353,8 @@ extern "C" {
((((uint64_t)(synd) << AMD_NB_STAT_CKSYND_SHIFT) & \
AMD_NB_STAT_CKSYND_MASK) | AMD_BANK_MKSYND(synd))
-#define AMD_ERRCODE_MASK 0x000000000000ffff
-#define AMD_ERREXT_MASK 0x00000000000f0000
+#define AMD_ERRCODE_MASK 0x000000000000ffffULL
+#define AMD_ERREXT_MASK 0x00000000000f0000ULL
#define AMD_ERREXT_SHIFT 16
#define AMD_ERRCODE_TT_MASK 0x000c
diff --git a/usr/src/uts/intel/sys/x86_archext.h b/usr/src/uts/intel/sys/x86_archext.h
index 1799e9e2ef..cf76d4f237 100644
--- a/usr/src/uts/intel/sys/x86_archext.h
+++ b/usr/src/uts/intel/sys/x86_archext.h
@@ -391,6 +391,89 @@ typedef struct mtrrvar {
#define X86_VENDOR_STRLEN 13 /* vendor string max len + \0 */
+/*
+ * Some vendor/family/model/stepping ranges are commonly grouped under
+ * a single identifying banner by the vendor. The following encode
+ * that "revision" in a uint32_t with the 8 most significant bits
+ * identifying the vendor with X86_VENDOR_*, the next 8 identifying the
+ * family, and the remaining 16 typically forming a bitmask of revisions
+ * within that family with more significant bits indicating "later" revisions.
+ */
+
+#define _X86_CHIPREV_VENDOR_MASK 0xff000000u
+#define _X86_CHIPREV_VENDOR_SHIFT 24
+#define _X86_CHIPREV_FAMILY_MASK 0x00ff0000u
+#define _X86_CHIPREV_FAMILY_SHIFT 16
+#define _X86_CHIPREV_REV_MASK 0x0000ffffu
+
+#define _X86_CHIPREV_VENDOR(x) \
+ (((x) & _X86_CHIPREV_VENDOR_MASK) >> _X86_CHIPREV_VENDOR_SHIFT)
+#define _X86_CHIPREV_FAMILY(x) \
+ (((x) & _X86_CHIPREV_FAMILY_MASK) >> _X86_CHIPREV_FAMILY_SHIFT)
+#define _X86_CHIPREV_REV(x) \
+ ((x) & _X86_CHIPREV_REV_MASK)
+
+/* True if x matches in vendor and family and if x matches the given rev mask */
+#define X86_CHIPREV_MATCH(x, mask) \
+ (_X86_CHIPREV_VENDOR(x) == _X86_CHIPREV_VENDOR(mask) && \
+ _X86_CHIPREV_FAMILY(x) == _X86_CHIPREV_FAMILY(mask) && \
+ ((_X86_CHIPREV_REV(x) & _X86_CHIPREV_REV(mask)) != 0))
+
+/* True if x matches in vendor and family and rev is at least minx */
+#define X86_CHIPREV_ATLEAST(x, minx) \
+ (_X86_CHIPREV_VENDOR(x) == _X86_CHIPREV_VENDOR(minx) && \
+ _X86_CHIPREV_FAMILY(x) == _X86_CHIPREV_FAMILY(minx) && \
+ _X86_CHIPREV_REV(x) >= _X86_CHIPREV_REV(minx))
+
+#define _X86_CHIPREV_MKREV(vendor, family, rev) \
+ ((uint32_t)(vendor) << _X86_CHIPREV_VENDOR_SHIFT | \
+ (family) << _X86_CHIPREV_FAMILY_SHIFT | (rev))
+
+/* Revision default */
+#define X86_CHIPREV_UNKNOWN 0x0
+
+/*
+ * Definitions for AMD Family 0xf. Minor revisions C0 and CG are
+ * sufficiently different that we will distinguish them; in all other
+ * case we will identify the major revision.
+ */
+#define X86_CHIPREV_AMD_F_REV_B _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0001)
+#define X86_CHIPREV_AMD_F_REV_C0 _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0002)
+#define X86_CHIPREV_AMD_F_REV_CG _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0004)
+#define X86_CHIPREV_AMD_F_REV_D _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0008)
+#define X86_CHIPREV_AMD_F_REV_E _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0010)
+#define X86_CHIPREV_AMD_F_REV_F _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0020)
+#define X86_CHIPREV_AMD_F_REV_G _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0040)
+
+/*
+ * Various socket/package types, extended as the need to distinguish
+ * a new type arises. The top 8 byte identfies the vendor and the
+ * remaining 24 bits describe 24 socket types.
+ */
+
+#define _X86_SOCKET_VENDOR_SHIFT 24
+#define _X86_SOCKET_VENDOR(x) ((x) >> _X86_SOCKET_VENDOR_SHIFT)
+#define _X86_SOCKET_TYPE_MASK 0x00ffffff
+#define _X86_SOCKET_TYPE(x) ((x) & _X86_SOCKET_TYPE_MASK)
+
+#define _X86_SOCKET_MKVAL(vendor, bitval) \
+ ((uint32_t)(vendor) << _X86_SOCKET_VENDOR_SHIFT | (bitval))
+
+#define X86_SOCKET_MATCH(s, mask) \
+ (_X86_SOCKET_VENDOR(s) == _X86_SOCKET_VENDOR(mask) && \
+ (_X86_SOCKET_TYPE(s) & _X86_SOCKET_TYPE(mask)) != 0)
+
+#define X86_SOCKET_UNKNOWN 0x0
+ /*
+ * AMD socket types
+ */
+#define X86_SOCKET_754 _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x000001)
+#define X86_SOCKET_939 _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x000002)
+#define X86_SOCKET_940 _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x000004)
+#define X86_SOCKET_S1g1 _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x000008)
+#define X86_SOCKET_AM2 _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x000010)
+#define X86_SOCKET_F1207 _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x000020)
+
#if !defined(_ASM)
#if defined(_KERNEL) || defined(_KMEMUSER)
@@ -452,6 +535,10 @@ extern int cpuid_is_cmt(struct cpu *);
extern int cpuid_syscall32_insn(struct cpu *);
extern int getl2cacheinfo(struct cpu *, int *, int *, int *);
+extern uint32_t cpuid_getchiprev(struct cpu *);
+extern const char *cpuid_getchiprevstr(struct cpu *);
+extern uint32_t cpuid_getsockettype(struct cpu *);
+
extern int cpuid_opteron_erratum(struct cpu *, uint_t);
struct cpuid_info;