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 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 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 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 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 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 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 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 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, NULL } 314 }; 315 316 int 317 _init(void) 318 { 319 int err; 320 321 err = nhm_init(); 322 if (err == 0 && (err = mod_install(&modlinkage)) == 0) { 323 rw_init(&inhm_mc_lock, NULL, RW_DRIVER, NULL); 324 init_dimms(); 325 } 326 327 return (err); 328 } 329 330 int 331 _info(struct modinfo *modinfop) 332 { 333 return (mod_info(&modlinkage, modinfop)); 334 } 335 336 int 337 _fini(void) 338 { 339 int err; 340 341 if ((err = mod_remove(&modlinkage)) == 0) { 342 nhm_unload(); 343 rw_destroy(&inhm_mc_lock); 344 } 345 346 return (err); 347 }