1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/nvpair.h>
30 #include <sys/cmn_err.h>
31 #include <sys/cred.h>
32 #include <sys/open.h>
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/conf.h>
36 #include <sys/modctl.h>
37 #include <sys/cyclic.h>
38 #include <sys/errorq.h>
39 #include <sys/stat.h>
40 #include <sys/cpuvar.h>
41 #include <sys/mc_intel.h>
42 #include <sys/mc.h>
43 #include <sys/fm/protocol.h>
44 #include "nhm_log.h"
45 #include "intel_nhm.h"
46
47 int max_bus_number = 0xff;
48
49 nvlist_t *inhm_mc_nvl[MAX_CPU_NODES];
50 krwlock_t inhm_mc_lock;
51
52 char *inhm_mc_snapshot[MAX_CPU_NODES];
53 uint_t nhm_config_gen;
54 uint_t inhm_mc_snapshotgen;
55 size_t inhm_mc_snapshotsz[MAX_CPU_NODES];
56 static dev_info_t *inhm_dip;
57 int nhm_allow_detach = 0;
58
59 extern int nhm_patrol_scrub;
60 extern int nhm_demand_scrub;
61 extern int nhm_no_smbios;
62 extern int nhm_smbios_serial;
63 extern int nhm_smbios_manufacturer;
64 extern int nhm_smbios_part_number;
65 extern int nhm_smbios_version;
66 extern int nhm_smbios_label;
67
68 extern void inhm_create_nvl(int);
69 extern char *inhm_mc_name(void);
70 extern void init_dimms(void);
71 extern void nhm_smbios();
72
73 static void
inhm_mc_snapshot_destroy()74 inhm_mc_snapshot_destroy()
75 {
76 int i;
77
78 ASSERT(RW_LOCK_HELD(&inhm_mc_lock));
79
80 for (i = 0; i < MAX_CPU_NODES; i++) {
81 if (inhm_mc_snapshot[i] == NULL)
82 continue;
83
84 kmem_free(inhm_mc_snapshot[i], inhm_mc_snapshotsz[i]);
85 inhm_mc_snapshot[i] = NULL;
86 inhm_mc_snapshotsz[i] = 0;
87 }
88 inhm_mc_snapshotgen++;
89 }
90
91 static int
inhm_mc_snapshot_update()92 inhm_mc_snapshot_update()
93 {
94 int i;
95 int rt = 0;
96
97 ASSERT(RW_LOCK_HELD(&inhm_mc_lock));
98
99 for (i = 0; i < MAX_CPU_NODES; i++) {
100 if (inhm_mc_snapshot[i] != NULL)
101 continue;
102
103 if (nvlist_pack(inhm_mc_nvl[i], &inhm_mc_snapshot[i],
104 &inhm_mc_snapshotsz[i], NV_ENCODE_XDR, KM_SLEEP) != 0)
105 rt = -1;
106 }
107
108 return (rt);
109 }
110
111 /*ARGSUSED*/
112 static int
inhm_mc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)113 inhm_mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
114 int *rvalp)
115 {
116 int rc = 0;
117 int chip;
118 mc_snapshot_info_t mcs;
119
120 if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT)
121 return (EINVAL);
122
123 rw_enter(&inhm_mc_lock, RW_READER);
124 chip = getminor(dev) % MAX_CPU_NODES;
125 if (inhm_mc_nvl[chip] == NULL ||
126 inhm_mc_snapshotgen != nhm_config_gen) {
127 if (!rw_tryupgrade(&inhm_mc_lock)) {
128 rw_exit(&inhm_mc_lock);
129 return (EAGAIN);
130 }
131 if (inhm_mc_nvl[chip])
132 inhm_mc_snapshot_destroy();
133 inhm_create_nvl(chip);
134 nhm_config_gen = inhm_mc_snapshotgen;
135 (void) inhm_mc_snapshot_update();
136 }
137 switch (cmd) {
138 case MC_IOC_SNAPSHOT_INFO:
139 mcs.mcs_size = (uint32_t)inhm_mc_snapshotsz[chip];
140 mcs.mcs_gen = inhm_mc_snapshotgen;
141
142 if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t),
143 mode) < 0)
144 rc = EFAULT;
145 break;
146 case MC_IOC_SNAPSHOT:
147 if (ddi_copyout(inhm_mc_snapshot[chip], (void *)arg,
148 inhm_mc_snapshotsz[chip], mode) < 0)
149 rc = EFAULT;
150 break;
151 }
152 rw_exit(&inhm_mc_lock);
153 return (rc);
154 }
155
156 /*ARGSUSED*/
157 static int
inhm_mc_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)158 inhm_mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
159 void **result)
160 {
161 if ((infocmd != DDI_INFO_DEVT2DEVINFO &&
162 infocmd != DDI_INFO_DEVT2INSTANCE) || inhm_dip == NULL) {
163 *result = NULL;
164 return (DDI_FAILURE);
165 }
166 if (infocmd == DDI_INFO_DEVT2DEVINFO)
167 *result = inhm_dip;
168 else
169 *result = (void *)(uintptr_t)ddi_get_instance(inhm_dip);
170 return (0);
171 }
172
173 static int
inhm_mc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)174 inhm_mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
175 {
176 int i;
177 char buf[64];
178
179 if (cmd == DDI_RESUME) {
180 nhm_dev_reinit();
181 nhm_scrubber_enable();
182 nhm_smbios();
183 return (DDI_SUCCESS);
184 }
185 if (cmd != DDI_ATTACH)
186 return (DDI_FAILURE);
187 if (inhm_dip == NULL) {
188 inhm_dip = dip;
189 nhm_pci_cfg_setup(dip);
190 (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
191 inhm_mc_name());
192 if (nhm_dev_init()) {
193 nhm_pci_cfg_free();
194 inhm_dip = NULL;
195 return (DDI_FAILURE);
196 }
197 ddi_set_name_addr(dip, "1");
198 for (i = 0; i < MAX_CPU_NODES; i++) {
199 (void) snprintf(buf, sizeof (buf), "mc-intel-%d", i);
200 if (ddi_create_minor_node(dip, buf, S_IFCHR,
201 i, "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
202 cmn_err(CE_WARN, "failed to create minor node"
203 " for memory controller %d\n", i);
204 }
205 }
206 cmi_hdl_walk(inhm_mc_register, NULL, NULL, NULL);
207 nhm_patrol_scrub = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
208 DDI_PROP_DONTPASS, "patrol-scrub", 0);
209 nhm_demand_scrub = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
210 DDI_PROP_DONTPASS, "demand-scrub", 0);
211 nhm_no_smbios = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
212 DDI_PROP_DONTPASS, "no-smbios", 0);
213 nhm_smbios_serial = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
214 DDI_PROP_DONTPASS, "smbios-dimm-serial", 1);
215 nhm_smbios_manufacturer = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
216 DDI_PROP_DONTPASS, "smbios-dimm-manufacturer", 1);
217 nhm_smbios_part_number = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
218 DDI_PROP_DONTPASS, "smbios-dimm-part-number", 1);
219 nhm_smbios_version = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
220 DDI_PROP_DONTPASS, "smbios-dimme-version", 1);
221 nhm_smbios_label = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
222 DDI_PROP_DONTPASS, "smbios-dimm-label", 1);
223 nhm_scrubber_enable();
224 nhm_smbios();
225 }
226
227 return (DDI_SUCCESS);
228 }
229
230 /*ARGSUSED*/
231 static int
inhm_mc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)232 inhm_mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
233 {
234 if (nhm_allow_detach && cmd == DDI_DETACH && dip == inhm_dip) {
235 rw_enter(&inhm_mc_lock, RW_WRITER);
236 inhm_mc_snapshot_destroy();
237 rw_exit(&inhm_mc_lock);
238 inhm_dip = NULL;
239 return (DDI_SUCCESS);
240 } else if (cmd == DDI_SUSPEND || cmd == DDI_PM_SUSPEND) {
241 return (DDI_SUCCESS);
242 } else {
243 return (DDI_FAILURE);
244 }
245 }
246
247 /*ARGSUSED*/
248 static int
inhm_mc_open(dev_t * devp,int flag,int otyp,cred_t * credp)249 inhm_mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
250 {
251 if (otyp != OTYP_CHR)
252 return (EINVAL);
253
254 rw_enter(&inhm_mc_lock, RW_READER);
255 if (getminor(*devp) >= MAX_CPU_NODES) {
256 rw_exit(&inhm_mc_lock);
257 return (EINVAL);
258 }
259 rw_exit(&inhm_mc_lock);
260
261 return (0);
262 }
263
264 /*ARGSUSED*/
265 static int
inhm_mc_close(dev_t dev,int flag,int otyp,cred_t * credp)266 inhm_mc_close(dev_t dev, int flag, int otyp, cred_t *credp)
267 {
268 return (0);
269 }
270
271
272 static struct cb_ops inhm_mc_cb_ops = {
273 inhm_mc_open,
274 inhm_mc_close,
275 nodev, /* not a block driver */
276 nodev, /* no print routine */
277 nodev, /* no dump routine */
278 nodev, /* no read routine */
279 nodev, /* no write routine */
280 inhm_mc_ioctl,
281 nodev, /* no devmap routine */
282 nodev, /* no mmap routine */
283 nodev, /* no segmap routine */
284 nochpoll, /* no chpoll routine */
285 ddi_prop_op,
286 0, /* not a STREAMS driver */
287 D_NEW | D_MP, /* safe for multi-thread/multi-processor */
288 };
289
290 static struct dev_ops inhm_mc_ops = {
291 DEVO_REV, /* devo_rev */
292 0, /* devo_refcnt */
293 inhm_mc_getinfo, /* devo_getinfo */
294 nulldev, /* devo_identify */
295 nulldev, /* devo_probe */
296 inhm_mc_attach, /* devo_attach */
297 inhm_mc_detach, /* devo_detach */
298 nodev, /* devo_reset */
299 &inhm_mc_cb_ops, /* devo_cb_ops */
300 NULL, /* devo_bus_ops */
301 NULL, /* devo_power */
302 ddi_quiesce_not_needed, /* devo_quiesce */
303 };
304
305 static struct modldrv modldrv = {
306 &mod_driverops,
307 "Intel QuickPath Memory Controller Hub Module",
308 &inhm_mc_ops
309 };
310
311 static struct modlinkage modlinkage = {
312 MODREV_1,
313 (void *)&modldrv,
314 NULL
315 };
316
317 int
_init(void)318 _init(void)
319 {
320 int err;
321
322 err = nhm_init();
323 if (err == 0 && (err = mod_install(&modlinkage)) == 0) {
324 rw_init(&inhm_mc_lock, NULL, RW_DRIVER, NULL);
325 init_dimms();
326 }
327
328 return (err);
329 }
330
331 int
_info(struct modinfo * modinfop)332 _info(struct modinfo *modinfop)
333 {
334 return (mod_info(&modlinkage, modinfop));
335 }
336
337 int
_fini(void)338 _fini(void)
339 {
340 int err;
341
342 if ((err = mod_remove(&modlinkage)) == 0) {
343 nhm_unload();
344 rw_destroy(&inhm_mc_lock);
345 }
346
347 return (err);
348 }
349