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 2008 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 "nb_log.h" 45 #include "nb5000.h" 46 47 char _depends_on[] = "drv/smbios"; 48 49 nvlist_t *inb_mc_nvl; 50 krwlock_t inb_mc_lock; 51 52 char *inb_mc_snapshot; 53 uint_t nb_config_gen; 54 uint_t inb_mc_snapshotgen; 55 size_t inb_mc_snapshotsz; 56 static dev_info_t *inb_dip; 57 int nb_allow_detach = 0; 58 int nb_no_smbios; 59 60 static uint64_t 61 rank_to_base(uint8_t branch, uint8_t rank, uint8_t *interleave, uint64_t *limit, 62 uint64_t *hole_base, uint64_t *hole_size, uint8_t *wayp, 63 uint8_t *branch_interleavep) 64 { 65 uint8_t i, j; 66 uint64_t base = 0; 67 uint64_t lt = 0; 68 uint64_t h = 0; 69 uint64_t hs = 0; 70 uint8_t il = 1; 71 uint8_t way = 0; 72 uint8_t branch_interleave = 0; 73 74 for (i = 0; i < NB_MEM_RANK_SELECT; i++) { 75 for (j = 0; j < NB_RANKS_IN_SELECT; j++) { 76 if (nb_ranks[branch][i].rank[j] == rank) { 77 base = nb_ranks[branch][i].base; 78 lt = nb_ranks[branch][i].limit; 79 il = nb_ranks[branch][i].interleave; 80 h = nb_ranks[branch][i].hole_base; 81 hs = nb_ranks[branch][i].hole_size; 82 way = j; 83 branch_interleave = 84 nb_ranks[branch][i].branch_interleave; 85 i = NB_MEM_RANK_SELECT; 86 break; 87 } 88 } 89 } 90 if (lt == 0) { 91 for (i = 0; lt == 0 && i < NB_MEM_BRANCH_SELECT; i++) { 92 if (nb_banks[i].way[branch] && 93 base >= nb_banks[i].base && 94 base < nb_banks[i].base + nb_banks[i].limit) { 95 lt = nb_banks[i].limit; 96 break; 97 } 98 } 99 } 100 *interleave = il; 101 *limit = lt; 102 *hole_base = h; 103 *hole_size = hs; 104 *wayp = way; 105 *branch_interleavep = branch_interleave; 106 return (base); 107 } 108 109 void 110 inb_rank(nvlist_t *newdimm, nb_dimm_t *nb_dimm, uint8_t channel, uint32_t dimm) 111 { 112 nvlist_t **newrank; 113 int i; 114 115 newrank = kmem_zalloc(sizeof (nvlist_t *) * nb_dimm->nranks, KM_SLEEP); 116 for (i = 0; i < nb_dimm->nranks; i++) { 117 uint64_t dimm_base; 118 uint64_t limit; 119 uint8_t interleave; 120 uint8_t way; 121 uint8_t branch_interleave; 122 uint64_t hole_base; 123 uint64_t hole_size; 124 125 dimm_base = rank_to_base(channel/2, dimm*2 + i, &interleave, 126 &limit, &hole_base, &hole_size, &way, &branch_interleave); 127 (void) nvlist_alloc(&newrank[i], NV_UNIQUE_NAME, KM_SLEEP); 128 129 (void) nvlist_add_uint64(newrank[i], "dimm-rank-base", 130 dimm_base); 131 if (hole_size) { 132 (void) nvlist_add_uint64(newrank[i], "dimm-hole", 133 hole_base); 134 (void) nvlist_add_uint64(newrank[i], "dimm-hole-size", 135 hole_size); 136 } 137 (void) nvlist_add_uint64(newrank[i], "dimm-rank-limit", 138 limit); 139 if (interleave > 1) { 140 (void) nvlist_add_uint32(newrank[i], 141 "dimm-rank-interleave", (uint32_t)interleave); 142 (void) nvlist_add_uint32(newrank[i], 143 "dimm-rank-interleave-way", (uint32_t)way); 144 if (branch_interleave) { 145 (void) nvlist_add_uint32(newrank[i], 146 "dimm-rank-interleave-branch", (uint32_t)1); 147 } 148 } 149 } 150 (void) nvlist_add_nvlist_array(newdimm, MCINTEL_NVLIST_RANKS, newrank, 151 nb_dimm->nranks); 152 for (i = 0; i < nb_dimm->nranks; i++) 153 nvlist_free(newrank[i]); 154 kmem_free(newrank, sizeof (nvlist_t *) * nb_dimm->nranks); 155 } 156 157 nvlist_t * 158 inb_dimm(nb_dimm_t *nb_dimm, uint8_t channel, uint32_t dimm) 159 { 160 nvlist_t *newdimm; 161 uint8_t t; 162 char sbuf[65]; 163 164 (void) nvlist_alloc(&newdimm, NV_UNIQUE_NAME, KM_SLEEP); 165 (void) nvlist_add_uint32(newdimm, "dimm-number", dimm); 166 167 if (nb_dimm->dimm_size >= 1024*1024*1024) { 168 (void) snprintf(sbuf, sizeof (sbuf), "%dG", 169 (int)(nb_dimm->dimm_size / (1024*1024*1024))); 170 } else { 171 (void) snprintf(sbuf, sizeof (sbuf), "%dM", 172 (int)(nb_dimm->dimm_size / (1024*1024))); 173 } 174 (void) nvlist_add_string(newdimm, "dimm-size", sbuf); 175 (void) nvlist_add_uint64(newdimm, "size", nb_dimm->dimm_size); 176 (void) nvlist_add_uint32(newdimm, "nbanks", (uint32_t)nb_dimm->nbanks); 177 (void) nvlist_add_uint32(newdimm, "ncolumn", 178 (uint32_t)nb_dimm->ncolumn); 179 (void) nvlist_add_uint32(newdimm, "nrow", (uint32_t)nb_dimm->nrow); 180 (void) nvlist_add_uint32(newdimm, "width", (uint32_t)nb_dimm->width); 181 (void) nvlist_add_uint32(newdimm, "ranks", (uint32_t)nb_dimm->nranks); 182 inb_rank(newdimm, nb_dimm, channel, dimm); 183 (void) nvlist_add_uint32(newdimm, "manufacture-id", 184 (uint32_t)nb_dimm->manufacture_id); 185 (void) nvlist_add_uint32(newdimm, "manufacture-location", 186 (uint32_t)nb_dimm->manufacture_location); 187 (void) nvlist_add_uint32(newdimm, "manufacture-week", 188 (uint32_t)nb_dimm->manufacture_week); 189 (void) nvlist_add_uint32(newdimm, "manufacture-year", 190 (uint32_t)nb_dimm->manufacture_year + 2000); 191 /* create Sun Serial number from SPD data */ 192 (void) snprintf(sbuf, sizeof (sbuf), "%04x%02x%02x%02x%08x", 193 (uint32_t)nb_dimm->manufacture_id & 0x7fff, 194 (uint32_t)nb_dimm->manufacture_location, 195 (uint32_t)nb_dimm->manufacture_year, 196 (uint32_t)nb_dimm->manufacture_week, 197 nb_dimm->serial_number); 198 (void) nvlist_add_string(newdimm, FM_FMRI_HC_SERIAL_ID, sbuf); 199 if (nb_dimm->part_number && nb_dimm->part_number[0]) { 200 t = sizeof (nb_dimm->part_number); 201 (void) strncpy(sbuf, nb_dimm->part_number, t); 202 sbuf[t] = 0; 203 (void) nvlist_add_string(newdimm, FM_FMRI_HC_PART, sbuf); 204 } 205 if (nb_dimm->revision && nb_dimm->revision[0]) { 206 t = sizeof (nb_dimm->revision); 207 (void) strncpy(sbuf, nb_dimm->revision, t); 208 sbuf[t] = 0; 209 (void) nvlist_add_string(newdimm, FM_FMRI_HC_REVISION, sbuf); 210 } 211 t = sizeof (nb_dimm->label); 212 (void) strncpy(sbuf, nb_dimm->label, t); 213 sbuf[t] = 0; 214 (void) nvlist_add_string(newdimm, FM_FAULT_FRU_LABEL, sbuf); 215 return (newdimm); 216 } 217 218 static void 219 inb_dimmlist(nvlist_t *nvl) 220 { 221 nvlist_t **dimmlist; 222 nvlist_t **newchannel; 223 int nchannels = nb_number_memory_controllers * 2; 224 int nd; 225 uint8_t i, j; 226 nb_dimm_t **dimmpp; 227 nb_dimm_t *dimmp; 228 229 dimmlist = kmem_zalloc(sizeof (nvlist_t *) * nb_dimms_per_channel, 230 KM_SLEEP); 231 newchannel = kmem_zalloc(sizeof (nvlist_t *) * nchannels, KM_SLEEP); 232 dimmpp = nb_dimms; 233 for (i = 0; i < nchannels; i++) { 234 (void) nvlist_alloc(&newchannel[i], NV_UNIQUE_NAME, KM_SLEEP); 235 nd = 0; 236 for (j = 0; j < nb_dimms_per_channel; j++) { 237 dimmp = *dimmpp; 238 if (dimmp != NULL) { 239 dimmlist[nd] = inb_dimm(dimmp, i, (uint32_t)j); 240 nd++; 241 } 242 dimmpp++; 243 } 244 if (nd) { 245 (void) nvlist_add_nvlist_array(newchannel[i], 246 "memory-dimms", dimmlist, nd); 247 for (j = 0; j < nd; j++) 248 nvlist_free(dimmlist[j]); 249 } 250 } 251 (void) nvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_MC, newchannel, 252 nchannels); 253 for (i = 0; i < nchannels; i++) 254 nvlist_free(newchannel[i]); 255 kmem_free(dimmlist, sizeof (nvlist_t *) * nb_dimms_per_channel); 256 kmem_free(newchannel, sizeof (nvlist_t *) * nchannels); 257 } 258 259 static char * 260 inb_mc_name() 261 { 262 char *mc; 263 264 switch (nb_chipset) { 265 case INTEL_NB_7300: 266 mc = "Intel 7300"; 267 break; 268 case INTEL_NB_5400: 269 mc = "Intel 5400"; 270 break; 271 case INTEL_NB_5400A: 272 mc = "Intel 5400A"; 273 break; 274 case INTEL_NB_5400B: 275 mc = "Intel 5400B"; 276 break; 277 case INTEL_NB_5000P: 278 mc = "Intel 5000P"; 279 break; 280 case INTEL_NB_5000V: 281 mc = "Intel 5000V"; 282 break; 283 case INTEL_NB_5000X: 284 mc = "Intel 5000X"; 285 break; 286 case INTEL_NB_5000Z: 287 mc = "Intel 5000Z"; 288 break; 289 default: 290 mc = "Intel 5000"; 291 break; 292 } 293 return (mc); 294 } 295 296 static void 297 inb_create_nvl() 298 { 299 nvlist_t *nvl; 300 301 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 302 (void) nvlist_add_uint8(nvl, MCINTEL_NVLIST_VERSTR, 303 MCINTEL_NVLIST_VERS); 304 (void) nvlist_add_string(nvl, "memory-controller", inb_mc_name()); 305 inb_dimmlist(nvl); 306 307 if (inb_mc_nvl) 308 nvlist_free(inb_mc_nvl); 309 inb_mc_nvl = nvl; 310 } 311 312 static void 313 inb_mc_snapshot_destroy() 314 { 315 ASSERT(RW_LOCK_HELD(&inb_mc_lock)); 316 317 if (inb_mc_snapshot == NULL) 318 return; 319 320 kmem_free(inb_mc_snapshot, inb_mc_snapshotsz); 321 inb_mc_snapshot = NULL; 322 inb_mc_snapshotsz = 0; 323 inb_mc_snapshotgen++; 324 } 325 326 static int 327 inb_mc_snapshot_update() 328 { 329 ASSERT(RW_LOCK_HELD(&inb_mc_lock)); 330 331 if (inb_mc_snapshot != NULL) 332 return (0); 333 334 if (nvlist_pack(inb_mc_nvl, &inb_mc_snapshot, &inb_mc_snapshotsz, 335 NV_ENCODE_XDR, KM_SLEEP) != 0) 336 return (-1); 337 338 return (0); 339 } 340 341 /*ARGSUSED*/ 342 static int 343 inb_mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 344 int *rvalp) 345 { 346 int rc = 0; 347 mc_snapshot_info_t mcs; 348 349 if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT) 350 return (EINVAL); 351 352 rw_enter(&inb_mc_lock, RW_READER); 353 if (inb_mc_nvl == NULL || inb_mc_snapshotgen != nb_config_gen) { 354 if (!rw_tryupgrade(&inb_mc_lock)) { 355 rw_exit(&inb_mc_lock); 356 return (EAGAIN); 357 } 358 if (inb_mc_nvl) 359 inb_mc_snapshot_destroy(); 360 inb_create_nvl(); 361 nb_config_gen = inb_mc_snapshotgen; 362 (void) inb_mc_snapshot_update(); 363 } 364 switch (cmd) { 365 case MC_IOC_SNAPSHOT_INFO: 366 mcs.mcs_size = (uint32_t)inb_mc_snapshotsz; 367 mcs.mcs_gen = inb_mc_snapshotgen; 368 369 if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t), 370 mode) < 0) 371 rc = EFAULT; 372 break; 373 case MC_IOC_SNAPSHOT: 374 if (ddi_copyout(inb_mc_snapshot, (void *)arg, inb_mc_snapshotsz, 375 mode) < 0) 376 rc = EFAULT; 377 break; 378 } 379 rw_exit(&inb_mc_lock); 380 return (rc); 381 } 382 383 /*ARGSUSED*/ 384 static int 385 inb_mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 386 void **result) 387 { 388 if ((infocmd != DDI_INFO_DEVT2DEVINFO && 389 infocmd != DDI_INFO_DEVT2INSTANCE) || inb_dip == NULL) { 390 *result = NULL; 391 return (DDI_FAILURE); 392 } 393 if (infocmd == DDI_INFO_DEVT2DEVINFO) 394 *result = inb_dip; 395 else 396 *result = (void *)(uintptr_t)ddi_get_instance(inb_dip); 397 return (0); 398 } 399 400 static int 401 inb_mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 402 { 403 if (cmd == DDI_RESUME) { 404 nb_dev_reinit(); 405 return (DDI_SUCCESS); 406 } 407 if (cmd != DDI_ATTACH) 408 return (DDI_FAILURE); 409 if (inb_dip == NULL) { 410 inb_dip = dip; 411 (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model", 412 inb_mc_name()); 413 nb_no_smbios = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 414 DDI_PROP_DONTPASS, "no-smbios", 0); 415 nb_pci_cfg_setup(dip); 416 if (nb_dev_init()) { 417 nb_pci_cfg_free(); 418 inb_dip = NULL; 419 return (DDI_FAILURE); 420 } 421 if (ddi_create_minor_node(dip, "mc-intel", S_IFCHR, 0, 422 "ddi_mem_ctrl", 0) != DDI_SUCCESS) { 423 cmn_err(CE_WARN, "failed to create minor node" 424 " for memory controller\n"); 425 } 426 cmi_hdl_walk(inb_mc_register, NULL, NULL, NULL); 427 } 428 429 return (DDI_SUCCESS); 430 } 431 432 /*ARGSUSED*/ 433 static int 434 inb_mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 435 { 436 if (nb_allow_detach && cmd == DDI_DETACH && dip == inb_dip) { 437 rw_enter(&inb_mc_lock, RW_WRITER); 438 inb_mc_snapshot_destroy(); 439 rw_exit(&inb_mc_lock); 440 inb_dip = NULL; 441 return (DDI_SUCCESS); 442 } else if (cmd == DDI_SUSPEND || cmd == DDI_PM_SUSPEND) { 443 return (DDI_SUCCESS); 444 } else { 445 return (DDI_FAILURE); 446 } 447 } 448 449 /*ARGSUSED*/ 450 static int 451 inb_mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 452 { 453 if (otyp != OTYP_CHR) 454 return (EINVAL); 455 456 rw_enter(&inb_mc_lock, RW_READER); 457 if (getminor(*devp) >= 1) { 458 rw_exit(&inb_mc_lock); 459 return (EINVAL); 460 } 461 rw_exit(&inb_mc_lock); 462 463 return (0); 464 } 465 466 /*ARGSUSED*/ 467 static int 468 inb_mc_close(dev_t dev, int flag, int otyp, cred_t *credp) 469 { 470 return (0); 471 } 472 473 474 static struct cb_ops inb_mc_cb_ops = { 475 inb_mc_open, 476 inb_mc_close, 477 nodev, /* not a block driver */ 478 nodev, /* no print routine */ 479 nodev, /* no dump routine */ 480 nodev, /* no read routine */ 481 nodev, /* no write routine */ 482 inb_mc_ioctl, 483 nodev, /* no devmap routine */ 484 nodev, /* no mmap routine */ 485 nodev, /* no segmap routine */ 486 nochpoll, /* no chpoll routine */ 487 ddi_prop_op, 488 0, /* not a STREAMS driver */ 489 D_NEW | D_MP, /* safe for multi-thread/multi-processor */ 490 }; 491 492 static struct dev_ops inb_mc_ops = { 493 DEVO_REV, /* devo_rev */ 494 0, /* devo_refcnt */ 495 inb_mc_getinfo, /* devo_getinfo */ 496 nulldev, /* devo_identify */ 497 nulldev, /* devo_probe */ 498 inb_mc_attach, /* devo_attach */ 499 inb_mc_detach, /* devo_detach */ 500 nodev, /* devo_reset */ 501 &inb_mc_cb_ops, /* devo_cb_ops */ 502 NULL, /* devo_bus_ops */ 503 NULL, /* devo_power */ 504 ddi_quiesce_not_needed, /* devo_quiesce */ 505 }; 506 507 static struct modldrv modldrv = { 508 &mod_driverops, 509 "Intel 5000 Memory Controller Hub Module", 510 &inb_mc_ops 511 }; 512 513 static struct modlinkage modlinkage = { 514 MODREV_1, 515 (void *)&modldrv, 516 NULL 517 }; 518 519 int 520 _init(void) 521 { 522 int err; 523 524 err = nb_init(); 525 if (err == 0 && (err = mod_install(&modlinkage)) == 0) 526 rw_init(&inb_mc_lock, NULL, RW_DRIVER, NULL); 527 528 return (err); 529 } 530 531 int 532 _info(struct modinfo *modinfop) 533 { 534 return (mod_info(&modlinkage, modinfop)); 535 } 536 537 int 538 _fini(void) 539 { 540 int err; 541 542 if ((err = mod_remove(&modlinkage)) == 0) { 543 nb_unload(); 544 rw_destroy(&inb_mc_lock); 545 } 546 547 return (err); 548 } 549