1f657cd55SCheng Sean Ye /*
2f657cd55SCheng Sean Ye  * CDDL HEADER START
3f657cd55SCheng Sean Ye  *
4f657cd55SCheng Sean Ye  * The contents of this file are subject to the terms of the
5f657cd55SCheng Sean Ye  * Common Development and Distribution License (the "License").
6f657cd55SCheng Sean Ye  * You may not use this file except in compliance with the License.
7f657cd55SCheng Sean Ye  *
8f657cd55SCheng Sean Ye  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9f657cd55SCheng Sean Ye  * or http://www.opensolaris.org/os/licensing.
10f657cd55SCheng Sean Ye  * See the License for the specific language governing permissions
11f657cd55SCheng Sean Ye  * and limitations under the License.
12f657cd55SCheng Sean Ye  *
13f657cd55SCheng Sean Ye  * When distributing Covered Code, include this CDDL HEADER in each
14f657cd55SCheng Sean Ye  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15f657cd55SCheng Sean Ye  * If applicable, add the following below this CDDL HEADER, with the
16f657cd55SCheng Sean Ye  * fields enclosed by brackets "[]" replaced with your own identifying
17f657cd55SCheng Sean Ye  * information: Portions Copyright [yyyy] [name of copyright owner]
18f657cd55SCheng Sean Ye  *
19f657cd55SCheng Sean Ye  * CDDL HEADER END
20f657cd55SCheng Sean Ye  */
21f657cd55SCheng Sean Ye 
22f657cd55SCheng Sean Ye /*
23e8ee2240SAdrian Frost  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24f657cd55SCheng Sean Ye  * Use is subject to license terms.
25f657cd55SCheng Sean Ye  */
26f657cd55SCheng Sean Ye 
27*598f111bSJohn Levon /*
28*598f111bSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
29*598f111bSJohn Levon  */
30*598f111bSJohn Levon 
31f657cd55SCheng Sean Ye #include <sys/types.h>
32f657cd55SCheng Sean Ye #include <sys/time.h>
33f657cd55SCheng Sean Ye #include <sys/nvpair.h>
34f657cd55SCheng Sean Ye #include <sys/cmn_err.h>
35f657cd55SCheng Sean Ye #include <sys/cred.h>
36f657cd55SCheng Sean Ye #include <sys/open.h>
37f657cd55SCheng Sean Ye #include <sys/ddi.h>
38f657cd55SCheng Sean Ye #include <sys/sunddi.h>
39f657cd55SCheng Sean Ye #include <sys/conf.h>
40f657cd55SCheng Sean Ye #include <sys/modctl.h>
41f657cd55SCheng Sean Ye #include <sys/cyclic.h>
42f657cd55SCheng Sean Ye #include <sys/errorq.h>
43f657cd55SCheng Sean Ye #include <sys/stat.h>
44f657cd55SCheng Sean Ye #include <sys/cpuvar.h>
45f657cd55SCheng Sean Ye #include <sys/mc_intel.h>
46f657cd55SCheng Sean Ye #include <sys/mc.h>
47f657cd55SCheng Sean Ye #include <sys/fm/protocol.h>
48f657cd55SCheng Sean Ye #include "nhm_log.h"
49f657cd55SCheng Sean Ye #include "intel_nhm.h"
50f657cd55SCheng Sean Ye 
51f657cd55SCheng Sean Ye extern nvlist_t *inhm_mc_nvl[MAX_CPU_NODES];
52f657cd55SCheng Sean Ye extern char closed_page;
53f657cd55SCheng Sean Ye extern char ecc_enabled;
54f657cd55SCheng Sean Ye extern char lockstep[MAX_CPU_NODES];
55f657cd55SCheng Sean Ye extern char mirror_mode[MAX_CPU_NODES];
56f657cd55SCheng Sean Ye extern char spare_channel[MAX_CPU_NODES];
57f657cd55SCheng Sean Ye 
58f657cd55SCheng Sean Ye static void
inhm_vrank(nvlist_t * vrank,int num,uint64_t dimm_base,uint64_t limit,uint32_t sinterleave,uint32_t cinterleave,uint32_t rinterleave,uint32_t sway,uint32_t cway,uint32_t rway)59f657cd55SCheng Sean Ye inhm_vrank(nvlist_t *vrank, int num, uint64_t dimm_base, uint64_t limit,
60f657cd55SCheng Sean Ye     uint32_t sinterleave, uint32_t cinterleave, uint32_t rinterleave,
61f657cd55SCheng Sean Ye     uint32_t sway, uint32_t cway, uint32_t rway)
62f657cd55SCheng Sean Ye {
63f657cd55SCheng Sean Ye 	char buf[128];
64f657cd55SCheng Sean Ye 
65f657cd55SCheng Sean Ye 	(void) snprintf(buf, sizeof (buf), "dimm-rank-base-%d", num);
66f657cd55SCheng Sean Ye 	(void) nvlist_add_uint64(vrank, buf, dimm_base);
67f657cd55SCheng Sean Ye 	(void) snprintf(buf, sizeof (buf), "dimm-rank-limit-%d", num);
68f657cd55SCheng Sean Ye 	(void) nvlist_add_uint64(vrank, buf, dimm_base + limit);
69f657cd55SCheng Sean Ye 	if (sinterleave > 1) {
70f657cd55SCheng Sean Ye 		(void) snprintf(buf, sizeof (buf), "dimm-socket-interleave-%d",
71f657cd55SCheng Sean Ye 		    num);
72f657cd55SCheng Sean Ye 		(void) nvlist_add_uint32(vrank, buf, sinterleave);
73f657cd55SCheng Sean Ye 		(void) snprintf(buf, sizeof (buf),
74f657cd55SCheng Sean Ye 		    "dimm-socket-interleave-way-%d", num);
75f657cd55SCheng Sean Ye 		(void) nvlist_add_uint32(vrank, buf, sway);
76f657cd55SCheng Sean Ye 	}
77f657cd55SCheng Sean Ye 	if (cinterleave > 1) {
78f657cd55SCheng Sean Ye 		(void) snprintf(buf, sizeof (buf), "dimm-channel-interleave-%d",
79f657cd55SCheng Sean Ye 		    num);
80f657cd55SCheng Sean Ye 		(void) nvlist_add_uint32(vrank, buf, cinterleave);
81f657cd55SCheng Sean Ye 		(void) snprintf(buf, sizeof (buf),
82f657cd55SCheng Sean Ye 		    "dimm-channel-interleave-way-%d", num);
83f657cd55SCheng Sean Ye 		(void) nvlist_add_uint32(vrank, buf, cway);
84f657cd55SCheng Sean Ye 	}
85f657cd55SCheng Sean Ye 	if (rinterleave > 1) {
86f657cd55SCheng Sean Ye 		(void) snprintf(buf, sizeof (buf), "dimm-rank-interleave-%d",
87f657cd55SCheng Sean Ye 		    num);
88f657cd55SCheng Sean Ye 		(void) nvlist_add_uint32(vrank, buf, rinterleave);
89f657cd55SCheng Sean Ye 		(void) snprintf(buf, sizeof (buf),
90f657cd55SCheng Sean Ye 		    "dimm-rank-interleave-way-%d", num);
91f657cd55SCheng Sean Ye 		(void) nvlist_add_uint32(vrank, buf, rway);
92f657cd55SCheng Sean Ye 	}
93f657cd55SCheng Sean Ye }
94f657cd55SCheng Sean Ye 
95f657cd55SCheng Sean Ye static void
inhm_rank(nvlist_t * newdimm,nhm_dimm_t * nhm_dimm,uint32_t node,uint8_t channel,uint32_t dimm,uint64_t rank_size)96f657cd55SCheng Sean Ye inhm_rank(nvlist_t *newdimm, nhm_dimm_t *nhm_dimm, uint32_t node,
97f657cd55SCheng Sean Ye     uint8_t channel, uint32_t dimm, uint64_t rank_size)
98f657cd55SCheng Sean Ye {
99f657cd55SCheng Sean Ye 	nvlist_t **newrank;
100f657cd55SCheng Sean Ye 	int num;
101f657cd55SCheng Sean Ye 	int i;
102f657cd55SCheng Sean Ye 	uint64_t dimm_base;
103f657cd55SCheng Sean Ye 	uint64_t vrank_sz;
104f657cd55SCheng Sean Ye 	uint64_t rank_addr;
105f657cd55SCheng Sean Ye 	uint64_t pa;
106f657cd55SCheng Sean Ye 	uint32_t sinterleave, cinterleave, rinterleave;
107f657cd55SCheng Sean Ye 	uint32_t sway, cway, rway;
108f657cd55SCheng Sean Ye 
109f657cd55SCheng Sean Ye 	newrank = kmem_zalloc(sizeof (nvlist_t *) * nhm_dimm->nranks, KM_SLEEP);
110f657cd55SCheng Sean Ye 	for (i = 0; i < nhm_dimm->nranks; i++) {
111f657cd55SCheng Sean Ye 		(void) nvlist_alloc(&newrank[i], NV_UNIQUE_NAME, KM_SLEEP);
112f657cd55SCheng Sean Ye 		rank_addr = 0;
113f657cd55SCheng Sean Ye 		num = 0;
114f657cd55SCheng Sean Ye 		while (rank_addr < rank_size) {
115f657cd55SCheng Sean Ye 			pa = dimm_to_addr(node, channel, dimm * 4 + i,
116f657cd55SCheng Sean Ye 			    rank_addr, &dimm_base, &vrank_sz, &sinterleave,
117f657cd55SCheng Sean Ye 			    &cinterleave, &rinterleave, &sway, &cway, &rway);
118e8ee2240SAdrian Frost 			if (pa == -1 || vrank_sz == 0)
119f657cd55SCheng Sean Ye 				break;
120f657cd55SCheng Sean Ye 			inhm_vrank(newrank[i], num, dimm_base,
121f657cd55SCheng Sean Ye 			    vrank_sz * sinterleave * cinterleave * rinterleave,
122f657cd55SCheng Sean Ye 			    sinterleave, cinterleave, rinterleave, sway, cway,
123f657cd55SCheng Sean Ye 			    rway);
124f657cd55SCheng Sean Ye 			rank_addr += vrank_sz;
125f657cd55SCheng Sean Ye 			num++;
126f657cd55SCheng Sean Ye 		}
127f657cd55SCheng Sean Ye 
128f657cd55SCheng Sean Ye 	}
129f657cd55SCheng Sean Ye 	(void) nvlist_add_nvlist_array(newdimm, MCINTEL_NVLIST_RANKS, newrank,
130f657cd55SCheng Sean Ye 	    nhm_dimm->nranks);
131f657cd55SCheng Sean Ye 	for (i = 0; i < nhm_dimm->nranks; i++)
132f657cd55SCheng Sean Ye 		nvlist_free(newrank[i]);
133f657cd55SCheng Sean Ye 	kmem_free(newrank, sizeof (nvlist_t *) * nhm_dimm->nranks);
134f657cd55SCheng Sean Ye }
135f657cd55SCheng Sean Ye 
136f657cd55SCheng Sean Ye static nvlist_t *
inhm_dimm(nhm_dimm_t * nhm_dimm,uint32_t node,uint8_t channel,uint32_t dimm)137f657cd55SCheng Sean Ye inhm_dimm(nhm_dimm_t *nhm_dimm, uint32_t node, uint8_t channel, uint32_t dimm)
138f657cd55SCheng Sean Ye {
139f657cd55SCheng Sean Ye 	nvlist_t *newdimm;
140f657cd55SCheng Sean Ye 	uint8_t t;
141f657cd55SCheng Sean Ye 	char sbuf[65];
142f657cd55SCheng Sean Ye 
143f657cd55SCheng Sean Ye 	(void) nvlist_alloc(&newdimm, NV_UNIQUE_NAME, KM_SLEEP);
144f657cd55SCheng Sean Ye 	(void) nvlist_add_uint32(newdimm, "dimm-number", dimm);
145f657cd55SCheng Sean Ye 
146f657cd55SCheng Sean Ye 	if (nhm_dimm->dimm_size >= 1024*1024*1024) {
147f657cd55SCheng Sean Ye 		(void) snprintf(sbuf, sizeof (sbuf), "%dG",
148f657cd55SCheng Sean Ye 		    (int)(nhm_dimm->dimm_size / (1024*1024*1024)));
149f657cd55SCheng Sean Ye 	} else {
150f657cd55SCheng Sean Ye 		(void) snprintf(sbuf, sizeof (sbuf), "%dM",
151f657cd55SCheng Sean Ye 		    (int)(nhm_dimm->dimm_size / (1024*1024)));
152f657cd55SCheng Sean Ye 	}
153f657cd55SCheng Sean Ye 	(void) nvlist_add_string(newdimm, "dimm-size", sbuf);
154f657cd55SCheng Sean Ye 	(void) nvlist_add_uint64(newdimm, "size", nhm_dimm->dimm_size);
155f657cd55SCheng Sean Ye 	(void) nvlist_add_uint32(newdimm, "nbanks", (uint32_t)nhm_dimm->nbanks);
156f657cd55SCheng Sean Ye 	(void) nvlist_add_uint32(newdimm, "ncolumn",
157f657cd55SCheng Sean Ye 	    (uint32_t)nhm_dimm->ncolumn);
158f657cd55SCheng Sean Ye 	(void) nvlist_add_uint32(newdimm, "nrow", (uint32_t)nhm_dimm->nrow);
159f657cd55SCheng Sean Ye 	(void) nvlist_add_uint32(newdimm, "width", (uint32_t)nhm_dimm->width);
160f657cd55SCheng Sean Ye 	(void) nvlist_add_uint32(newdimm, "ranks", (uint32_t)nhm_dimm->nranks);
161f657cd55SCheng Sean Ye 	inhm_rank(newdimm, nhm_dimm, node, channel, dimm,
162f657cd55SCheng Sean Ye 	    nhm_dimm->dimm_size / nhm_dimm->nranks);
163*598f111bSJohn Levon 	if (nhm_dimm->manufacturer[0]) {
164f657cd55SCheng Sean Ye 		t = sizeof (nhm_dimm->manufacturer);
165f657cd55SCheng Sean Ye 		(void) strncpy(sbuf, nhm_dimm->manufacturer, t);
166f657cd55SCheng Sean Ye 		sbuf[t] = 0;
167f657cd55SCheng Sean Ye 		(void) nvlist_add_string(newdimm, "manufacturer", sbuf);
168f657cd55SCheng Sean Ye 	}
169*598f111bSJohn Levon 	if (nhm_dimm->serial_number[0]) {
170f657cd55SCheng Sean Ye 		t = sizeof (nhm_dimm->serial_number);
171f657cd55SCheng Sean Ye 		(void) strncpy(sbuf, nhm_dimm->serial_number, t);
172f657cd55SCheng Sean Ye 		sbuf[t] = 0;
173f657cd55SCheng Sean Ye 		(void) nvlist_add_string(newdimm, FM_FMRI_HC_SERIAL_ID, sbuf);
174f657cd55SCheng Sean Ye 	}
175*598f111bSJohn Levon 	if (nhm_dimm->part_number[0]) {
176f657cd55SCheng Sean Ye 		t = sizeof (nhm_dimm->part_number);
177f657cd55SCheng Sean Ye 		(void) strncpy(sbuf, nhm_dimm->part_number, t);
178f657cd55SCheng Sean Ye 		sbuf[t] = 0;
179f657cd55SCheng Sean Ye 		(void) nvlist_add_string(newdimm, FM_FMRI_HC_PART, sbuf);
180f657cd55SCheng Sean Ye 	}
181*598f111bSJohn Levon 	if (nhm_dimm->revision[0]) {
182f657cd55SCheng Sean Ye 		t = sizeof (nhm_dimm->revision);
183f657cd55SCheng Sean Ye 		(void) strncpy(sbuf, nhm_dimm->revision, t);
184f657cd55SCheng Sean Ye 		sbuf[t] = 0;
185f657cd55SCheng Sean Ye 		(void) nvlist_add_string(newdimm, FM_FMRI_HC_REVISION, sbuf);
186f657cd55SCheng Sean Ye 	}
187f657cd55SCheng Sean Ye 	t = sizeof (nhm_dimm->label);
188f657cd55SCheng Sean Ye 	(void) strncpy(sbuf, nhm_dimm->label, t);
189f657cd55SCheng Sean Ye 	sbuf[t] = 0;
190f657cd55SCheng Sean Ye 	(void) nvlist_add_string(newdimm, FM_FAULT_FRU_LABEL, sbuf);
191f657cd55SCheng Sean Ye 	return (newdimm);
192f657cd55SCheng Sean Ye }
193f657cd55SCheng Sean Ye 
194f657cd55SCheng Sean Ye static void
inhm_dimmlist(uint32_t node,nvlist_t * nvl)195f657cd55SCheng Sean Ye inhm_dimmlist(uint32_t node, nvlist_t *nvl)
196f657cd55SCheng Sean Ye {
197f657cd55SCheng Sean Ye 	nvlist_t **dimmlist;
198f657cd55SCheng Sean Ye 	nvlist_t **newchannel;
199f657cd55SCheng Sean Ye 	int nchannels = CHANNELS_PER_MEMORY_CONTROLLER;
200f657cd55SCheng Sean Ye 	int nd;
201f657cd55SCheng Sean Ye 	uint8_t i, j;
202f657cd55SCheng Sean Ye 	nhm_dimm_t **dimmpp;
203f657cd55SCheng Sean Ye 	nhm_dimm_t *dimmp;
204f657cd55SCheng Sean Ye 
205f657cd55SCheng Sean Ye 	dimmlist =  kmem_zalloc(sizeof (nvlist_t *) * MAX_DIMMS_PER_CHANNEL,
206f657cd55SCheng Sean Ye 	    KM_SLEEP);
207f657cd55SCheng Sean Ye 	newchannel = kmem_zalloc(sizeof (nvlist_t *) * nchannels, KM_SLEEP);
208f657cd55SCheng Sean Ye 	dimmpp = &nhm_dimms[node * CHANNELS_PER_MEMORY_CONTROLLER *
209f657cd55SCheng Sean Ye 	    MAX_DIMMS_PER_CHANNEL];
210f657cd55SCheng Sean Ye 	(void) nvlist_add_string(nvl, "memory-policy",
211f657cd55SCheng Sean Ye 	    closed_page ? "closed-page" : "open-page");
212f657cd55SCheng Sean Ye 	(void) nvlist_add_string(nvl, "memory-ecc",
213f657cd55SCheng Sean Ye 	    ecc_enabled ? lockstep[node] ? "x8" : "x4" : "no");
214f657cd55SCheng Sean Ye 	for (i = 0; i < nchannels; i++) {
215f657cd55SCheng Sean Ye 		(void) nvlist_alloc(&newchannel[i], NV_UNIQUE_NAME, KM_SLEEP);
216f657cd55SCheng Sean Ye 		(void) nvlist_add_string(newchannel[i], "channel-mode",
217f657cd55SCheng Sean Ye 		    CHANNEL_DISABLED(MC_STATUS_RD(node), i) ? "disabled" :
218f657cd55SCheng Sean Ye 		    i != 2 && lockstep[node] ? "lockstep" :
219f657cd55SCheng Sean Ye 		    i != 2 && mirror_mode[node] ?
220f657cd55SCheng Sean Ye 		    REDUNDANCY_LOSS(MC_RAS_STATUS_RD(node)) ?
221f657cd55SCheng Sean Ye 		    "redundancy-loss" : "mirror" :
222f657cd55SCheng Sean Ye 		    i == 2 && spare_channel[node] &&
223f657cd55SCheng Sean Ye 		    !REDUNDANCY_LOSS(MC_RAS_STATUS_RD(node)) ? "spare" :
224f657cd55SCheng Sean Ye 		    "independent");
225f657cd55SCheng Sean Ye 		nd = 0;
226f657cd55SCheng Sean Ye 		for (j = 0; j < MAX_DIMMS_PER_CHANNEL; j++) {
227f657cd55SCheng Sean Ye 			dimmp = *dimmpp;
228f657cd55SCheng Sean Ye 			if (dimmp != NULL) {
229f657cd55SCheng Sean Ye 				dimmlist[nd] = inhm_dimm(dimmp, node, i,
230f657cd55SCheng Sean Ye 				    (uint32_t)j);
231f657cd55SCheng Sean Ye 				nd++;
232f657cd55SCheng Sean Ye 			}
233f657cd55SCheng Sean Ye 			dimmpp++;
234f657cd55SCheng Sean Ye 		}
235f657cd55SCheng Sean Ye 		if (nd) {
236f657cd55SCheng Sean Ye 			(void) nvlist_add_nvlist_array(newchannel[i],
237f657cd55SCheng Sean Ye 			    "memory-dimms", dimmlist, nd);
238f657cd55SCheng Sean Ye 			for (j = 0; j < nd; j++)
239f657cd55SCheng Sean Ye 				nvlist_free(dimmlist[j]);
240f657cd55SCheng Sean Ye 		}
241f657cd55SCheng Sean Ye 	}
242f657cd55SCheng Sean Ye 	(void) nvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_MC, newchannel,
243f657cd55SCheng Sean Ye 	    nchannels);
244f657cd55SCheng Sean Ye 	for (i = 0; i < nchannels; i++)
245f657cd55SCheng Sean Ye 		nvlist_free(newchannel[i]);
246f657cd55SCheng Sean Ye 	kmem_free(dimmlist, sizeof (nvlist_t *) * MAX_DIMMS_PER_CHANNEL);
247f657cd55SCheng Sean Ye 	kmem_free(newchannel, sizeof (nvlist_t *) * nchannels);
248f657cd55SCheng Sean Ye }
249f657cd55SCheng Sean Ye 
250f657cd55SCheng Sean Ye char *
inhm_mc_name()251f657cd55SCheng Sean Ye inhm_mc_name()
252f657cd55SCheng Sean Ye {
253ee9ef9e5SAdrian Frost 	return (NHM_INTERCONNECT);
254f657cd55SCheng Sean Ye }
255f657cd55SCheng Sean Ye 
256f657cd55SCheng Sean Ye void
inhm_create_nvl(int chip)257f657cd55SCheng Sean Ye inhm_create_nvl(int chip)
258f657cd55SCheng Sean Ye {
259f657cd55SCheng Sean Ye 	nvlist_t *nvl;
260f657cd55SCheng Sean Ye 
261f657cd55SCheng Sean Ye 	(void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
262f657cd55SCheng Sean Ye 	(void) nvlist_add_uint8(nvl, MCINTEL_NVLIST_VERSTR,
263f657cd55SCheng Sean Ye 	    MCINTEL_NVLIST_VERS);
264f657cd55SCheng Sean Ye 	(void) nvlist_add_string(nvl, MCINTEL_NVLIST_MEM, inhm_mc_name());
265f657cd55SCheng Sean Ye 	(void) nvlist_add_uint8(nvl, MCINTEL_NVLIST_NMEM, 1);
266f657cd55SCheng Sean Ye 	(void) nvlist_add_uint8(nvl, MCINTEL_NVLIST_NRANKS, 4);
267f657cd55SCheng Sean Ye 	inhm_dimmlist(chip, nvl);
268f657cd55SCheng Sean Ye 
269f657cd55SCheng Sean Ye 	nvlist_free(inhm_mc_nvl[chip]);
270f657cd55SCheng Sean Ye 	inhm_mc_nvl[chip] = nvl;
271f657cd55SCheng Sean Ye }
272