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