diff options
Diffstat (limited to 'usr/src/uts')
84 files changed, 7750 insertions, 395 deletions
diff --git a/usr/src/uts/Makefile.targ b/usr/src/uts/Makefile.targ index aebc896db7..40526b3af2 100644 --- a/usr/src/uts/Makefile.targ +++ b/usr/src/uts/Makefile.targ @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -146,6 +146,9 @@ $(USR_MOD_DIRS_32): $(USR_MOD_DIR) $(ROOT_MOD_DIR)/%: $(OBJS_DIR)/% $(ROOT_MOD_DIR) FRC $(INS.file) +$(ROOT_CPU_DIR)/%: $(OBJS_DIR)/% $(ROOT_CPU_DIR) FRC + $(INS.file) + $(ROOT_DRV_DIR)/%: $(OBJS_DIR)/% $(ROOT_DRV_DIR) FRC $(INS.file) diff --git a/usr/src/uts/common/io/mem.c b/usr/src/uts/common/io/mem.c index 0569b0ebae..11667e8c59 100644 --- a/usr/src/uts/common/io/mem.c +++ b/usr/src/uts/common/io/mem.c @@ -68,13 +68,15 @@ #include <sys/debug.h> #include <sys/fm/protocol.h> -#ifdef __sparc +#if defined(__sparc) extern int cpu_get_mem_name(uint64_t, uint64_t *, uint64_t, char *, int, int *); extern int cpu_get_mem_info(uint64_t, uint64_t, uint64_t *, uint64_t *, uint64_t *, int *, int *, int *); extern size_t cpu_get_name_bufsize(void); extern int cpu_get_mem_sid(char *, char *, int, int *); extern int cpu_get_mem_addr(char *, char *, uint64_t, uint64_t *); +#elif defined(__i386) || defined(__amd64) +#include <sys/cpu_module.h> #endif /* __sparc */ /* @@ -415,6 +417,9 @@ mmwrite(dev_t dev, struct uio *uio, cred_t *cred) static int mmioctl_vtop(intptr_t data) { +#ifdef _SYSCALL32 + mem_vtop32_t vtop32; +#endif mem_vtop_t mem_vtop; proc_t *p; pfn_t pfn = (pfn_t)PFN_INVALID; @@ -422,13 +427,36 @@ mmioctl_vtop(intptr_t data) struct as *as; struct seg *seg; - if (copyin((void *)data, &mem_vtop, sizeof (mem_vtop_t))) - return (EFAULT); + if (get_udatamodel() == DATAMODEL_NATIVE) { + if (copyin((void *)data, &mem_vtop, sizeof (mem_vtop_t))) + return (EFAULT); + } +#ifdef _SYSCALL32 + else { + if (copyin((void *)data, &vtop32, sizeof (mem_vtop32_t))) + return (EFAULT); + mem_vtop.m_as = (struct as *)vtop32.m_as; + mem_vtop.m_va = (void *)vtop32.m_va; + + if (mem_vtop.m_as != NULL) + return (EINVAL); + } +#endif + if (mem_vtop.m_as == &kas) { pfn = hat_getpfnum(kas.a_hat, mem_vtop.m_va); - } else if (mem_vtop.m_as == NULL) { - return (EIO); } else { + if (mem_vtop.m_as == NULL) { + /* + * Assume the calling process's address space if the + * caller didn't specify one. + */ + p = curthread->t_procp; + if (p == NULL) + return (EIO); + mem_vtop.m_as = p->p_as; + } + mutex_enter(&pidlock); for (p = practive; p != NULL; p = p->p_next) { if (p->p_as == mem_vtop.m_as) { @@ -461,8 +489,18 @@ mmioctl_vtop(intptr_t data) mem_vtop.m_pfn = pfn; if (pfn == PFN_INVALID) return (EIO); - if (copyout(&mem_vtop, (void *)data, sizeof (mem_vtop_t))) - return (EFAULT); + + if (get_udatamodel() == DATAMODEL_NATIVE) { + if (copyout(&mem_vtop, (void *)data, sizeof (mem_vtop_t))) + return (EFAULT); + } +#ifdef _SYSCALL32 + else { + vtop32.m_pfn = mem_vtop.m_pfn; + if (copyout(&vtop32, (void *)data, sizeof (mem_vtop32_t))) + return (EFAULT); + } +#endif return (0); } @@ -533,7 +571,7 @@ mmioctl_page_fmri_retire(int cmd, intptr_t data) if ((err = mm_get_mem_fmri(&mpage, &nvl)) < 0) return (err); - if ((err = mm_get_paddr(nvl, &pa)) < 0) { + if ((err = mm_get_paddr(nvl, &pa)) != 0) { nvlist_free(nvl); return (err); } @@ -1120,7 +1158,7 @@ mm_get_paddr(nvlist_t *nvl, uint64_t *paddr) * If the "offset" member is not present, then the address is * retrieved from the "physaddr" member. */ -#ifdef __sparc +#if defined(__sparc) if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &offset) != 0) { if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &pa) != 0) { @@ -1134,9 +1172,11 @@ mm_get_paddr(nvlist_t *nvl, uint64_t *paddr) if ((err = cpu_get_mem_addr(unum, serids[0], offset, &pa)) != 0) return (err); } -#else /* __i386, __amd64 */ - if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &pa) != 0) +#elif defined(__i386) || defined(__amd64) + if (cmi_mc_unumtopa(NULL, nvl, &pa) == 0) return (EINVAL); +#else +#error "port me" #endif /* __sparc */ *paddr = pa; diff --git a/usr/src/uts/common/krtld/kobj.c b/usr/src/uts/common/krtld/kobj.c index 6df0916ea0..003022d104 100644 --- a/usr/src/uts/common/krtld/kobj.c +++ b/usr/src/uts/common/krtld/kobj.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -2970,7 +2970,7 @@ kobj_getelfsym(char *name, void *mp, int *size) } uintptr_t -kobj_lookup(void *mod, char *name) +kobj_lookup(struct module *mod, const char *name) { Sym *sp; diff --git a/usr/src/uts/common/krtld/kobj_stubs.c b/usr/src/uts/common/krtld/kobj_stubs.c index 7bc82c5139..3d972194bb 100644 --- a/usr/src/uts/common/krtld/kobj_stubs.c +++ b/usr/src/uts/common/krtld/kobj_stubs.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -159,7 +159,7 @@ kobj_searchsym(struct module *mp, uintptr_t value, ulong_t *offset) /*ARGSUSED*/ uintptr_t -kobj_lookup(void *mod, char *name) +kobj_lookup(struct module *mod, const char *name) { return (0); } diff --git a/usr/src/uts/common/os/chip.c b/usr/src/uts/common/os/chip.c index b6fcbe89d2..8b0bfd765d 100644 --- a/usr/src/uts/common/os/chip.c +++ b/usr/src/uts/common/os/chip.c @@ -124,6 +124,18 @@ chip_find(chipid_t chipid) return (NULL); } +chip_t * +chip_lookup(chipid_t chipid) +{ + chip_t *chp; + + mutex_enter(&cpu_lock); + chp = chip_find(chipid); + mutex_exit(&cpu_lock); + + return (chp); +} + #ifndef sun4v /* * Setup the kstats for this chip, if needed diff --git a/usr/src/uts/common/os/cpu.c b/usr/src/uts/common/os/cpu.c index e9f4453f81..674f8bd6e5 100644 --- a/usr/src/uts/common/os/cpu.c +++ b/usr/src/uts/common/os/cpu.c @@ -59,6 +59,9 @@ #include <sys/msacct.h> #include <sys/time.h> #include <sys/archsystm.h> +#if defined(__i386) || defined(__amd64) +#include <sys/x86_archext.h> +#endif extern int mp_cpu_start(cpu_t *); extern int mp_cpu_stop(cpu_t *); @@ -2067,12 +2070,19 @@ static struct { kstat_named_t ci_clock_MHz; kstat_named_t ci_chip_id; kstat_named_t ci_implementation; -#ifdef __sparcv9 + kstat_named_t ci_brandstr; + kstat_named_t ci_core_id; +#if defined(__sparcv9) kstat_named_t ci_device_ID; kstat_named_t ci_cpu_fru; #endif - kstat_named_t ci_brandstr; - kstat_named_t ci_core_id; +#if defined(__i386) || defined(__amd64) + kstat_named_t ci_vendorstr; + kstat_named_t ci_family; + kstat_named_t ci_model; + kstat_named_t ci_step; + kstat_named_t ci_clogid; +#endif } cpu_info_template = { { "state", KSTAT_DATA_CHAR }, { "state_begin", KSTAT_DATA_LONG }, @@ -2081,12 +2091,19 @@ static struct { { "clock_MHz", KSTAT_DATA_LONG }, { "chip_id", KSTAT_DATA_LONG }, { "implementation", KSTAT_DATA_STRING }, -#ifdef __sparcv9 + { "brand", KSTAT_DATA_STRING }, + { "core_id", KSTAT_DATA_LONG }, +#if defined(__sparcv9) { "device_ID", KSTAT_DATA_UINT64 }, { "cpu_fru", KSTAT_DATA_STRING }, #endif - { "brand", KSTAT_DATA_STRING }, - { "core_id", KSTAT_DATA_LONG }, +#if defined(__i386) || defined(__amd64) + { "vendor_id", KSTAT_DATA_STRING }, + { "family", KSTAT_DATA_INT32 }, + { "model", KSTAT_DATA_INT32 }, + { "stepping", KSTAT_DATA_INT32 }, + { "clog_id", KSTAT_DATA_INT32 }, +#endif }; static kmutex_t cpu_info_template_lock; @@ -2132,13 +2149,23 @@ cpu_info_kstat_update(kstat_t *ksp, int rw) cpu_info_template.ci_chip_id.value.l = chip_plat_get_chipid(cp); kstat_named_setstr(&cpu_info_template.ci_implementation, cp->cpu_idstr); -#ifdef __sparcv9 + kstat_named_setstr(&cpu_info_template.ci_brandstr, cp->cpu_brandstr); + +#if defined(__sparcv9) cpu_info_template.ci_device_ID.value.ui64 = cpunodes[cp->cpu_id].device_id; kstat_named_setstr(&cpu_info_template.ci_cpu_fru, cpu_fru_fmri(cp)); #endif - kstat_named_setstr(&cpu_info_template.ci_brandstr, cp->cpu_brandstr); +#if defined(__i386) || defined(__amd64) cpu_info_template.ci_core_id.value.l = chip_plat_get_coreid(cp); + kstat_named_setstr(&cpu_info_template.ci_vendorstr, + cpuid_getvendorstr(cp)); + cpu_info_template.ci_family.value.l = cpuid_getfamily(cp); + cpu_info_template.ci_model.value.l = cpuid_getmodel(cp); + cpu_info_template.ci_step.value.l = cpuid_getstep(cp); + cpu_info_template.ci_clogid.value.l = chip_plat_get_clogid(cp); +#endif + return (0); } @@ -2155,13 +2182,16 @@ cpu_info_kstat_create(cpu_t *cp) zoneid = ALL_ZONES; if ((cp->cpu_info_kstat = kstat_create_zone("cpu_info", cp->cpu_id, NULL, "misc", KSTAT_TYPE_NAMED, - sizeof (cpu_info_template) / sizeof (kstat_named_t), - KSTAT_FLAG_VIRTUAL, zoneid)) != NULL) { + sizeof (cpu_info_template) / sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL, zoneid)) != NULL) { cp->cpu_info_kstat->ks_data_size += 2 * CPU_IDSTRLEN; -#ifdef __sparcv9 +#if defined(__sparcv9) cp->cpu_info_kstat->ks_data_size += strlen(cpu_fru_fmri(cp)) + 1; #endif +#if defined(__i386) || defined(__amd64) + cp->cpu_info_kstat->ks_data_size += X86_VENDOR_STRLEN; +#endif cp->cpu_info_kstat->ks_lock = &cpu_info_template_lock; cp->cpu_info_kstat->ks_data = &cpu_info_template; cp->cpu_info_kstat->ks_private = cp; diff --git a/usr/src/uts/common/os/ddifm.c b/usr/src/uts/common/os/ddifm.c index 911a7d9a2f..6edd829ba8 100644 --- a/usr/src/uts/common/os/ddifm.c +++ b/usr/src/uts/common/os/ddifm.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -477,7 +477,10 @@ ddi_fm_handler_register(dev_info_t *dip, ddi_err_func_t handler, return; } - pdip = (dev_info_t *)DEVI(dip)->devi_parent; + if (dip == ddi_root_node()) + pdip = dip; + else + pdip = (dev_info_t *)DEVI(dip)->devi_parent; ASSERT(pdip); @@ -529,7 +532,10 @@ ddi_fm_handler_unregister(dev_info_t *dip) return; } - pdip = (dev_info_t *)DEVI(dip)->devi_parent; + if (dip == ddi_root_node()) + pdip = dip; + else + pdip = (dev_info_t *)DEVI(dip)->devi_parent; ASSERT(pdip); @@ -581,10 +587,11 @@ ddi_fm_handler_unregister(dev_info_t *dip) * This function must be called from a driver's attach(9E) entry point. */ void -ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibc) +ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibcp) { struct dev_info *devi = DEVI(dip); struct i_ddi_fmhdl *fmhdl; + ddi_iblock_cookie_t ibc; int pcap, newcap = DDI_FM_NOT_CAPABLE; if (!DEVI_IS_ATTACHING(dip)) { @@ -606,11 +613,12 @@ ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibc) * Initialize the default ibc. The parent may change it * depending upon its capabilities. */ - *ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL); + ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL); - pcap = i_ndi_busop_fm_init(dip, *fmcap, ibc); + pcap = i_ndi_busop_fm_init(dip, *fmcap, &ibc); } else { pcap = *fmcap; + ibc = *ibcp; } /* Initialize the per-device instance FM handle */ @@ -636,7 +644,7 @@ ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibc) fmhdl->fh_acc_cache = NULL; fmhdl->fh_tgts = NULL; fmhdl->fh_dip = dip; - fmhdl->fh_ibc = *ibc; + fmhdl->fh_ibc = ibc; mutex_init(&fmhdl->fh_lock, NULL, MUTEX_DRIVER, fmhdl->fh_ibc); devi->devi_fmhdl = fmhdl; @@ -672,7 +680,7 @@ ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibc) */ if (DDI_FM_DMA_ERR_CAP(*fmcap) && DDI_FM_DMA_ERR_CAP(pcap)) { - i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, *ibc); + i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, ibc); /* Set-up dma chk capability prop */ if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, @@ -684,7 +692,7 @@ ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibc) } if (DDI_FM_ACC_ERR_CAP(*fmcap) && DDI_FM_ACC_ERR_CAP(pcap)) { - i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, *ibc); + i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, ibc); /* Set-up dma chk capability prop */ if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "fm-accchk-capable", 0) == 0) @@ -700,6 +708,9 @@ ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibc) */ fmhdl->fh_cap = newcap; *fmcap = newcap; + + if (ibcp != NULL) + *ibcp = ibc; } /* diff --git a/usr/src/uts/common/os/fm.c b/usr/src/uts/common/os/fm.c index 33e8357a88..897697c2dc 100644 --- a/usr/src/uts/common/os/fm.c +++ b/usr/src/uts/common/os/fm.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -60,7 +60,6 @@ #include <sys/sysevent.h> #include <sys/sysevent_impl.h> #include <sys/nvpair.h> -#include <sys/nvpair_impl.h> #include <sys/cmn_err.h> #include <sys/cpuvar.h> #include <sys/sysmacros.h> @@ -73,6 +72,8 @@ #include <sys/cpuvar.h> #include <sys/console.h> #include <sys/panic.h> +#include <sys/kobj.h> +#include <sys/sunddi.h> #include <sys/systeminfo.h> #include <sys/sysevent/eventdefs.h> #include <sys/fm/util.h> @@ -614,15 +615,13 @@ fm_nvlist_create(nv_alloc_t *nva) void fm_nvlist_destroy(nvlist_t *nvl, int flag) { - nv_alloc_t *nvhdl; - - nvhdl = ((nvpriv_t *)(uintptr_t)nvl->nvl_priv)->nvp_nva; + nv_alloc_t *nva = nvlist_lookup_nv_alloc(nvl); nvlist_free(nvl); - if (nvhdl != NULL) { + if (nva != NULL) { if (flag == FM_NVA_FREE) - fm_nva_xdestroy(nvhdl); + fm_nva_xdestroy(nva); } } @@ -820,53 +819,88 @@ fm_ereport_set(nvlist_t *ereport, int version, const char *erpt_class, atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1); } +/* + * Set-up and validate the members of an hc fmri according to; + * + * Member name Type Value + * =================================================== + * version uint8_t 0 + * auth nvlist_t <auth> + * hc-name string <name> + * hc-id string <id> + * + * Note that auth and hc-id are optional members. + */ + +#define HC_MAXPAIRS 20 +#define HC_MAXNAMELEN 50 + static int -i_fm_fmri_hc_set_v0(nvlist_t *hc, uint32_t size, va_list ap) +fm_fmri_hc_set_common(nvlist_t *fmri, int version, const nvlist_t *auth) { - int i, ret; - const char *name, *id; - nvlist_t **hc_nvl; + if (version != FM_HC_SCHEME_VERSION) { + atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); + return (0); + } - if (size <= 0) + if (nvlist_add_uint8(fmri, FM_VERSION, version) != 0 || + nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC) != 0) { + atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); return (0); + } - hc_nvl = kmem_zalloc(size * sizeof (nvlist_t *), KM_SLEEP); + if (auth != NULL && nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY, + (nvlist_t *)auth) != 0) { + atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); + return (0); + } - for (i = 0; i < size; ++i) { - name = va_arg(ap, const char *); - if (name == NULL) { - ret = EINVAL; - goto fail; - } - id = va_arg(ap, const char *); - if ((hc_nvl[i] = fm_nvlist_create( - ((nvpriv_t *)(uintptr_t)hc->nvl_priv)->nvp_nva)) == NULL) { - ret = ENOMEM; - goto fail; + return (1); +} + +void +fm_fmri_hc_set(nvlist_t *fmri, int version, const nvlist_t *auth, + nvlist_t *snvl, int npairs, ...) +{ + nv_alloc_t *nva = nvlist_lookup_nv_alloc(fmri); + nvlist_t *pairs[HC_MAXPAIRS]; + va_list ap; + int i; + + if (!fm_fmri_hc_set_common(fmri, version, auth)) + return; + + npairs = MIN(npairs, HC_MAXPAIRS); + + va_start(ap, npairs); + for (i = 0; i < npairs; i++) { + const char *name = va_arg(ap, const char *); + uint32_t id = va_arg(ap, uint32_t); + char idstr[11]; + + (void) snprintf(idstr, sizeof (idstr), "%u", id); + + pairs[i] = fm_nvlist_create(nva); + if (nvlist_add_string(pairs[i], FM_FMRI_HC_NAME, name) != 0 || + nvlist_add_string(pairs[i], FM_FMRI_HC_ID, idstr) != 0) { + atomic_add_64( + &erpt_kstat_data.fmri_set_failed.value.ui64, 1); } - if ((ret = nvlist_add_string(hc_nvl[i], FM_FMRI_HC_NAME, - name)) != 0) - goto fail; - if ((ret = nvlist_add_string(hc_nvl[i], FM_FMRI_HC_ID, - id)) != 0) - goto fail; } + va_end(ap); - if ((ret = nvlist_add_nvlist_array(hc, FM_FMRI_HC_LIST, hc_nvl, - size)) != 0) - goto fail; + if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, pairs, npairs) != 0) + atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); - kmem_free(hc_nvl, size * sizeof (nvlist_t *)); - return (0); + for (i = 0; i < npairs; i++) + fm_nvlist_destroy(pairs[i], FM_NVA_RETAIN); -fail: - for (i = 0; i < size; ++i) { - if (hc_nvl[i] != NULL) - fm_nvlist_destroy(hc_nvl[i], FM_NVA_RETAIN); + if (snvl != NULL) { + if (nvlist_add_nvlist(fmri, FM_FMRI_HC_SPECIFIC, snvl) != 0) { + atomic_add_64( + &erpt_kstat_data.fmri_set_failed.value.ui64, 1); + } } - - kmem_free(hc_nvl, size * sizeof (nvlist_t *)); - return (ret); } /* @@ -930,47 +964,45 @@ fm_fmri_dev_set(nvlist_t *fmri_dev, int version, const nvlist_t *auth, * cpumask uint8_t <cpu_mask> * serial uint64_t <serial_id> * - * Note that auth is an optional member. + * Note that auth, cpumask, serial are optional members. * */ void fm_fmri_cpu_set(nvlist_t *fmri_cpu, int version, const nvlist_t *auth, - uint32_t cpu_id, uint8_t cpu_mask, uint64_t serial_id) + uint32_t cpu_id, uint8_t *cpu_maskp, const char *serial_idp) { - if (version != CPU_SCHEME_VERSION0) { - atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); + uint64_t *failedp = &erpt_kstat_data.fmri_set_failed.value.ui64; + + if (version < CPU_SCHEME_VERSION1) { + atomic_add_64(failedp, 1); return; } if (nvlist_add_uint8(fmri_cpu, FM_VERSION, version) != 0) { - atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); + atomic_add_64(failedp, 1); return; } if (nvlist_add_string(fmri_cpu, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU) != 0) { - atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); + atomic_add_64(failedp, 1); return; } - if (auth != NULL) - if (nvlist_add_nvlist(fmri_cpu, FM_FMRI_AUTHORITY, - (nvlist_t *)auth) != 0) { - atomic_add_64( - &erpt_kstat_data.fmri_set_failed.value.ui64, 1); - } + if (auth != NULL && nvlist_add_nvlist(fmri_cpu, FM_FMRI_AUTHORITY, + (nvlist_t *)auth) != 0) + atomic_add_64(failedp, 1); - if (nvlist_add_uint32(fmri_cpu, FM_FMRI_CPU_ID, cpu_id) != 0) { - atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); - } + if (nvlist_add_uint32(fmri_cpu, FM_FMRI_CPU_ID, cpu_id) != 0) + atomic_add_64(failedp, 1); - if (nvlist_add_uint8(fmri_cpu, FM_FMRI_CPU_MASK, cpu_mask) != 0) { - atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); - } + if (cpu_maskp != NULL && nvlist_add_uint8(fmri_cpu, FM_FMRI_CPU_MASK, + *cpu_maskp) != 0) + atomic_add_64(failedp, 1); - if (nvlist_add_uint64(fmri_cpu, FM_FMRI_CPU_SERIAL_ID, serial_id) - != 0) - atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); + if (serial_idp == NULL || nvlist_add_string(fmri_cpu, + FM_FMRI_CPU_SERIAL_ID, (char *)serial_idp) != 0) + atomic_add_64(failedp, 1); } /* @@ -1159,3 +1191,29 @@ fm_ena_time_get(uint64_t ena) return (time); } + +/* + * Convert a getpcstack() trace to symbolic name+offset, and add the resulting + * string array to a Fault Management ereport as FM_EREPORT_PAYLOAD_NAME_STACK. + */ +void +fm_payload_stack_add(nvlist_t *payload, const pc_t *stack, int depth) +{ + int i; + char *sym; + ulong_t off; + char *stkpp[FM_STK_DEPTH]; + char buf[FM_STK_DEPTH * FM_SYM_SZ]; + char *stkp = buf; + + for (i = 0; i < depth && i != FM_STK_DEPTH; i++, stkp += FM_SYM_SZ) { + if ((sym = kobj_getsymname(stack[i], &off)) != NULL) + (void) snprintf(stkp, FM_SYM_SZ, "%s+%lx", sym, off); + else + (void) snprintf(stkp, FM_SYM_SZ, "%lx", (long)stack[i]); + stkpp[i] = stkp; + } + + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_STACK, + DATA_TYPE_STRING_ARRAY, FM_STK_DEPTH, stkpp, NULL); +} diff --git a/usr/src/uts/common/os/kcpc.c b/usr/src/uts/common/os/kcpc.c index f3bfd93d24..569344e8ca 100644 --- a/usr/src/uts/common/os/kcpc.c +++ b/usr/src/uts/common/os/kcpc.c @@ -19,8 +19,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -1515,41 +1516,12 @@ kcpc_invalidate(kthread_t *t) int kcpc_pcbe_tryload(const char *prefix, uint_t first, uint_t second, uint_t third) { - char modname[PCBE_NAMELEN]; - char stub[PCBE_NAMELEN]; - - if (prefix != NULL) - (void) snprintf(stub, PCBE_NAMELEN, "pcbe.%s", prefix); - else - (void) snprintf(stub, PCBE_NAMELEN, "pcbe"); - - (void) snprintf(modname, PCBE_NAMELEN, "%s.%u.%u.%u", - stub, first, second, third); - - DTRACE_PROBE1(kcpc__pcbe__spec, char *, modname); - - if (modload("pcbe", modname) >= 0) - return (0); - - (void) snprintf(modname, PCBE_NAMELEN, "%s.%u.%u", - stub, first, second); - if (modload("pcbe", modname) >= 0) - return (0); - - (void) snprintf(modname, PCBE_NAMELEN, "%s.%u", stub, first); - if (modload("pcbe", modname) >= 0) - return (0); - - if (prefix == NULL) - /* - * If no prefix was given, we have tried all possible - * PCBE names. - */ - return (-1); + uint_t s[3]; - (void) snprintf(modname, PCBE_NAMELEN, "%s", stub); - if (modload("pcbe", modname) >= 0) - return (0); + s[0] = first; + s[1] = second; + s[2] = third; - return (-1); + return (modload_qualified("pcbe", + "pcbe", prefix, ".", s, 3) < 0 ? -1 : 0); } diff --git a/usr/src/uts/common/os/modconf.c b/usr/src/uts/common/os/modconf.c index 2c033495bc..f270cad2a0 100644 --- a/usr/src/uts/common/os/modconf.c +++ b/usr/src/uts/common/os/modconf.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -101,6 +101,11 @@ struct mod_ops mod_miscops = { mod_null, mod_null, mod_infonull }; +/* CPU Modules */ +struct mod_ops mod_cpuops = { + mod_null, mod_null, mod_infonull +}; + /* * Cryptographic Modules */ diff --git a/usr/src/uts/common/os/modctl.c b/usr/src/uts/common/os/modctl.c index 73e0447ab1..e776a36310 100644 --- a/usr/src/uts/common/os/modctl.c +++ b/usr/src/uts/common/os/modctl.c @@ -19,6 +19,7 @@ * * CDDL HEADER END */ + /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. @@ -84,15 +85,14 @@ static int modinfo(modid_t, struct modinfo *); static void mod_uninstall_all(void); static int mod_getinfo(struct modctl *, struct modinfo *); -static struct modctl *allocate_modp(char *, char *); +static struct modctl *allocate_modp(const char *, const char *); static int mod_load(struct modctl *, int); static void mod_unload(struct modctl *); static int modinstall(struct modctl *); static int moduninstall(struct modctl *); -static struct modctl *mod_hold_by_name_common(struct modctl *, char *); -static struct modctl *mod_hold_by_id(modid_t); +static struct modctl *mod_hold_by_name_common(struct modctl *, const char *); static struct modctl *mod_hold_next_by_id(modid_t); static struct modctl *mod_hold_loaded_mod(struct modctl *, char *, int *); static struct modctl *mod_hold_installed_mod(char *, int, int *); @@ -1943,6 +1943,71 @@ modload(char *subdir, char *filename) } /* + * Load a module using a series of qualified names from most specific to least + * specific, e.g. for subdir "foo", p1 "bar", p2 "baz", we might try: + * + * foo/bar.baz.1.2.3 + * foo/bar.baz.1.2 + * foo/bar.baz.1 + * + * Return the module ID on success; -1 if no module was loaded. + */ +int +modload_qualified(const char *subdir, const char *p1, + const char *p2, const char *delim, uint_t suffv[], int suffc) +{ + char path[MOD_MAXPATH]; + size_t n, resid = sizeof (path); + char *p = path; + + char **dotv; + int i, rc, id; + modctl_t *mp; + + if (p2 != NULL) + n = snprintf(p, resid, "%s/%s%s%s", subdir, p1, delim, p2); + else + n = snprintf(p, resid, "%s/%s", subdir, p1); + + if (n >= resid) + return (-1); + + p += n; + resid -= n; + dotv = kmem_alloc(sizeof (char *) * (suffc + 1), KM_SLEEP); + + for (i = 0; i < suffc; i++) { + dotv[i] = p; + n = snprintf(p, resid, "%s%u", delim, suffv[i]); + + if (n >= resid) { + kmem_free(dotv, sizeof (char *) * (suffc + 1)); + return (-1); + } + + p += n; + resid -= n; + } + + dotv[suffc] = p; + + for (i = suffc; i >= 0; i--) { + dotv[i][0] = '\0'; + mp = mod_hold_installed_mod(path, 1, &rc); + + if (mp != NULL) { + kmem_free(dotv, sizeof (char *) * (suffc + 1)); + id = mp->mod_id; + mod_release_mod(mp); + return (id); + } + } + + kmem_free(dotv, sizeof (char *) * (suffc + 1)); + return (-1); +} + +/* * Load a module. */ int @@ -2440,7 +2505,7 @@ modadd(struct modctl *mp) /*ARGSUSED*/ static struct modctl * -allocate_modp(char *filename, char *modname) +allocate_modp(const char *filename, const char *modname) { struct modctl *mp; @@ -2473,12 +2538,12 @@ modgetsymname(uintptr_t value, ulong_t *offset) } /* - * Lookup a symbol in a specified module. This is a wrapper routine that - * calls kobj_lookup(). kobj_lookup() may go away but this - * wrapper will prevent callers from noticing. + * Lookup a symbol in a specified module. These are wrapper routines that + * call kobj_lookup(). kobj_lookup() may go away but these wrappers will + * prevent callers from noticing. */ uintptr_t -modlookup(char *modname, char *symname) +modlookup(const char *modname, const char *symname) { struct modctl *modp; uintptr_t val; @@ -2490,6 +2555,14 @@ modlookup(char *modname, char *symname) return (val); } +uintptr_t +modlookup_by_modctl(modctl_t *modp, const char *symname) +{ + ASSERT(modp->mod_ref > 0 || modp->mod_busy); + + return (kobj_lookup(modp->mod_mp, symname)); +} + /* * Ask the user for the name of the system file and the default path * for modules. @@ -3149,9 +3222,9 @@ mod_hold_by_modctl(struct modctl *mp, int f) } static struct modctl * -mod_hold_by_name_common(struct modctl *dep, char *filename) +mod_hold_by_name_common(struct modctl *dep, const char *filename) { - char *modname; + const char *modname; struct modctl *mp; char *curname, *newname; int found = 0; @@ -3232,12 +3305,12 @@ mod_hold_by_name_requisite(struct modctl *dep, char *filename) } struct modctl * -mod_hold_by_name(char *filename) +mod_hold_by_name(const char *filename) { return (mod_hold_by_name_common(NULL, filename)); } -static struct modctl * +struct modctl * mod_hold_by_id(modid_t modid) { struct modctl *mp; diff --git a/usr/src/uts/common/os/panic.c b/usr/src/uts/common/os/panic.c index e085b0e586..87910574f5 100644 --- a/usr/src/uts/common/os/panic.c +++ b/usr/src/uts/common/os/panic.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -143,6 +143,7 @@ #include <sys/spl.h> #include <sys/errorq.h> #include <sys/panic.h> +#include <sys/fm/util.h> /* * Panic variables which are set once during the QUIESCE state by the diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c index 0dccc35dce..fe4a5c82df 100644 --- a/usr/src/uts/common/os/policy.c +++ b/usr/src/uts/common/os/policy.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -1279,6 +1279,12 @@ secpolicy_kmdb(const cred_t *scr) return (PRIV_POLICY(scr, PRIV_ALL, B_FALSE, EPERM, NULL)); } +int +secpolicy_error_inject(const cred_t *scr) +{ + return (PRIV_POLICY(scr, PRIV_ALL, B_FALSE, EPERM, NULL)); +} + /* * Processor sets, cpu configuration, resource pools. */ diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile index bb8e6e8d7a..f82a933903 100644 --- a/usr/src/uts/common/sys/Makefile +++ b/usr/src/uts/common/sys/Makefile @@ -20,7 +20,7 @@ # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -657,12 +657,6 @@ FMHDRS= \ protocol.h \ util.h -sparc_FMCPUHDRS= \ - UltraSPARC-II.h \ - UltraSPARC-III.h \ - UltraSPARC-T1.h -FMCPUHDRS=$($(MACH)_FMCPUHDRS) - FMIOHDRS= \ ddi.h \ pci.h \ @@ -920,7 +914,6 @@ CHECKHDRS= \ $(TAVORHDRS:%.h=ib/adapters/tavor/%.check) \ $(ISOHDRS:%.h=iso/%.check) \ $(FMHDRS:%.h=fm/%.check) \ - $(FMCPUHDRS:%.h=fm/cpu/%.check) \ $(FMIOHDRS:%.h=fm/io/%.check) \ $(FSHDRS:%.h=fs/%.check) \ $(LVMHDRS:%.h=lvm/%.check) \ @@ -955,8 +948,7 @@ CHECKHDRS= \ $(ROOTDCAMHDRS) \ $(ROOTISOHDRS) \ $(ROOTFMHDRS) \ - $(ROOTFMCPUHDRS) \ - $(ROOTFMIOHDRS) \ + $(ROOTFMIOHDRS) \ $(ROOTFSHDRS) \ $(ROOTIBDHDRS) \ $(ROOTIBHDRS) \ @@ -1000,7 +992,6 @@ install_h: \ $(ROOTDCAMHDRS) \ $(ROOTISOHDRS) \ $(ROOTFMHDRS) \ - $(ROOTFMCPUHDRS) \ $(ROOTFMIOHDRS) \ $(ROOTFSHDRS) \ $(ROOTIBDHDRS) \ diff --git a/usr/src/uts/common/sys/chip.h b/usr/src/uts/common/sys/chip.h index e33b521783..80b9541d9a 100644 --- a/usr/src/uts/common/sys/chip.h +++ b/usr/src/uts/common/sys/chip.h @@ -172,6 +172,7 @@ void chip_cpu_fini(cpu_t *); void chip_cpu_assign(cpu_t *); void chip_cpu_unassign(cpu_t *); void chip_cpu_startup(cpu_t *); +chip_t *chip_lookup(chipid_t); void chip_bootstrap_cpu(cpu_t *); void chip_cpu_move_part(cpu_t *, struct cpupart *, diff --git a/usr/src/uts/common/sys/dumphdr.h b/usr/src/uts/common/sys/dumphdr.h index 7be14a40ed..5949b218c8 100644 --- a/usr/src/uts/common/sys/dumphdr.h +++ b/usr/src/uts/common/sys/dumphdr.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,7 +33,6 @@ #include <sys/param.h> #include <sys/utsname.h> #include <sys/log.h> -#include <sys/fm/util.h> #ifdef __cplusplus extern "C" { diff --git a/usr/src/uts/common/sys/fm/protocol.h b/usr/src/uts/common/sys/fm/protocol.h index 32f4d099f0..89b761ef6c 100644 --- a/usr/src/uts/common/sys/fm/protocol.h +++ b/usr/src/uts/common/sys/fm/protocol.h @@ -21,7 +21,7 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -177,7 +177,8 @@ extern "C" { #define FM_HC_VERS0 0 #define FM_HC_SCHEME_VERSION FM_HC_VERS0 #define CPU_SCHEME_VERSION0 0 -#define FM_CPU_SCHEME_VERSION CPU_SCHEME_VERSION0 +#define CPU_SCHEME_VERSION1 1 +#define FM_CPU_SCHEME_VERSION CPU_SCHEME_VERSION1 #define MEM_SCHEME_VERSION0 0 #define FM_MEM_SCHEME_VERSION MEM_SCHEME_VERSION0 #define MOD_SCHEME_VERSION0 0 @@ -194,6 +195,7 @@ extern "C" { #define FM_FMRI_HC_ROOT "hc-root" #define FM_FMRI_HC_LIST_SZ "hc-list-sz" #define FM_FMRI_HC_LIST "hc-list" +#define FM_FMRI_HC_SPECIFIC "hc-specific" /* hc-list version and member names */ #define FM_FMRI_HC_NAME "hc-name" @@ -202,6 +204,9 @@ extern "C" { #define HC_LIST_VERSION0 0 #define FM_HC_LIST_VERSION HC_LIST_VERSION0 +/* hc-specific member names */ +#define FM_FMRI_HC_SPECIFIC_OFFSET "offset" + /* fmd module scheme member names */ #define FM_FMRI_FMD_NAME "mod-name" #define FM_FMRI_FMD_VERSION "mod-version" @@ -261,13 +266,13 @@ extern void fm_ereport_set(nvlist_t *, int, const char *, uint64_t, const nvlist_t *, ...); extern void fm_payload_set(nvlist_t *, ...); extern int i_fm_payload_set(nvlist_t *, const char *, va_list); -extern void fm_fmri_hc_set(nvlist_t *, int, const nvlist_t *, const char *, - const char *, const char *, const char *, uint32_t, ...); +extern void fm_fmri_hc_set(nvlist_t *, int, const nvlist_t *, nvlist_t *, + int, ...); extern void fm_fmri_dev_set(nvlist_t *, int, const nvlist_t *, const char *, const char *); extern void fm_fmri_de_set(nvlist_t *, int, const nvlist_t *, const char *); extern void fm_fmri_cpu_set(nvlist_t *, int, const nvlist_t *, uint32_t, - uint8_t, uint64_t); + uint8_t *, const char *); extern void fm_fmri_mem_set(nvlist_t *, int, const nvlist_t *, const char *, const char *, uint64_t); extern void fm_authority_set(nvlist_t *, int, const char *, const char *, diff --git a/usr/src/uts/common/sys/fm/util.h b/usr/src/uts/common/sys/fm/util.h index c0dc908ddc..33f5876cab 100644 --- a/usr/src/uts/common/sys/fm/util.h +++ b/usr/src/uts/common/sys/fm/util.h @@ -19,8 +19,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -74,9 +75,14 @@ typedef struct erpt_dump { } erpt_dump_t; #ifdef _KERNEL +#include <sys/systm.h> +#define FM_STK_DEPTH 20 /* maximum stack depth */ +#define FM_SYM_SZ 64 /* maximum symbol size */ #define FM_ERR_PIL 2 /* PIL for ereport_errorq drain processing */ +#define FM_EREPORT_PAYLOAD_NAME_STACK "stack" + extern errorq_t *ereport_errorq; extern void *ereport_dumpbuf; extern size_t ereport_dumplen; @@ -89,6 +95,8 @@ extern void fm_banner(void); extern void fm_ereport_dump(void); extern void fm_ereport_post(nvlist_t *, int); +extern void fm_payload_stack_add(nvlist_t *, const pc_t *, int); + #endif /* _KERNEL */ #ifdef __cplusplus diff --git a/usr/src/uts/common/sys/kobj.h b/usr/src/uts/common/sys/kobj.h index c2f8b2f9cb..7d2bd0922e 100644 --- a/usr/src/uts/common/sys/kobj.h +++ b/usr/src/uts/common/sys/kobj.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -152,7 +152,7 @@ typedef struct { extern int kobj_load_module(struct modctl *, int); extern void kobj_unload_module(struct modctl *); -extern uintptr_t kobj_lookup(void *, char *); +extern uintptr_t kobj_lookup(struct module *, const char *); extern Sym *kobj_lookup_all(struct module *, char *, int); extern int kobj_addrcheck(void *, caddr_t); extern int kobj_module_to_id(void *); diff --git a/usr/src/uts/common/sys/mem.h b/usr/src/uts/common/sys/mem.h index f6f749ef0e..d1307589a1 100644 --- a/usr/src/uts/common/sys/mem.h +++ b/usr/src/uts/common/sys/mem.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -55,6 +55,14 @@ typedef struct mem_vtop { pfn_t m_pfn; } mem_vtop_t; +#if defined(_SYSCALL32) +typedef struct mem_vtop32 { + uint32_t m_as; + uint32_t m_va; + uint32_t m_pfn; +} mem_vtop32_t; +#endif + /* * Private ioctls for fmd(1M). These interfaces are Sun Private. Applications * and drivers should not make use of these interfaces: they can change without diff --git a/usr/src/uts/common/sys/modctl.h b/usr/src/uts/common/sys/modctl.h index 06019d5f0f..d2f4cfd803 100644 --- a/usr/src/uts/common/sys/modctl.h +++ b/usr/src/uts/common/sys/modctl.h @@ -62,6 +62,9 @@ struct mod_ops { * The defined set of mod_ops structures for each loadable module type * Defined in modctl.c */ +#if defined(__i386) || defined(__amd64) +extern struct mod_ops mod_cpuops; +#endif extern struct mod_ops mod_cryptoops; extern struct mod_ops mod_driverops; extern struct mod_ops mod_execops; @@ -107,6 +110,17 @@ struct modlfs { struct vfsdef_v3 *fs_vfsdef; /* version may actually vary */ }; +#if defined(__i386) || defined(__amd64) +struct cmi_ops; + +/* For CPU modules */ +struct modlcpu { + struct mod_ops *cpu_modops; + char *cpu_linkinfo; + struct cmi_ops *cpu_cmiops; +}; +#endif + /* For cryptographic providers */ struct modlcrypto { struct mod_ops *crypto_modops; @@ -417,7 +431,7 @@ struct modctl_list { * are replicated in the modctl structure so that mod_containing_pc() * doesn't have to grab any locks (modctls are persistent; modules are not.) */ -struct modctl { +typedef struct modctl { struct modctl *mod_next; /* &modules based list */ struct modctl *mod_prev; int mod_id; @@ -449,7 +463,7 @@ struct modctl { int mod_gencount; /* # times loaded/unloaded */ struct modctl *mod_requisite_loading; /* mod circular dependency */ -}; +} modctl_t; /* * mod_loadflags @@ -479,7 +493,10 @@ extern int moddebug; * this is the head of a doubly linked list. Only the next and prev * pointers are used */ -extern struct modctl modules; +extern modctl_t modules; + +extern int modload_qualified(const char *, + const char *, const char *, const char *, uint_t[], int); extern void mod_setup(void); extern int modload(char *, char *); @@ -494,7 +511,7 @@ extern int mod_remove_by_name(char *); extern int mod_sysvar(const char *, const char *, u_longlong_t *); extern int mod_sysctl(int, void *); struct sysparam; -extern int mod_hold_by_modctl(struct modctl *, int); +extern int mod_hold_by_modctl(modctl_t *, int); #define MOD_WAIT_ONCE 0x01 #define MOD_WAIT_FOREVER 0x02 #define MOD_LOCK_HELD 0x04 @@ -506,13 +523,15 @@ extern void mod_release_stub(struct mod_stub_info *); extern void mod_askparams(void); extern void mod_uninstall_daemon(void); extern void modreap(void); -extern struct modctl *mod_hold_by_name(char *); -extern void mod_release_mod(struct modctl *); -extern uintptr_t modlookup(char *, char *); +extern modctl_t *mod_hold_by_id(modid_t); +extern modctl_t *mod_hold_by_name(const char *); +extern void mod_release_mod(modctl_t *); +extern uintptr_t modlookup(const char *, const char *); +extern uintptr_t modlookup_by_modctl(modctl_t *, const char *); extern char *modgetsymname(uintptr_t, unsigned long *); -extern void mod_release_requisites(struct modctl *); -extern struct modctl *mod_load_requisite(struct modctl *, char *); -extern struct modctl *mod_find_by_filename(char *, char *); +extern void mod_release_requisites(modctl_t *); +extern modctl_t *mod_load_requisite(modctl_t *, char *); +extern modctl_t *mod_find_by_filename(char *, char *); extern uintptr_t modgetsymvalue(char *, int); extern void mod_rele_dev_by_major(major_t); @@ -532,11 +551,11 @@ extern void read_class_file(void); extern void setbootpath(char *); extern void setbootfstype(char *); -extern int install_stubs_by_name(struct modctl *, char *); -extern void install_stubs(struct modctl *); -extern void uninstall_stubs(struct modctl *); -extern void reset_stubs(struct modctl *); -extern struct modctl *mod_getctl(struct modlinkage *); +extern int install_stubs_by_name(modctl_t *, char *); +extern void install_stubs(modctl_t *); +extern void uninstall_stubs(modctl_t *); +extern void reset_stubs(modctl_t *); +extern modctl_t *mod_getctl(struct modlinkage *); extern major_t mod_name_to_major(char *); extern modid_t mod_name_to_modid(char *); extern char *mod_major_to_name(major_t); @@ -550,7 +569,7 @@ extern char *mod_containing_pc(caddr_t); extern int mod_in_autounload(void); extern char *mod_modname(struct modlinkage *); -extern int dev_minorperm(dev_info_t *dip, char *name, mperm_t *rmp); +extern int dev_minorperm(dev_info_t *, char *, mperm_t *); /* * Declarations used for dynamic linking support routines. Interfaces diff --git a/usr/src/uts/common/sys/policy.h b/usr/src/uts/common/sys/policy.h index 3e1b260c1e..9653a58b0e 100644 --- a/usr/src/uts/common/sys/policy.h +++ b/usr/src/uts/common/sys/policy.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -88,6 +88,7 @@ boolean_t secpolicy_contract_event_choice(const cred_t *); int secpolicy_coreadm(const cred_t *); int secpolicy_cpc_cpu(const cred_t *); int secpolicy_dispadm(const cred_t *); +int secpolicy_error_inject(const cred_t *); int secpolicy_excl_open(const cred_t *); int secpolicy_fs_mount(cred_t *, vnode_t *, struct vfs *); int secpolicy_fs_unmount(cred_t *, struct vfs *); diff --git a/usr/src/uts/i86pc/Makefile b/usr/src/uts/i86pc/Makefile index 90cc8f9dd2..a4bafb16ff 100644 --- a/usr/src/uts/i86pc/Makefile +++ b/usr/src/uts/i86pc/Makefile @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -69,11 +69,16 @@ check := TARGET= check .PARALLEL: $(KMODS) $(CLOSED_KMODS) $(XMODS) $(CLOSED_XMODS) \ modlist modlist.intel -def all clean clobber clean.lint: genassym unix .WAIT \ +INITIAL_TARGETS = \ + genassym \ + unix \ + cpu/scripts + +def all clean clobber clean.lint: setup genassym unix .WAIT \ $(KMODS) $(CLOSED_KMODS) $(XMODS) $(CLOSED_XMODS) -install: install_platforms genassym unix .WAIT $(KMODS) $(CLOSED_KMODS) \ - $(XMODS) $(CLOSED_XMODS) +install: install_platforms setup genassym unix .WAIT \ + $(KMODS) $(CLOSED_KMODS) $(XMODS) $(CLOSED_XMODS) # list the modules under i86pc. modlist: unix $(KMODS) $(CLOSED_KMODS) $(XMODS) $(CLOSED_XMODS) @@ -91,6 +96,9 @@ modlintlib: $(KMODS) $(CLOSED_KMODS) genassym unix $(KMODS): FRC @cd $@; pwd; $(MAKE) $(NO_STATE) $(TARGET) +setup: FRC + @cd cpu/scripts; pwd; $(MAKE) setup + $(XMODS): FRC @if [ -f $@/Makefile ]; then \ cd $@; pwd; $(MAKE) $(NO_STATE) $(TARGET); \ diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files index ff2999c94c..d9195b54ac 100644 --- a/usr/src/uts/i86pc/Makefile.files +++ b/usr/src/uts/i86pc/Makefile.files @@ -20,7 +20,7 @@ # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -37,6 +37,7 @@ CORE_OBJS += \ beeper.o \ biosdisk.o \ cbe.o \ + cmi.o \ confunix.o \ cpuid.o \ dis_tables.o \ @@ -127,6 +128,12 @@ AGPGART_OBJS += agpgart.o \ AGPTARGET_OBJS += agptarget.o AMD64GART_OBJS += amd64_gart.o +include $(SRC)/common/mc/mc-amd/Makefile.mcamd +MCAMD_OBJS += \ + $(MCAMD_CMN_OBJS) \ + mcamd_drv.o \ + mcamd_subr.o + # # PCI-Express driver modules # diff --git a/usr/src/uts/i86pc/Makefile.i86pc.shared b/usr/src/uts/i86pc/Makefile.i86pc.shared index c983d2a6ca..fcbd150bb8 100644 --- a/usr/src/uts/i86pc/Makefile.i86pc.shared +++ b/usr/src/uts/i86pc/Makefile.i86pc.shared @@ -21,7 +21,7 @@ # # uts/i86pc/Makefile.i86pc # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -260,16 +260,18 @@ DRV_KMODS += agptarget DRV_KMODS += amd64_gart DRV_KMODS += cpc +DRV_KMODS += mc-amd DRV_KMODS += power -$(CLOSED_BUILD)CLOSED_DRV_KMODS += ata -$(CLOSED_BUILD)CLOSED_DRV_KMODS += audiovia823x +$(CLOSED_BUILD)CLOSED_DRV_KMODS += ata +$(CLOSED_BUILD)CLOSED_DRV_KMODS += audiovia823x $(CLOSED_BUILD)CLOSED_DRV_KMODS += audioens $(CLOSED_BUILD)CLOSED_DRV_KMODS += bmc $(CLOSED_BUILD)CLOSED_DRV_KMODS_32 += bscbus $(CLOSED_BUILD)CLOSED_DRV_KMODS_32 += bscv $(CLOSED_BUILD)CLOSED_DRV_KMODS += elxl $(CLOSED_BUILD)CLOSED_DRV_KMODS += iprb +$(CLOSED_BUILD)CLOSED_DRV_KMODS += memtest $(CLOSED_BUILD)CLOSED_DRV_KMODS_32 += ncrs $(CLOSED_BUILD)CLOSED_DRV_KMODS += pcic $(CLOSED_BUILD)CLOSED_DRV_KMODS += pcn @@ -277,6 +279,12 @@ $(CLOSED_BUILD)CLOSED_DRV_KMODS += rtls $(CLOSED_BUILD)CLOSED_DRV_KMODS_32 += sbpro # +# CPU Modules +# +CPU_KMODS += amd_opteron +CPU_KMODS += generic_cpu + +# # Exec Class Modules (/kernel/exec): # EXEC_KMODS += diff --git a/usr/src/uts/i86pc/Makefile.rules b/usr/src/uts/i86pc/Makefile.rules index a272f48bf5..772db5ece7 100644 --- a/usr/src/uts/i86pc/Makefile.rules +++ b/usr/src/uts/i86pc/Makefile.rules @@ -20,7 +20,7 @@ # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #pragma ident "%Z%%M% %I% %E% SMI" @@ -45,10 +45,28 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/conf/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/cpu/amd_opteron/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/cpu/amd_opteron/%.s + $(COMPILE.s) -o $@ $< + +$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/cpu/generic_cpu/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/cpu/generic_cpu/%.s + $(COMPILE.s) -o $@ $< + $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/mc/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pci/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -118,6 +136,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/power/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(SRC)/common/mc/mc-amd/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/acpica/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -179,9 +201,24 @@ $(DTRACESTUBS): $(DTRACESTUBS_O) $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/conf/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/cpu/amd_opteron/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/cpu/amd_opteron/%.s + @($(LHEAD) $(LINT.s) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/cpu/generic_cpu/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/cpu/generic_cpu/%.s + @($(LHEAD) $(LINT.s) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/mc/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pci/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) @@ -227,6 +264,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/agpgart/%.c $(LINTS_DIR)/%.ln: $(SRC)/common/atomic/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(SRC)/common/mc/mc-amd/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/acpica/%.s @($(LHEAD) $(LINT.s) $< $(LTAIL)) diff --git a/usr/src/uts/i86pc/Makefile.workarounds b/usr/src/uts/i86pc/Makefile.workarounds index f6f79bde49..ffd5a20e69 100644 --- a/usr/src/uts/i86pc/Makefile.workarounds +++ b/usr/src/uts/i86pc/Makefile.workarounds @@ -19,10 +19,7 @@ # # CDDL HEADER END # -# -# uts/i86pc/Makefile.workarounds -# -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -57,6 +54,11 @@ WORKAROUND_DEFS += -DOPTERON_ERRATUM_95 WORKAROUND_DEFS += -DOPTERON_ERRATUM_100 # +# DRAM Scrubber May Cause Data Corruption When Using Node-Interleaved Memory +# +WORKAROUND_DEFS += -DOPTERON_ERRATUM_101 + +# # CPUID Instruction May Return Incorrect Model Number in Some Processors # WORKAROUND_DEFS += -DOPTERON_ERRATUM_108 diff --git a/usr/src/uts/i86pc/amd_opteron/Makefile b/usr/src/uts/i86pc/amd_opteron/Makefile new file mode 100644 index 0000000000..50db5b7db0 --- /dev/null +++ b/usr/src/uts/i86pc/amd_opteron/Makefile @@ -0,0 +1,105 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = cpu.AuthenticAMD.15 +# +OBJECTS = $(CPU_AO_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(CPU_AO_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_CPU_DIR)/$(MODULE) + +SRCDIR = ../cpu/amd_opteron + +AO_MCA_DISP_C = $(OBJS_DIR)/ao_mca_disp.c +AO_MCA_DISP_SRC = $(SRCDIR)/ao_mca_disp.in +AO_GENDISP = ../cpu/scripts/ao_gendisp + +# +# Include common rules. +# +include ../cpu/Makefile.cpu + +# +# Our lint library has a different name from that of the module we build. +# +LINT_MODULE = amd_opteron + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(LINT_MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Overrides and additions +# +CLEANFILES += $(AO_MCA_DISP_C) +CPPFLAGS += -I$(SRCDIR) -I$(OBJS_DIR) +ASFLAGS += -I$(SRCDIR) -I$(OBJS_DIR) + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Create ao_mca_disp.c +# +$(AO_MCA_DISP_C): $(AO_MCA_DISP_SRC) $(AO_GENDISP) + $(AO_GENDISP) $(AO_MCA_DISP_SRC) >$@ + +$(OBJS_DIR)/%.o: $(OBJS_DIR)/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +# +# Include common targets. +# +include ../Makefile.targ diff --git a/usr/src/uts/i86pc/cpu/Makefile.cpu b/usr/src/uts/i86pc/cpu/Makefile.cpu new file mode 100644 index 0000000000..c73344de87 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/Makefile.cpu @@ -0,0 +1,28 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include $(UTSBASE)/i86pc/Makefile.i86pc +include $(UTSBASE)/i86pc/cpu/Makefile.files diff --git a/usr/src/uts/i86pc/cpu/Makefile.files b/usr/src/uts/i86pc/cpu/Makefile.files new file mode 100644 index 0000000000..3252058682 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/Makefile.files @@ -0,0 +1,37 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +CPU_AO_OBJS = \ + ao_cpu.o \ + ao_main.o \ + ao_mc.o \ + ao_mca.o \ + ao_mca_disp.o \ + ao_poll.o + +CPU_GCPU_OBJS = \ + gcpu_main.o \ + gcpu_mca.o diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao.h b/usr/src/uts/i86pc/cpu/amd_opteron/ao.h new file mode 100644 index 0000000000..e5f6c518a8 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao.h @@ -0,0 +1,235 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _AO_H +#define _AO_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/mc.h> +#include <sys/mca_amd.h> +#include <sys/cpu_module_impl.h> +#include <sys/nvpair.h> +#include <sys/cyclic.h> +#include <sys/errorq.h> +#include <sys/kobj.h> +#include <sys/fm/util.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define AO_MCA_MAX_ERRORS 10 + +typedef struct ao_data ao_data_t; + +typedef struct ao_bank_regs { + uint32_t abr_status; + uint32_t abr_addr; +} ao_bank_regs_t; + +extern ao_bank_regs_t ao_bank_regs[AMD_MCA_BANK_COUNT]; + +/* + * Rather than using torturous conditionals, we match errors using a table of + * ao_error_disp_t's. The members in the ao_error_disp_t are matched against + * the value of MCi_STATUS, with a successful match indicating that the given + * error occurred. + * + * While aed_stat_code will match most of the status code bits, a few of the + * status code fields are either/or, and are treated separately so as to + * minimize the number of ao_error_disp_t structures that must be created. + * For example, the dc.tag_par error can have r4 values drd or dwr. Rather + * than creating two ao_error_disp_t's, we use the separate aed_stat_r4_bits + * field to indicate both AO_MCA_R4_BIT_DRD and AO_MCA_R4_BIT_DWD. As the + * matching r4 values are drawn from aed_stat_r4_bits, we don't use the r4 + * bits in aed_stat_code for matching. Similar reasoning lies behind the + * creation of the pp and ii fields. + */ +#define AO_AED_PANIC_NEVER 0x00000000 +#define AO_AED_PANIC_PRIV 0x00000001 +#define AO_AED_PANIC_USER 0x00000002 +#define AO_AED_PANIC_ALWAYS 0x00000003 + +#define AO_AED_F_CORRECTABLE 0x00000001 +#define AO_AED_F_LOFAULT_OK 0x00000002 + +typedef struct ao_error_disp { + const char *aed_class; /* ereport class for use if match */ + uint64_t aed_ereport_members; /* ereport contents flags if match */ + uint64_t aed_stat_mask; /* status msr bits for match */ + uint64_t aed_stat_mask_res; /* status mask result for match */ + uint16_t aed_stat_code; /* status code for match */ + uint8_t aed_stat_extcode; /* extended status code for match */ + uint8_t aed_stat_pp_bits:4; /* AO_MCA_PP_BIT_* for pp matching */ + uint8_t aed_stat_ii_bits:4; /* AO_MCA_II_BIT_* for ii matching */ + uint16_t aed_stat_r4_bits; /* AO_MCA_R4_BIT_* for r4 matching */ + uint8_t aed_panic_when; /* extra conditions for panic */ + uint8_t aed_flags; /* AO_AED_F_* */ +} ao_error_disp_t; + +/* + * The poller has two parts. First is the omni cyclic, which runs on all + * CPUs, and which polls the error MSRs at some fixed (long) interval. This + * cyclic will run on all machines, all the time, and thus must have minimal + * runtime impact. The second portion of the poller is manually-initiated, and + * is used by the error injector/synthesizer to request an immediate poll of the + * error state registers. + * + * With this number of moving parts, it is essential that we have some sort of + * audit log for post-mortem analysis. A circular array of trace buffers + * (ao_mca_poll_trace_t structures) is kept to record this activity. Whenever + * an event occurs that is of interest to the poller, an entry is made in + * the trace array describing that event. + */ +#define AO_MPT_WHAT_CYC_ERR 0 /* cyclic-induced poll */ +#define AO_MPT_WHAT_POKE_ERR 1 /* manually-induced poll */ +#define AO_MPT_WHAT_UNFAULTING 2 /* discarded error state */ + +typedef struct ao_mca_poll_trace { + hrtime_t mpt_when; /* timestamp of event */ + uint8_t mpt_what; /* AO_MPT_WHAT_* (which event?) */ + uint8_t mpt_nerr; /* number of errors discovered */ + uint16_t mpt_pad1; + uint32_t mpt_pad2; +} ao_mca_poll_trace_t; + +/* + * Processor error state is saved in logout areas. There are three separate + * logout areas, each used for a different purpose. The logout areas are stored + * in an array (ao_mca_logout), indexed by the AO_MCA_LOGOUT_* macros. + * + * The save areas are: + * + * 1. Exception handler MSR save - Written to by the initial portion of the #mc + * handler. Read from by the main body of the exception handler. + * + * 3. Poller MSR save - Used by the poller to store error state MSR values. + * While this logout area doesn't necessarily have to live in the ao_mca_t, + * it does so to enhance observability. + * + * The logout areas contain both global error state (acl_ip, acl_timestamp, + * etc.), as well as a bank array. The bank array contains one ao_bank_logout_t + * per error reporting bank. + */ + +typedef struct ao_bank_logout { + uint64_t abl_status; /* Saved MCi_STATUS register */ + uint64_t abl_addr; /* Saved MCi_ADDR register */ +} ao_bank_logout_t; + +#define AO_ACL_F_PRIV 0x1 /* #mc in kernel mode (else user) */ +#define AO_ACL_F_FATAL 0x2 /* logout detected fatal error(s) */ + +typedef struct ao_cpu_logout { + ao_data_t *acl_ao; /* pointer to per-cpu ao_data_t */ + uintptr_t acl_ip; /* instruction pointer if #mc trap */ + uint64_t acl_timestamp; /* gethrtime() at time of logout */ + uint64_t acl_mcg_status; /* MCG_STATUS register value */ + ao_bank_logout_t acl_banks[AMD_MCA_BANK_COUNT]; /* bank state saves */ + pc_t acl_stack[FM_STK_DEPTH]; /* saved stack trace (if any) */ + int acl_stackdepth; /* saved stack trace depth */ + uint_t acl_flags; /* flags (see AO_ACL_F_* above) */ +} ao_cpu_logout_t; + +/* Index for ao_mca_logout, below */ +#define AO_MCA_LOGOUT_EXCEPTION 0 +#define AO_MCA_LOGOUT_POLLER 1 +#define AO_MCA_LOGOUT_NUM 2 + +#define AO_MCA_F_UNFAULTING 0x1 /* CPU exiting faulted state */ + +/* + * We store config as inherited from the BIOS to assist in troubleshooting. + */ +typedef struct ao_bios_cfg { + uint64_t bcfg_bank_ctl[AMD_MCA_BANK_COUNT]; + uint64_t bcfg_bank_mask[AMD_MCA_BANK_COUNT]; + uint32_t bcfg_nb_cfg; +} ao_bios_cfg_t; + +/* + * The master data structure used to hold MCA-related state. + */ +typedef struct ao_mca { + ao_bios_cfg_t ao_mca_bios_cfg; /* Bank and NB config before our init */ + ao_cpu_logout_t ao_mca_logout[AO_MCA_LOGOUT_NUM]; /* save areas */ + kmutex_t ao_mca_poll_lock; /* keep pollers from colliding */ + ao_mca_poll_trace_t *ao_mca_poll_trace; /* trace buffers for this cpu */ + uint_t ao_mca_poll_curtrace; /* most recently-filled trace buffer */ + uint_t ao_mca_flags; /* AO_MCA_F_* */ +} ao_mca_t; + +/* + * Per-CPU state + */ +struct ao_data { + ao_mca_t ao_mca; /* MCA state for this CPU */ + cpu_t *ao_cpu; /* link to CPU's cpu_t */ + const cmi_mc_ops_t *ao_mc_ops; /* memory controller ops */ + void *ao_mc_data; /* argument for memory controller ops */ +}; + +#ifdef _KERNEL + +struct regs; + +extern errorq_t *ao_mca_queue; +extern const cmi_ops_t _cmi_ops; + +extern void ao_faulted_enter(void *); +extern void ao_faulted_exit(void *); +extern int ao_scrubber_enable(void *, uint64_t, uint64_t); + +extern void ao_mca_post_init(void *); +extern void ao_mca_init(void *); +extern int ao_mca_trap(void *, struct regs *); +extern int ao_mca_inject(void *, cmi_mca_regs_t *, uint_t); +extern void ao_mca_poke(void *); +extern void ao_mca_poll_init(ao_mca_t *); +extern void ao_mca_poll_start(void); + +extern int ao_mca_logout(ao_cpu_logout_t *, struct regs *, int *); +extern void ao_mca_drain(void *, const void *, const errorq_elem_t *); +extern nvlist_t *ao_fmri_create(ao_data_t *, nv_alloc_t *); + +extern void ao_mc_register(void *, const cmi_mc_ops_t *, void *); +extern const struct cmi_mc_ops *ao_mc_getops(void *); +extern int ao_mc_patounum(ao_data_t *, uint64_t, uint32_t, int, mc_unum_t *); +extern int ao_mc_unumtopa(ao_data_t *, mc_unum_t *, nvlist_t *, uint64_t *); + +extern void ao_pcicfg_write(uint_t, uint_t, uint_t, uint32_t); +extern uint32_t ao_pcicfg_read(uint_t, uint_t, uint_t); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _AO_H */ diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c new file mode 100644 index 0000000000..e13c672fac --- /dev/null +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c @@ -0,0 +1,203 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/chip.h> +#include <sys/cmn_err.h> +#include <sys/sysmacros.h> +#include <sys/fm/protocol.h> + +#include "ao.h" + +/* + * AMD Opteron CPU Subroutines + * + * The following three tunables are used to determine the scrubbing rates for + * the D$, L2$, and DRAM hardware scrubbers. The values range from 0x00-0x16 + * as described in BKDG 3.6.6 Scrub Control Register. A value of zero disables + * the scrubber. Values above zero indicate rates in descending order. + * + * The current default values are used on several Sun systems. In the future + * this code should assign values dynamically based on memory sizing. If you + * tune these values manually be aware of the following architectural issue: + * At present, Opteron can only survive certain kinds of multi-bit errors if + * they are detected by the scrubbers. Therefore in general we want these + * values tuned as high as possible without impacting workload performance. + */ +uint32_t ao_scrub_rate_dcache = 8; /* 64B every 5.12 us */ +uint32_t ao_scrub_rate_l2cache = 9; /* 64B every 10.2 us */ +uint32_t ao_scrub_rate_dram = 0xd; /* 64B every 163.8 us */ + +uint32_t ao_scrub_system; /* debug stash for system's value */ +uint32_t ao_scrub_bios; /* debug stash for bios's value */ +uint32_t ao_scrub_lo; /* debug stash for system low addr */ +uint32_t ao_scrub_hi; /* debug stash for system high addr */ + +enum { + AO_SCRUB_DEFAULT, /* retain system default values */ + AO_SCRUB_FIXED, /* assign ao_scrub_rate_* values */ + AO_SCRUB_MAX /* assign max of system and tunables */ +} ao_scrub_policy = AO_SCRUB_MAX; + +nvlist_t * +ao_fmri_create(ao_data_t *ao, nv_alloc_t *nva) +{ + nvlist_t *nvl = fm_nvlist_create(nva); + + fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 3, + "motherboard", 0, + "chip", ao->ao_cpu->cpu_chip->chip_id, + "cpu", chip_plat_get_clogid(ao->ao_cpu)); + + return (nvl); +} + +/* + * Return the maximum scrubbing rate between r1 and r2, where r2 is extracted + * from the specified 'cfg' register value using 'mask' and 'shift'. If a + * value is zero, scrubbing is off so return the opposite value. Otherwise + * the maximum rate is the smallest non-zero value of the two values. + */ +static uint32_t +ao_scrubber_max(uint32_t r1, uint32_t cfg, uint32_t mask, uint32_t shift) +{ + uint32_t r2 = (cfg & mask) >> shift; + + if (r1 != 0 && r2 != 0) + return (MIN(r1, r2)); + + return (r1 ? r1 : r2); +} + +/* + * Enable the chip-specific hardware scrubbers for the D$, L2$, and DRAM, and + * return a boolean value indicating if we enabled the DRAM scrubber. We set + * the scrubber rate based on a set of tunables defined at the top of the file. + * The 'base' parameter is the DRAM Base Address for this chip and is used to + * determine where the scrubber starts. The 'ilen' value is the IntvlEn field + * from the DRAM configuration indicating the node-interleaving configuration. + */ +int +ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen) +{ + ao_data_t *ao = data; + chipid_t chipid = chip_plat_get_chipid(ao->ao_cpu); + uint32_t scrubctl, lo, hi; + int rv = 1; + + /* + * Read the initial scrubber configuration and save it for debugging. + * If ao_scrub_policy is DEFAULT, return immediately. Otherwise we + * disable scrubbing activity while we fiddle with the configuration. + */ + scrubctl = ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBCTL); + cas32(&ao_scrub_bios, 0, scrubctl); + + if (ao_scrub_policy == AO_SCRUB_DEFAULT) + return ((scrubctl & AMD_NB_SCRUBCTL_DRAM_MASK) != 0); + + scrubctl &= ~AMD_NB_SCRUBCTL_DRAM_MASK; + scrubctl &= ~AMD_NB_SCRUBCTL_L2_MASK; + scrubctl &= ~AMD_NB_SCRUBCTL_DC_MASK; + + ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBCTL, scrubctl); + + /* + * Read the DRAM Scrub Address Low and High registers, clear their + * address fields, enable sequential-redirect mode, and update the + * address fields using the specified DRAM Base Address. + */ + lo = ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBADDR_LO); + hi = ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBADDR_HI); + + lo &= ~AMD_NB_SCRUBADDR_LO_MASK; + hi &= ~AMD_NB_SCRUBADDR_HI_MASK; + + lo |= AMD_NB_SCRUBADDR_MKLO(base) | AMD_NB_SCRUBADDR_LO_SCRUBREDIREN; + hi |= AMD_NB_SCRUBADDR_MKHI(base); + + ao_scrub_lo = lo; + ao_scrub_hi = hi; + + ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBADDR_LO, lo); + ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBADDR_HI, hi); + + if (ao_scrub_rate_dcache > AMD_NB_SCRUBCTL_RATE_MAX) { + cmn_err(CE_WARN, "ao_scrub_rate_dcache is too large; " + "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); + ao_scrub_rate_dcache = AMD_NB_SCRUBCTL_RATE_MAX; + } + + if (ao_scrub_rate_l2cache > AMD_NB_SCRUBCTL_RATE_MAX) { + cmn_err(CE_WARN, "ao_scrub_rate_l2cache is too large; " + "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); + ao_scrub_rate_l2cache = AMD_NB_SCRUBCTL_RATE_MAX; + } + + if (ao_scrub_rate_dram > AMD_NB_SCRUBCTL_RATE_MAX) { + cmn_err(CE_WARN, "ao_scrub_rate_dram is too large; " + "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); + ao_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_MAX; + } + + if (ao_scrub_policy == AO_SCRUB_MAX) { + ao_scrub_rate_dcache = + ao_scrubber_max(ao_scrub_rate_dcache, ao_scrub_bios, + AMD_NB_SCRUBCTL_DC_MASK, AMD_NB_SCRUBCTL_DC_SHIFT); + + ao_scrub_rate_l2cache = + ao_scrubber_max(ao_scrub_rate_l2cache, ao_scrub_bios, + AMD_NB_SCRUBCTL_L2_MASK, AMD_NB_SCRUBCTL_L2_SHIFT); + + ao_scrub_rate_dram = + ao_scrubber_max(ao_scrub_rate_dram, ao_scrub_bios, + AMD_NB_SCRUBCTL_DRAM_MASK, AMD_NB_SCRUBCTL_DRAM_SHIFT); + } + +#ifdef OPTERON_ERRATUM_101 + /* + * If the DRAM Base Address register's IntlvEn field indicates that + * node interleaving is enabled, we must disable the DRAM scrubber + * and return zero to indicate that Solaris should use s/w instead. + */ + if (ilen != 0) { + cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled because " + "DRAM memory is node-interleaved"); + ao_scrub_rate_dram = 0; + rv = 0; + } +#endif + scrubctl |= AMD_NB_MKSCRUBCTL(ao_scrub_rate_dcache, + ao_scrub_rate_l2cache, ao_scrub_rate_dram); + + ao_scrub_system = scrubctl; + ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBCTL, scrubctl); + + return (rv); +} diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c new file mode 100644 index 0000000000..777538adf0 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c @@ -0,0 +1,135 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The CPU module for the AMD Athlon64 and Opteron processors + */ + +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/sunddi.h> +#include <sys/cpu_module_impl.h> +#include <sys/cpuvar.h> +#include <sys/x86_archext.h> +#include <sys/kmem.h> +#include <sys/modctl.h> +#include <sys/mc.h> + +#include "ao.h" + +/* + * At present this CPU module only supports the features for Athlon64 and + * Opteron up to and including the Rev E processor. If we detect Rev F or + * later, return ENOTSUP and let the generic x86 CPU module load instead. + * Opteron Rev F is currently defined as Family 0xF Model [0x40 .. 0x5F]. + */ +static uint_t ao_model_limit = 0x40; + +static int +ao_init(cpu_t *cp, void **datap) +{ + ao_data_t *ao; + + if (cpuid_getmodel(cp) >= ao_model_limit) + return (ENOTSUP); + + ao = *datap = kmem_zalloc(sizeof (ao_data_t), KM_SLEEP); + ao->ao_cpu = cp; + + return (0); +} + +static void +ao_fini(void *data) +{ + kmem_free(data, sizeof (ao_data_t)); +} + +const cmi_ops_t _cmi_ops = { + ao_init, + ao_mca_post_init, + ao_fini, + ao_faulted_enter, + ao_faulted_exit, + ao_scrubber_enable, + ao_mca_init, + ao_mca_trap, + ao_mca_inject, + ao_mca_poke, + ao_mc_register, + ao_mc_getops +}; + +static struct modlcpu modlcpu = { + &mod_cpuops, + "AMD Athlon64/Opteron CPU Module" +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modlcpu, + NULL +}; + +int +_init(void) +{ + int err; + + ao_mca_queue = errorq_create("ao_mca_queue", + ao_mca_drain, NULL, AO_MCA_MAX_ERRORS * (max_ncpus + 1), + sizeof (ao_cpu_logout_t), 1, ERRORQ_VITAL); + + if (ao_mca_queue == NULL) + return (EAGAIN); /* errorq_create() logs a message for us */ + + if ((err = mod_install(&modlinkage)) != 0) { + errorq_destroy(ao_mca_queue); + ao_mca_queue = NULL; + } + + return (err); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +int +_fini(void) +{ + int err; + + if ((err = mod_remove(&modlinkage)) == 0) + errorq_destroy(ao_mca_queue); + + return (err); +} diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mc.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mc.c new file mode 100644 index 0000000000..b2fd0ca82f --- /dev/null +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mc.c @@ -0,0 +1,72 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/cpu_module_impl.h> + +#include "ao.h" + +void +ao_mc_register(void *data, const cmi_mc_ops_t *mcops, void *mcdata) +{ + ao_data_t *ao = data; + + ASSERT(ao->ao_mc_ops == NULL); + + ao->ao_mc_ops = mcops; + ao->ao_mc_data = mcdata; +} + +const struct cmi_mc_ops * +ao_mc_getops(void *data) +{ + ao_data_t *ao = data; + + return (ao->ao_mc_ops); +} + +int +ao_mc_patounum(ao_data_t *ao, uint64_t pa, uint32_t synd, int syndtype, + mc_unum_t *unump) +{ + if (ao->ao_mc_ops == NULL) + return (0); /* mc not registered, or failed to load */ + + return (ao->ao_mc_ops->cmi_mc_patounum(ao->ao_mc_data, pa, synd, + syndtype, unump)); +} + +int +ao_mc_unumtopa(ao_data_t *ao, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap) +{ + if (ao->ao_mc_ops == NULL) + return (0); /* mc not registered, or failed to load */ + + return (ao->ao_mc_ops->cmi_mc_unumtopa(ao->ao_mc_data, unump, nvl, + pap)); +} diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c new file mode 100644 index 0000000000..e8884f212d --- /dev/null +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c @@ -0,0 +1,811 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/regset.h> +#include <sys/privregs.h> +#include <sys/pci_impl.h> +#include <sys/cpuvar.h> +#include <sys/x86_archext.h> +#include <sys/cmn_err.h> +#include <sys/systm.h> +#include <sys/sysmacros.h> +#include <sys/chip.h> +#include <sys/cyclic.h> +#include <sys/cpu_module_impl.h> +#include <sys/pci_cfgspace_impl.h> +#include <sys/sysevent.h> +#include <sys/smbios.h> +#include <sys/mca_x86.h> +#include <sys/mca_amd.h> +#include <sys/mc.h> +#include <sys/psw.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/sdt.h> +#include <sys/fm/util.h> +#include <sys/fm/protocol.h> +#include <sys/fm/cpu/AMD.h> + +#include "ao.h" +#include "ao_mca_disp.h" + +errorq_t *ao_mca_queue; /* machine-check ereport queue */ +int ao_mca_stack_flag = 1; /* record stack trace in ereports */ +int ao_mca_smi_disable = 1; /* attempt to disable SMI polling */ + +ao_bank_regs_t ao_bank_regs[AMD_MCA_BANK_COUNT] = { + { AMD_MSR_DC_STATUS, AMD_MSR_DC_ADDR }, + { AMD_MSR_IC_STATUS, AMD_MSR_IC_ADDR }, + { AMD_MSR_BU_STATUS, AMD_MSR_BU_ADDR }, + { AMD_MSR_LS_STATUS, AMD_MSR_LS_ADDR }, + { AMD_MSR_NB_STATUS, AMD_MSR_NB_ADDR } +}; + +typedef struct ao_bank_cfg { + uint_t bank_ctl; + uint_t bank_ctl_mask; + uint64_t bank_ctl_init; + uint_t bank_status; + uint_t bank_addr; +} ao_bank_cfg_t; + +static const ao_bank_cfg_t ao_bank_cfgs[] = { + { AMD_MSR_DC_CTL, AMD_MSR_DC_MASK, AMD_DC_CTL_INIT, AMD_MSR_DC_STATUS, + AMD_MSR_DC_ADDR }, + { AMD_MSR_IC_CTL, AMD_MSR_IC_MASK, AMD_IC_CTL_INIT, AMD_MSR_IC_STATUS, + AMD_MSR_IC_ADDR }, + { AMD_MSR_BU_CTL, AMD_MSR_BU_MASK, AMD_BU_CTL_INIT, AMD_MSR_BU_STATUS, + AMD_MSR_BU_ADDR }, + { AMD_MSR_LS_CTL, AMD_MSR_LS_MASK, AMD_LS_CTL_INIT, AMD_MSR_LS_STATUS, + AMD_MSR_LS_ADDR }, + { AMD_MSR_NB_CTL, AMD_MSR_NB_MASK, AMD_NB_CTL_INIT, AMD_MSR_NB_STATUS, + AMD_MSR_NB_ADDR } +}; + +static const ao_error_disp_t ao_disp_unknown = { + FM_EREPORT_CPU_AMD_UNKNOWN, + FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_UNKNOWN +}; + +/* + * This is quite awful but necessary to work around x86 system vendor's view of + * the world. Other operating systems (you know who you are) don't understand + * Opteron-specific error handling, so BIOS and system vendors often hide these + * conditions from them by using SMI polling to copy out any errors from the + * machine-check registers. When Solaris runs on a system with this feature, + * we want to disable the SMI polling so we can use FMA instead. Sadly, there + * isn't even a standard self-describing way to express the whole situation, + * so we have to resort to hard-coded values. This should all be changed to + * be a self-describing vendor-specific SMBIOS structure in the future. + */ +static const struct ao_smi_disable { + const char *asd_sys_vendor; /* SMB_TYPE_SYSTEM vendor prefix */ + const char *asd_bios_vendor; /* SMB_TYPE_BIOS vendor prefix */ + uint32_t asd_port; /* output port for SMI disable */ + uint32_t asd_code; /* output code for SMI disable */ +} ao_smi_disable[] = { + { "Sun Microsystems", "American Megatrends", 0x502F, 0x59 }, + { NULL, NULL, 0, 0 } +}; + +static int +ao_disp_match_r4(uint16_t ref, uint8_t r4) +{ + static const uint16_t ao_r4_map[] = { + AO_MCA_R4_BIT_GEN, /* AMD_ERRCODE_R4_GEN */ + AO_MCA_R4_BIT_RD, /* AMD_ERRCODE_R4_RD */ + AO_MCA_R4_BIT_WR, /* AMD_ERRCODE_R4_WR */ + AO_MCA_R4_BIT_DRD, /* AMD_ERRCODE_R4_DRD */ + AO_MCA_R4_BIT_DWR, /* AMD_ERRCODE_R4_DWR */ + AO_MCA_R4_BIT_IRD, /* AMD_ERRCODE_R4_IRD */ + AO_MCA_R4_BIT_PREFETCH, /* AMD_ERRCODE_R4_PREFETCH */ + AO_MCA_R4_BIT_EVICT, /* AMD_ERRCODE_R4_EVICT */ + AO_MCA_R4_BIT_SNOOP /* AMD_ERRCODE_R4_SNOOP */ + }; + + ASSERT(r4 < sizeof (ao_r4_map) / sizeof (uint16_t)); + + return ((ref & ao_r4_map[r4]) != 0); +} + +static int +ao_disp_match_pp(uint8_t ref, uint8_t pp) +{ + static const uint8_t ao_pp_map[] = { + AO_MCA_PP_BIT_SRC, /* AMD_ERRCODE_PP_SRC */ + AO_MCA_PP_BIT_RSP, /* AMD_ERRCODE_PP_RSP */ + AO_MCA_PP_BIT_OBS, /* AMD_ERRCODE_PP_OBS */ + AO_MCA_PP_BIT_GEN /* AMD_ERRCODE_PP_GEN */ + }; + + ASSERT(pp < sizeof (ao_pp_map) / sizeof (uint8_t)); + + return ((ref & ao_pp_map[pp]) != 0); +} + +static int +ao_disp_match_ii(uint8_t ref, uint8_t ii) +{ + static const uint8_t ao_ii_map[] = { + AO_MCA_II_BIT_MEM, /* AMD_ERRCODE_II_MEM */ + 0, + AO_MCA_II_BIT_IO, /* AMD_ERRCODE_II_IO */ + AO_MCA_II_BIT_GEN /* AMD_ERRCODE_II_GEN */ + }; + + ASSERT(ii < sizeof (ao_ii_map) / sizeof (uint8_t)); + + return ((ref & ao_ii_map[ii]) != 0); +} + +static uint8_t +bit_strip(uint16_t *codep, uint16_t mask, uint16_t shift) +{ + uint8_t val = (*codep & mask) >> shift; + *codep &= ~mask; + return (val); +} + +#define BIT_STRIP(codep, name) \ + bit_strip(codep, AMD_ERRCODE_##name##_MASK, AMD_ERRCODE_##name##_SHIFT) + +static int +ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status) +{ + uint16_t code = status & AMD_ERRCODE_MASK; + uint8_t extcode = (status & AMD_ERREXT_MASK) >> AMD_ERREXT_SHIFT; + uint64_t stat_mask = aed->aed_stat_mask; + uint64_t stat_mask_res = aed->aed_stat_mask_res; + + /* + * If the bank's status register indicates overflow, then we can no + * longer rely on the value of CECC: our experience with actual fault + * injection has shown that multiple CE's overwriting each other shows + * AMD_BANK_STAT_CECC and AMD_BANK_STAT_UECC both set to zero. This + * should be clarified in a future BKDG or by the Revision Guide. + */ + if (status & AMD_BANK_STAT_OVER) { + stat_mask &= ~AMD_BANK_STAT_CECC; + stat_mask_res &= ~AMD_BANK_STAT_CECC; + } + + if ((status & stat_mask) != stat_mask_res) + return (0); + + /* + * r4 and pp bits are stored separately, so we mask off and compare them + * for the code types that use them. Once we've taken the r4 and pp + * bits out of the equation, we can directly compare the resulting code + * with the one stored in the ao_error_disp_t. + */ + if (AMD_ERRCODE_ISMEM(code)) { + uint8_t r4 = BIT_STRIP(&code, R4); + + if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4)) + return (0); + + } else if (AMD_ERRCODE_ISBUS(code)) { + uint8_t r4 = BIT_STRIP(&code, R4); + uint8_t pp = BIT_STRIP(&code, PP); + uint8_t ii = BIT_STRIP(&code, II); + + if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4) || + !ao_disp_match_pp(aed->aed_stat_pp_bits, pp) || + !ao_disp_match_ii(aed->aed_stat_ii_bits, ii)) + return (0); + } + + return (code == aed->aed_stat_code && extcode == aed->aed_stat_extcode); +} + +static const ao_error_disp_t * +ao_disp_match(uint_t bankno, uint64_t status) +{ + const ao_error_disp_t *aed; + + for (aed = ao_error_disp[bankno]; aed->aed_stat_mask != 0; aed++) { + if (ao_disp_match_one(aed, status)) + return (aed); + } + + return (&ao_disp_unknown); +} + +void +ao_pcicfg_write(uint_t chipid, uint_t func, uint_t reg, uint32_t val) +{ + ASSERT(chipid + 24 <= 31); + ASSERT((func & 7) == func); + ASSERT((reg & 3) == 0 && reg < 256); + + pci_mech1_putl(0, chipid + 24, func, reg, val); +} + +uint32_t +ao_pcicfg_read(uint_t chipid, uint_t func, uint_t reg) +{ + ASSERT(chipid + 24 <= 31); + ASSERT((func & 7) == func); + ASSERT((reg & 3) == 0 && reg < 256); + + return (pci_mech1_getl(0, chipid + 24, func, reg)); +} + +/* + * Setup individual bank detectors after stashing their bios settings. + */ +static void +ao_bank_cfg(ao_mca_t *mca) +{ + ao_bios_cfg_t *bioscfg = &mca->ao_mca_bios_cfg; + const ao_bank_cfg_t *bankcfg = ao_bank_cfgs; + int i; + + for (i = 0; i < AMD_MCA_BANK_COUNT; i++, bankcfg++) { + bioscfg->bcfg_bank_ctl[i] = rdmsr(bankcfg->bank_ctl); + bioscfg->bcfg_bank_mask[i] = rdmsr(bankcfg->bank_ctl_mask); + wrmsr(bankcfg->bank_ctl, bankcfg->bank_ctl_init); + } +} + +/* + * Bits to be added to the NorthBridge (NB) configuration register. + * See BKDG 3.29 Section 3.6.4.2 for more information. + */ +uint32_t ao_nb_cfg_add = + AMD_NB_CFG_NBMCATOMSTCPUEN | + AMD_NB_CFG_DISPCICFGCPUERRRSP | + AMD_NB_CFG_SYNCONUCECCEN | + AMD_NB_CFG_CPUECCERREN; + +/* + * Bits to be cleared from the NorthBridge (NB) configuration register. + * See BKDG 3.29 Section 3.6.4.2 for more information. + */ +uint32_t ao_nb_cfg_remove = + AMD_NB_CFG_IORDDATERREN | + AMD_NB_CFG_SYNCONANYERREN | + AMD_NB_CFG_SYNCONWDOGEN | + AMD_NB_CFG_IOERRDIS | + AMD_NB_CFG_IOMSTABORTDIS | + AMD_NB_CFG_SYNCPKTPROPDIS | + AMD_NB_CFG_SYNCPKTGENDIS; + +/* + * Bits to be used if we configure the NorthBridge (NB) Watchdog. The watchdog + * triggers a machine check exception when no response to an NB system access + * occurs within a specified time interval. If the BIOS (i.e. platform design) + * has enabled the watchdog, we leave its rate alone. If the BIOS has not + * enabled the watchdog, we enable it and set the rate to one specified below. + * To disable the watchdog, add the AMD_NB_CFG_WDOGTMRDIS bit to ao_nb_cfg_add. + */ +uint32_t ao_nb_cfg_wdog = + AMD_NB_CFG_WDOGTMRCNTSEL_4095 | + AMD_NB_CFG_WDOGTMRBASESEL_1MS; + +static void +ao_nb_cfg(ao_mca_t *mca) +{ + uint_t chipid = chip_plat_get_chipid(CPU); + uint32_t val; + + if (chip_plat_get_clogid(CPU) != 0) + return; /* only configure NB once per CPU */ + + /* + * Read the NorthBridge (NB) configuration register in PCI space, + * modify the settings accordingly, and store the new value back. + */ + mca->ao_mca_bios_cfg.bcfg_nb_cfg = val = + ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_CFG); + + /* + * If the watchdog was disabled, enable it according to the policy + * described above. Then apply the ao_nb_cfg_[add|remove] masks. + */ + if (val & AMD_NB_CFG_WDOGTMRDIS) { + val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK; + val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK; + val &= ~AMD_NB_CFG_WDOGTMRDIS; + val |= ao_nb_cfg_wdog; + } + + val &= ~ao_nb_cfg_remove; + val |= ao_nb_cfg_add; + + ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_CFG, val); +} + +/* + * Capture the machine-check exception state into our per-CPU logout area, and + * dispatch a copy of the logout area to our error queue for ereport creation. + * If 'rp' is non-NULL, we're being called from trap context; otherwise we're + * being polled or poked by the injector. We return the number of errors + * found through 'np', and a boolean indicating whether the error is fatal. + * The caller is expected to call fm_panic() if we return fatal (non-zero). + */ +int +ao_mca_logout(ao_cpu_logout_t *acl, struct regs *rp, int *np) +{ + int i, fatal, n = 0; + + acl->acl_timestamp = gethrtime_waitfree(); + acl->acl_mcg_status = rdmsr(IA32_MSR_MCG_STATUS); + acl->acl_ip = rp ? rp->r_pc : 0; + acl->acl_flags = 0; + + /* + * Iterate over the banks of machine-check registers, read the address + * and status registers into the logout area, and clear them as we go. + */ + for (i = 0; i < AMD_MCA_BANK_COUNT; i++) { + ao_bank_logout_t *abl = &acl->acl_banks[i]; + + abl->abl_addr = rdmsr(ao_bank_regs[i].abr_addr); + abl->abl_status = rdmsr(ao_bank_regs[i].abr_status); + + if (abl->abl_status & AMD_BANK_STAT_VALID) + wrmsr(ao_bank_regs[i].abr_status, 0); + } + + if (rp == NULL || !USERMODE(rp->r_cs)) + acl->acl_flags |= AO_ACL_F_PRIV; + + if (ao_mca_stack_flag) + acl->acl_stackdepth = getpcstack(acl->acl_stack, FM_STK_DEPTH); + else + acl->acl_stackdepth = 0; + + /* + * Clear MCG_STATUS, indicating that machine-check trap processing is + * complete. Once we do this, another machine-check trap can occur. + */ + wrmsr(IA32_MSR_MCG_STATUS, 0); + + /* + * If we took a machine-check trap, then the error is fatal if the + * return instruction pointer is not valid in the global register. + */ + fatal = rp != NULL && !(acl->acl_mcg_status & MCG_STATUS_RIPV); + + /* + * Now iterate over the saved logout area, determining whether the + * error that we saw is fatal or not based upon our dispositions + * and the hardware's indicators of whether or not we can resume. + */ + for (i = 0; i < AMD_MCA_BANK_COUNT; i++) { + ao_bank_logout_t *abl = &acl->acl_banks[i]; + const ao_error_disp_t *aed; + + if (!(abl->abl_status & AMD_BANK_STAT_VALID)) + continue; + + aed = ao_disp_match(i, abl->abl_status); + fatal |= (aed->aed_panic_when != AO_AED_PANIC_NEVER); + + /* + * If we are taking a machine-check exception and the overflow + * bit is set or our context is corrupt, then we must die. + * NOTE: This code assumes that if the overflow bit is set and + * we didn't take a #mc exception (i.e. the poller found it), + * then multiple correctable errors overwrote each other. + * This will need to change if we eventually use the Opteron + * Rev E exception mechanism for detecting correctable errors. + */ + if (rp != NULL && (abl->abl_status & + (AMD_BANK_STAT_OVER | AMD_BANK_STAT_PCC))) + fatal = 1; + + /* + * If we are taking a machine-check exception and we don't + * recognize the error case at all, then assume it's fatal. + * This will need to change if we eventually use the Opteron + * Rev E exception mechanism for detecting correctable errors. + */ + if (rp != NULL && aed == &ao_disp_unknown) + fatal = 1; + + n++; + } + + if (n > 0) { + errorq_dispatch(ao_mca_queue, acl, sizeof (ao_cpu_logout_t), + fatal && cmi_panic_on_uncorrectable_error ? + ERRORQ_SYNC : ERRORQ_ASYNC); + } + + if (np != NULL) + *np = n; /* return number of errors found to caller */ + + return (fatal); +} + +static uint_t +ao_ereport_synd(ao_mca_t *mca, + const ao_bank_logout_t *abl, uint_t *typep, int is_nb) +{ + if (is_nb) { + if ((mca->ao_mca_bios_cfg.bcfg_nb_cfg & + AMD_NB_CFG_CHIPKILLECCEN) != 0) { + *typep = AMD_SYNDTYPE_CHIPKILL; + return (AMD_NB_STAT_CKSYND(abl->abl_status)); + } else { + *typep = AMD_SYNDTYPE_ECC; + return (AMD_BANK_SYND(abl->abl_status)); + } + } else { + *typep = AMD_SYNDTYPE_ECC; + return (AMD_BANK_SYND(abl->abl_status)); + } +} + +static void +ao_ereport_create_resource_elem(nvlist_t **nvlp, nv_alloc_t *nva, + mc_unum_t *unump, int dimmnum) +{ + nvlist_t *snvl; + *nvlp = fm_nvlist_create(nva); /* freed by caller */ + + snvl = fm_nvlist_create(nva); + + (void) nvlist_add_uint64(snvl, FM_FMRI_HC_SPECIFIC_OFFSET, + unump->unum_offset); + + fm_fmri_hc_set(*nvlp, FM_HC_SCHEME_VERSION, NULL, snvl, 4, + "motherboard", unump->unum_board, + "chip", unump->unum_chip, + "memory-controller", unump->unum_mc, + "dimm", unump->unum_dimms[dimmnum]); + + fm_nvlist_destroy(snvl, FM_NVA_FREE); +} + +static void +ao_ereport_add_resource(nvlist_t *payload, nv_alloc_t *nva, mc_unum_t *unump) +{ + + nvlist_t *elems[MC_UNUM_NDIMM]; + int nelems = 0; + int i; + + for (i = 0; i < MC_UNUM_NDIMM; i++) { + if (unump->unum_dimms[i] == -1) + break; + ao_ereport_create_resource_elem(&elems[nelems++], nva, + unump, i); + } + + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE, + DATA_TYPE_NVLIST_ARRAY, nelems, elems); + + for (i = 0; i < nelems; i++) + fm_nvlist_destroy(elems[i], FM_NVA_FREE); +} + +static void +ao_ereport_add_logout(ao_data_t *ao, nvlist_t *payload, nv_alloc_t *nva, + const ao_cpu_logout_t *acl, uint_t bankno, const ao_error_disp_t *aed) +{ + uint64_t members = aed->aed_ereport_members; + ao_mca_t *mca = &ao->ao_mca; + const ao_bank_logout_t *abl = &acl->acl_banks[bankno]; + uint_t synd, syndtype; + + synd = ao_ereport_synd(mca, abl, &syndtype, bankno == AMD_MCA_BANK_NB); + + if (members & FM_EREPORT_PAYLOAD_FLAG_BANK_STAT) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BANK_STAT, + DATA_TYPE_UINT64, abl->abl_status, NULL); + } + + if (members & FM_EREPORT_PAYLOAD_FLAG_BANK_NUM) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BANK_NUM, + DATA_TYPE_UINT8, bankno, NULL); + } + + if (members & FM_EREPORT_PAYLOAD_FLAG_ADDR) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_ADDR, + DATA_TYPE_UINT64, abl->abl_addr, NULL); + } + + if (members & FM_EREPORT_PAYLOAD_FLAG_ADDR_VALID) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_ADDR_VALID, + DATA_TYPE_BOOLEAN_VALUE, (abl->abl_status & + AMD_BANK_STAT_ADDRV) ? B_TRUE : B_FALSE); + } + + if (members & FM_EREPORT_PAYLOAD_FLAG_SYND) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SYND, + DATA_TYPE_UINT16, synd, NULL); + } + + if (members & FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SYND_TYPE, + DATA_TYPE_STRING, (syndtype == AMD_SYNDTYPE_CHIPKILL ? + "C" : "E"), NULL); + } + + if (members & FM_EREPORT_PAYLOAD_FLAG_IP) { + uint64_t ip = (acl->acl_mcg_status & MCG_STATUS_EIPV) ? + acl->acl_ip : 0; + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_IP, + DATA_TYPE_UINT64, ip, NULL); + } + + if (members & FM_EREPORT_PAYLOAD_FLAG_PRIV) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PRIV, + DATA_TYPE_BOOLEAN_VALUE, (acl->acl_flags & AO_ACL_F_PRIV) ? + B_TRUE : B_FALSE, NULL); + } + + if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE) { + mc_unum_t unum; + int addrvalid; + + addrvalid = (members & FM_EREPORT_PAYLOAD_FLAG_ADDR) && + (members & FM_EREPORT_PAYLOAD_FLAG_ADDR_VALID) && + (abl->abl_status & AMD_BANK_STAT_ADDRV); + + if (addrvalid && ao_mc_patounum(ao, abl->abl_addr, synd, + syndtype, &unum)) + ao_ereport_add_resource(payload, nva, &unum); + } + + if (ao_mca_stack_flag && members & FM_EREPORT_PAYLOAD_FLAG_STACK) { + fm_payload_stack_add(payload, acl->acl_stack, + acl->acl_stackdepth); + } +} + +static void +ao_ereport_post(const ao_cpu_logout_t *acl, + int bankno, const ao_error_disp_t *aed) +{ + ao_data_t *ao = acl->acl_ao; + errorq_elem_t *eqep; + nvlist_t *ereport, *detector; + nv_alloc_t *nva = NULL; + char buf[FM_MAX_CLASS]; + + if (panicstr) { + if ((eqep = errorq_reserve(ereport_errorq)) == NULL) + return; + ereport = errorq_elem_nvl(ereport_errorq, eqep); + nva = errorq_elem_nva(ereport_errorq, eqep); + } else { + ereport = fm_nvlist_create(nva); + } + + /* + * Create the scheme "cpu" FMRI + */ + detector = ao_fmri_create(ao, nva); + + /* + * Encode all the common data into the ereport. + */ + (void) snprintf(buf, FM_MAX_CLASS, "%s.%s.%s", + FM_ERROR_CPU, "amd", aed->aed_class); + + fm_ereport_set(ereport, FM_EREPORT_VERSION, buf, + fm_ena_generate_cpu(acl->acl_timestamp, ao->ao_cpu->cpu_id, + FM_ENA_FMT1), detector, NULL); + + /* + * Encode the error-specific data that was saved in the logout area. + */ + ao_ereport_add_logout(ao, ereport, nva, acl, bankno, aed); + + if (panicstr) { + errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); + } else { + (void) fm_ereport_post(ereport, EVCH_TRYHARD); + fm_nvlist_destroy(ereport, FM_NVA_FREE); + fm_nvlist_destroy(detector, FM_NVA_FREE); + } +} + +/*ARGSUSED*/ +void +ao_mca_drain(void *ignored, const void *data, const errorq_elem_t *eqe) +{ + const ao_cpu_logout_t *acl = data; + int i; + + for (i = 0; i < AMD_MCA_BANK_COUNT; i++) { + const ao_bank_logout_t *abl = &acl->acl_banks[i]; + const ao_error_disp_t *aed; + + if (abl->abl_status & AMD_BANK_STAT_VALID) { + aed = ao_disp_match(i, abl->abl_status); + ao_ereport_post(acl, i, aed); + } + } +} + +int +ao_mca_trap(void *data, struct regs *rp) +{ + ao_data_t *ao = data; + ao_mca_t *mca = &ao->ao_mca; + ao_cpu_logout_t *acl = &mca->ao_mca_logout[AO_MCA_LOGOUT_EXCEPTION]; + return (ao_mca_logout(acl, rp, NULL)); +} + +/*ARGSUSED*/ +int +ao_mca_inject(void *data, cmi_mca_regs_t *regs, uint_t nregs) +{ + uint64_t hwcr, oldhwcr; + int i; + + oldhwcr = rdmsr(MSR_AMD_HWCR); + hwcr = oldhwcr | AMD_HWCR_MCI_STATUS_WREN; + wrmsr(MSR_AMD_HWCR, hwcr); + + for (i = 0; i < nregs; i++) + wrmsr(regs[i].cmr_msrnum, regs[i].cmr_msrval); + + wrmsr(MSR_AMD_HWCR, oldhwcr); + return (0); +} + +void +ao_mca_init(void *data) +{ + ao_data_t *ao = data; + ao_mca_t *mca = &ao->ao_mca; + uint64_t cap; + int i; + + ao_mca_poll_init(mca); + + ASSERT(x86_feature & X86_MCA); + cap = rdmsr(IA32_MSR_MCG_CAP); + ASSERT(cap & MCG_CAP_CTL_P); + + /* + * If the hardware's bank count is different than what we expect, then + * we're running on some Opteron variant that we don't understand yet. + */ + if ((cap & MCG_CAP_COUNT_MASK) != AMD_MCA_BANK_COUNT) { + cmn_err(CE_WARN, "CPU %d has %llu MCA banks; expected %u: " + "disabling MCA on this CPU", ao->ao_cpu->cpu_id, + (u_longlong_t)cap & MCG_CAP_COUNT_MASK, AMD_MCA_BANK_COUNT); + return; + } + + /* + * Configure the logout areas. We preset every logout area's acl_ao + * pointer to refer back to our per-CPU state for errorq drain usage. + */ + for (i = 0; i < AO_MCA_LOGOUT_NUM; i++) + mca->ao_mca_logout[i].acl_ao = ao; + + ao_bank_cfg(mca); + ao_nb_cfg(mca); + + wrmsr(IA32_MSR_MCG_CTL, AMD_MCG_EN_ALL); + + /* + * Throw away all existing bank state. We do this because some BIOSes, + * perhaps during POST, do things to the machine that cause MCA state + * to be updated. If we interpret this state as an actual error, we + * may end up indicting something that's not actually broken. + */ + for (i = 0; i < sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfg_t); i++) + wrmsr(ao_bank_cfgs[i].bank_status, 0ULL); + + wrmsr(IA32_MSR_MCG_STATUS, 0ULL); + membar_producer(); + + setcr4(getcr4() | CR4_MCE); /* enable #mc exceptions */ +} + +/*ARGSUSED*/ +void +ao_mca_post_init(void *data) +{ + const struct ao_smi_disable *asd; + id_t id; + + smbios_system_t sy; + smbios_bios_t sb; + smbios_info_t si; + + /* + * Fetch the System and BIOS vendor strings from SMBIOS and see if they + * match a value in our table. If so, disable SMI error polling. This + * is grotesque and should be replaced by self-describing vendor- + * specific SMBIOS data or a specification enhancement instead. + */ + if (ao_mca_smi_disable && ksmbios != NULL && + smbios_info_bios(ksmbios, &sb) != SMB_ERR && + (id = smbios_info_system(ksmbios, &sy)) != SMB_ERR && + smbios_info_common(ksmbios, id, &si) != SMB_ERR) { + + for (asd = ao_smi_disable; asd->asd_sys_vendor != NULL; asd++) { + if (strncmp(asd->asd_sys_vendor, si.smbi_manufacturer, + strlen(asd->asd_sys_vendor)) != 0 || + strncmp(asd->asd_bios_vendor, sb.smbb_vendor, + strlen(asd->asd_bios_vendor)) != 0) + continue; + + cmn_err(CE_CONT, "?SMI polling disabled in favor of " + "Solaris Fault Management for AMD Processors"); + + outl(asd->asd_port, asd->asd_code); + break; + } + } + + ao_mca_poll_start(); +} + +/* + * Called after a CPU has been marked with CPU_FAULTED. Not called on the + * faulted CPU. cpu_lock is held. + */ +/*ARGSUSED*/ +void +ao_faulted_enter(void *data) +{ + /* + * Nothing to do here. We'd like to turn off the faulted CPU's + * correctable error detectors, but that can only be done by the + * faulted CPU itself. cpu_get_state() will now return P_FAULTED, + * allowing the poller to skip this CPU until it is re-enabled. + */ +} + +/* + * Called after the CPU_FAULTED bit has been cleared from a previously-faulted + * CPU. Not called on the faulted CPU. cpu_lock is held. + */ +void +ao_faulted_exit(void *data) +{ + ao_data_t *ao = data; + + /* + * We'd like to clear the faulted CPU's MCi_STATUS registers so as to + * avoid generating ereports for errors which occurred while the CPU was + * officially faulted. Unfortunately, those registers can only be + * cleared by the CPU itself, so we can't do it here. + * + * We're going to set the UNFAULTING bit on the formerly-faulted CPU's + * MCA state. This will tell the poller that the MCi_STATUS registers + * can't yet be trusted. The poller, which is the first thing we + * control that'll execute on that CPU, will clear the registers, and + * will then clear the bit. + */ + + ao->ao_mca.ao_mca_flags |= AO_MCA_F_UNFAULTING; +} diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.h b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.h new file mode 100644 index 0000000000..ccb1c01bce --- /dev/null +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.h @@ -0,0 +1,67 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _AO_MCA_DISP_H +#define _AO_MCA_DISP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/mca_amd.h> +#include <sys/fm/cpu/AMD.h> + +#include "ao.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define AO_MCA_PP_BIT_SRC 0x1 +#define AO_MCA_PP_BIT_RSP 0x2 +#define AO_MCA_PP_BIT_OBS 0x4 +#define AO_MCA_PP_BIT_GEN 0x8 + +#define AO_MCA_II_BIT_MEM 0x1 +#define AO_MCA_II_BIT_IO 0x2 +#define AO_MCA_II_BIT_GEN 0x4 + +#define AO_MCA_R4_BIT_GEN 0x001 +#define AO_MCA_R4_BIT_RD 0x002 +#define AO_MCA_R4_BIT_WR 0x004 +#define AO_MCA_R4_BIT_DRD 0x008 +#define AO_MCA_R4_BIT_DWD 0x010 +#define AO_MCA_R4_BIT_DWR 0x020 +#define AO_MCA_R4_BIT_IRD 0x040 +#define AO_MCA_R4_BIT_PREFETCH 0x080 +#define AO_MCA_R4_BIT_EVICT 0x100 +#define AO_MCA_R4_BIT_SNOOP 0x200 + +extern const ao_error_disp_t *ao_error_disp[]; + +#ifdef __cplusplus +} +#endif + +#endif /* _AO_MCA_DISP_H */ diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in new file mode 100644 index 0000000000..b8d88e71a5 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in @@ -0,0 +1,778 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +funcunit = dc + +desc = Correctable D$ data infill from system memory +error = ereport.cpu.amd.dc.inf_sys_ecc1 + +mask on = AMD_BANK_STAT_CECC +mask off = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src 0 drd mem/io lg - + +panic = never +flags = correctable + +# --- + +desc = Correctable D$ data infill from L2$ +error = ereport.cpu.amd.dc.inf_l2_ecc1 + +mask on = AMD_BANK_STAT_CECC +mask off = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - drd - l2 data + +panic = never +flags = correctable + +# --- + +desc = Uncorrectable D$ data infill from system memory +error = ereport.cpu.amd.dc.inf_sys_eccm + +mask on = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src 0 drd mem/io lg - + +panic = always +flags = + +# --- + +desc = Uncorrectable D$ data infill from L2$ +error = ereport.cpu.amd.dc.inf_l2_eccm + +mask on = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - drd - l2 data + +panic = always +flags = correctable + +# --- + +desc = Correctable single-bit error in Data Array from scrub +error = ereport.cpu.amd.dc.data_ecc1 + +mask on = AMD_BANK_STAT_CECC, AMD_BANK_STAT_SCRUB +mask off = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - gen - l1 data + +panic = never +flags = correctable + +# --- + +desc = Uncorrectable single-bit error in Data Array +error = ereport.cpu.amd.dc.data_ecc1_uc + +mask on = AMD_BANK_STAT_UC, AMD_BANK_STAT_CECC +mask off = AMD_BANK_STAT_SCRUB + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - drd/dwr/ev/snp - l1 data + +panic = always +flags = + +# --- + +desc = Uncorrectable multi-bit error in Data Array +error = ereport.cpu.amd.dc.data_eccm + +mask on = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_SCRUB + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - drd/dwr/ev/snp - l1 data + +panic = always +flags = + +# --- + +desc = Uncorrectable multi-bit error in Data Array from scrub +error = ereport.cpu.amd.dc.data_eccm + +mask on = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC, AMD_BANK_STAT_SCRUB +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - gen - l1 data + +panic = always +flags = + +# --- + +desc = Main Tag Array Parity Error +error = ereport.cpu.amd.dc.tag_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - drd/dwr - l1 data + +panic = always +flags = + +# --- + +desc = Snoop Tag Array Parity Error +error = ereport.cpu.amd.dc.stag_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - snp/ev - l1 data + +panic = always +flags = + +# --- + +desc = L1 DTLB Parity Error +error = ereport.cpu.amd.dc.l1tlb_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 tlb - - - - l1 data + +panic = always +flags = + +# --- + +desc = L1 DTLB Parity Error (multimatch) +error = ereport.cpu.amd.dc.l1tlb_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0001 tlb - - - - l1 data + +panic = always +flags = + +# --- + +desc = L2 DTLB Parity Error +error = ereport.cpu.amd.dc.l2tlb_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 tlb - - - - l2 data + +panic = always +flags = + +# --- + +desc = L2 DTLB Parity Error (multimatch) +error = ereport.cpu.amd.dc.l2tlb_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0001 tlb - - - - l2 data + +panic = always +flags = + +# +# Instruction Cache Functional Unit +# + +funcunit = ic + +desc = Correctable I$ data infill from system memory +error = ereport.cpu.amd.ic.inf_sys_ecc1 + +mask on = AMD_BANK_STAT_CECC +mask off = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src 0 ird mem lg - + +panic = never +flags = correctable + +# ---- + +desc = Correctable I$ data infill from L2$ +error = ereport.cpu.amd.ic.inf_l2_ecc1 + +mask on = AMD_BANK_STAT_CECC +mask off = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - ird - l2 instr + +panic = never +flags = correctable + +# ---- + +desc = Uncorrectable I$ data infill from system memory +error = ereport.cpu.amd.ic.inf_sys_eccm + +mask on = AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src 0 ird mem lg - + +panic = always +flags = + +# --- + +desc = Uncorrectable I$ data infill from L2$ +error = ereport.cpu.amd.ic.inf_l2_eccm + +mask on = AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - ird - l2 instr + +panic = always +flags = + +# --- + +desc = Data Array Parity Error +error = ereport.cpu.amd.ic.data_par + +mask on = +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - ird - l1 instr + +panic = never +flags = correctable + +# --- + +desc = Main Tag Array Parity Error +error = ereport.cpu.amd.ic.tag_par + +mask on = +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - ird/ev - l1 instr + +panic = never +flags = correctable + +# --- + +desc = Snoop Tag Array Parity Error +error = ereport.cpu.amd.ic.stag_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - snp/ev - l1 instr + +panic = always +flags = + +# --- + +desc = L1 ITLB Parity Error +error = ereport.cpu.amd.ic.l1tlb_par + +mask on = +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 tlb - - - - l1 instr + +panic = never +flags = correctable + +# --- + +desc = L1 ITLB Parity Error (multimatch) +error = ereport.cpu.amd.ic.l1tlb_par + +mask on = +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0001 tlb - - - - l1 instr + +panic = never +flags = correctable + +# --- + +desc = L2 ITLB Parity Error +error = ereport.cpu.amd.ic.l2tlb_par + +mask on = +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 tlb - - - - l2 instr + +panic = never +flags = correctable + +# --- + +desc = L2 ITLB Parity Error (multimatch) +error = ereport.cpu.amd.ic.l2tlb_par + +mask on = +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0001 tlb - - - - l2 instr + +panic = never +flags = correctable + +# --- + +desc = System Data Read Error +error = ereport.cpu.amd.ic.rdde + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src 0 ird mem lg - + +panic = always +flags = + +# +# --- +# + +funcunit = bu + +# --- + +desc = L2 data array single-bit ECC during TLB reload, snoop, or copyback +error = ereport.cpu.amd.bu.l2d_ecc1 + +mask on = AMD_BANK_STAT_CECC +mask off = AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - rd/snp/ev - l2 gen + +panic = never +flags = correctable + +# --- + +desc = L2 data array multi-bit ECC during TLB reload, snoop, or copyback +error = ereport.cpu.amd.bu.l2d_eccm + +mask on = AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 mem - - rd/snp/ev - l2 gen + +panic = always +flags = + +# --- + +desc = L2 main tag array single-bit ECC error on scrubber access +error = ereport.cpu.amd.bu.l2t_ecc1 + +mask on = AMD_BANK_STAT_CECC, AMD_BANK_STAT_SCRUB +mask off = AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0010 mem - - gen - l2 instr + +panic = never +flags = correctable + +# --- + +desc = L2 main tag array multi-bit ECC error on scrubber access +error = ereport.cpu.amd.bu.l2t_eccm + +mask on = AMD_BANK_STAT_UECC, AMD_BANK_STAT_UC, AMD_BANK_STAT_SCRUB +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0010 mem - - gen - l2 instr + +panic = always +flags = + +# --- + +desc = L2 main tag array parity error on I$ fetch +error = ereport.cpu.amd.bu.l2t_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0010 mem - - ird - l2 instr + +panic = always +flags = + +# --- + +desc = L2 main tag array parity error on D$ fetch +error = ereport.cpu.amd.bu.l2t_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0010 mem - - drd - l2 data + +panic = always +flags = + +# --- + +desc = L2 main tag array parity error on TLB reload, snoop, or copyback +error = ereport.cpu.amd.bu.l2t_par + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0010 mem - - rd/snp/ev - l2 gen + +panic = always +flags = + +# --- + +desc = L2 main tag array parity error on scrubber access +error = ereport.cpu.amd.bu.l2t_par + +mask on = AMD_BANK_STAT_UC, AMD_BANK_STAT_SCRUB +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0010 mem - - gen - l2 instr + +panic = always +flags = + +# --- + +desc = System data single-bit ECC for hardware prefetch or TLB reload +error = ereport.cpu.amd.bu.s_ecc1 + +mask on = AMD_BANK_STAT_CECC +mask off = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src 0 rd/pf mem/io lg - + +panic = never +flags = correctable + +# --- + +desc = System data multi-bit ECC for hardware prefetch or TLB reload +error = ereport.cpu.amd.bu.s_eccm + +mask on = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src 0 rd/pf mem/io lg - + +panic = always +flags = + +# --- + +desc = System data read error for TLB reload or hardware prefetch +error = ereport.cpu.amd.bu.s_rde + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src 0 rd/pf mem/io lg - + +panic = always +flags = + +# +# --- +# + +funcunit = ls + +desc = System data read error +error = ereport.cpu.amd.ls.s_rde + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src 0 rd/wr mem/io lg - + +panic = always +flags = + +# +# --- +# + +funcunit = nb + +desc = Correctable ECC error from Normal ECC +error = ereport.cpu.amd.nb.mem_ce + +mask on = AMD_BANK_STAT_CECC +mask off = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src/rsp 0 rd/wr mem lg - + +panic = never +flags = correctable + +# --- + +desc = Uncorrectable ECC error from Normal ECC +error = ereport.cpu.amd.nb.mem_ue + +mask on = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0000 bus src/rsp 0 rd/wr mem lg - + +panic = always +flags = + +# --- + +desc = Correctable ECC error from ChipKill ECC +error = ereport.cpu.amd.nb.mem_ce + +mask on = AMD_BANK_STAT_CECC +mask off = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 1000 bus src/rsp 0 rd/wr mem lg - + +panic = never +flags = correctable + +# --- + +desc = Uncorrectable ECC error from ChipKill ECC +error = ereport.cpu.amd.nb.mem_ue + +mask on = AMD_BANK_STAT_UC, AMD_BANK_STAT_UECC +mask off = AMD_BANK_STAT_CECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 1000 bus src/rsp 0 rd/wr mem lg - + +panic = always +flags = + +# --- + +desc = Hypertransport CRC error +error = ereport.cpu.amd.nb.ht_crc + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0001 bus obs 0 gen gen lg - + +panic = always +flags = + +# --- + +desc = Hypertransport Sync packet error +error = ereport.cpu.amd.nb.ht_sync + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0010 bus obs 0 gen gen lg - + +panic = always +flags = + +# --- + +desc = Master Abort +error = ereport.cpu.amd.nb.ma + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0011 bus src/obs 0 rd/wr mem/io lg - + +panic = never +flags = + +# --- + +desc = Target Abort +error = ereport.cpu.amd.nb.ta + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0100 bus src/obs 0 rd/wr mem/io lg - + +panic = never +flags = + +# --- + +desc = GART Table Walk Error +error = ereport.cpu.amd.nb.gart_walk + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0101 tlb - - - - lg gen + +panic = always +flags = + +# --- + +desc = Atomic Read/Modify/Write error +error = ereport.cpu.amd.nb.rmw + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0110 bus obs 0 gen io lg - + +panic = always +flags = + +# --- + +desc = Watchdog error (timeout) +error = ereport.cpu.amd.nb.wdog + +mask on = AMD_BANK_STAT_UC +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC + +# ext type pp t rrrr ii ll tt +# ------- ------- ------- ------- --------------- ------- ------- ----- +code = 0111 bus gen 1 gen gen lg - + +panic = always +flags = diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c new file mode 100644 index 0000000000..e083351d12 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c @@ -0,0 +1,194 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * AMD Athlon64/Opteron CPU Module Machine-Check Poller + * + * The AMD Opteron processor doesn't yet report correctable errors via #mc's. + * Instead, it fixes the problem, silently updates the error state MSRs, and + * resumes operation. In order to discover occurrances of correctable errors, + * we have to poll in the background using the omni cyclics mechanism. The + * error injector also has the ability to manually request an immediate poll. + * Locking is fairly simple within the poller: the per-CPU mutex + * ao->ao_mca.ao_mca_poll_lock ensures that only one poll request is active. + */ + +#include <sys/types.h> +#include <sys/sysmacros.h> +#include <sys/x86_archext.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/ksynch.h> +#include <sys/sdt.h> + +#include "ao.h" + +static uint_t ao_mca_poll_trace_nent = 100; +#ifdef DEBUG +static uint_t ao_mca_poll_trace_always = 1; +#else +static uint_t ao_mca_poll_trace_always = 0; +#endif + +static cyclic_id_t ao_mca_poll_cycid; +static hrtime_t ao_mca_poll_interval = NANOSEC * 10ULL; + +static void +ao_mca_poll_trace(ao_mca_t *mca, uint32_t what, uint32_t nerr) +{ + uint_t next; + ao_mca_poll_trace_t *pt; + + ASSERT(MUTEX_HELD(&mca->ao_mca_poll_lock)); + DTRACE_PROBE2(ao__poll__trace, uint32_t, what, uint32_t, nerr); + + if (mca->ao_mca_poll_trace == NULL) + return; /* poll trace buffer is disabled */ + + next = (mca->ao_mca_poll_curtrace + 1) % ao_mca_poll_trace_nent; + pt = &mca->ao_mca_poll_trace[next]; + + pt->mpt_when = 0; + pt->mpt_what = what; + + if (what == AO_MPT_WHAT_CYC_ERR) + pt->mpt_nerr = MIN(nerr, UINT8_MAX); + + pt->mpt_when = gethrtime(); + mca->ao_mca_poll_curtrace = next; +} + +static void +ao_mca_poll_common(ao_mca_t *mca, int what) +{ + ao_cpu_logout_t *acl = &mca->ao_mca_logout[AO_MCA_LOGOUT_POLLER]; + int i, n, fatal; + + if (mca->ao_mca_flags & AO_MCA_F_UNFAULTING) { + mca->ao_mca_flags &= ~AO_MCA_F_UNFAULTING; + ao_mca_poll_trace(mca, AO_MPT_WHAT_UNFAULTING, 0); + + /* + * On the first poll after re-enabling a faulty CPU we clear + * the status registers; see ao_faulted_exit() for more info. + */ + if (what == AO_MPT_WHAT_CYC_ERR) { + for (i = 0; i < AMD_MCA_BANK_COUNT; i++) + wrmsr(ao_bank_regs[i].abr_status, 0); + return; + } + } + + fatal = ao_mca_logout(acl, NULL, &n); + ao_mca_poll_trace(mca, what, n); + + if (fatal && cmi_panic_on_uncorrectable_error) + fm_panic("Unrecoverable Machine-Check Exception"); +} + +static void +ao_mca_poll_cyclic(void *arg) +{ + ao_data_t *ao = arg; + + if (ao != NULL && mutex_tryenter(&ao->ao_mca.ao_mca_poll_lock)) { + ao_mca_poll_common(&ao->ao_mca, AO_MPT_WHAT_CYC_ERR); + mutex_exit(&ao->ao_mca.ao_mca_poll_lock); + } +} + +void +ao_mca_poke(void *arg) +{ + ao_data_t *ao = arg; + + mutex_enter(&ao->ao_mca.ao_mca_poll_lock); + ao_mca_poll_common(&ao->ao_mca, AO_MPT_WHAT_POKE_ERR); + mutex_exit(&ao->ao_mca.ao_mca_poll_lock); +} + +/*ARGSUSED*/ +static void +ao_mca_poll_online(void *arg, cpu_t *cpu, cyc_handler_t *cyh, cyc_time_t *cyt) +{ + cyt->cyt_when = 0; + cyh->cyh_level = CY_LOW_LEVEL; + + /* + * If the CPU coming on-line isn't supported by this CPU module, then + * disable the cylic by cranking cyt_interval and setting arg to NULL. + */ + if (cpu->cpu_m.mcpu_cmi != NULL && + cpu->cpu_m.mcpu_cmi->cmi_ops != &_cmi_ops) { + cyt->cyt_interval = INT64_MAX; + cyh->cyh_func = ao_mca_poll_cyclic; + cyh->cyh_arg = NULL; + } else { + cyt->cyt_interval = ao_mca_poll_interval; + cyh->cyh_func = ao_mca_poll_cyclic; + cyh->cyh_arg = cpu->cpu_m.mcpu_cmidata; + } +} + +/*ARGSUSED*/ +static void +ao_mca_poll_offline(void *arg, cpu_t *cpu, void *cyh_arg) +{ + /* nothing to do here */ +} + +void +ao_mca_poll_init(ao_mca_t *mca) +{ + mutex_init(&mca->ao_mca_poll_lock, NULL, MUTEX_DRIVER, NULL); + + if (ao_mca_poll_trace_always) { + mca->ao_mca_poll_trace = + kmem_zalloc(sizeof (ao_mca_poll_trace_t) * + ao_mca_poll_trace_nent, KM_SLEEP); + mca->ao_mca_poll_curtrace = 0; + } +} + +void +ao_mca_poll_start(void) +{ + cyc_omni_handler_t cyo; + + if (ao_mca_poll_interval == 0) + return; /* if manually tuned to zero, disable polling */ + + cyo.cyo_online = ao_mca_poll_online; + cyo.cyo_offline = ao_mca_poll_offline; + cyo.cyo_arg = NULL; + + mutex_enter(&cpu_lock); + ao_mca_poll_cycid = cyclic_add_omni(&cyo); + mutex_exit(&cpu_lock); +} diff --git a/usr/src/uts/i86pc/cpu/generic_cpu/gcpu.h b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu.h new file mode 100644 index 0000000000..f330920591 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu.h @@ -0,0 +1,72 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _GCPU_H +#define _GCPU_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/cpu_module.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct gcpu_mca_bank { + uint_t bank_ctl; /* MCi_CTL MSR */ + uint_t bank_status; /* MCi_STATUS MSR */ + uint_t bank_addr; /* MCi_ADDR MSR */ + uint_t bank_misc; /* MCi_MISC MSR */ +} gcpu_mca_bank_t; + +typedef struct gcpu_mca_data { + uint64_t bank_status_data; /* MCi_STATUS value from exception */ + uint64_t bank_addr_data; /* MCi_ADDR value from exception */ + uint64_t bank_misc_data; /* MCi_MISC value from exception */ +} gcpu_mca_data_t; + +typedef struct gcpu_mca { + const gcpu_mca_bank_t *gcpu_mca_banks; + gcpu_mca_data_t *gcpu_mca_data; + uint_t gcpu_mca_nbanks; +} gcpu_mca_t; + +typedef struct gcpu_data { + gcpu_mca_t gcpu_mca; +} gcpu_data_t; + +struct regs; + +extern void gcpu_mca_init(void *); +extern int gcpu_mca_trap(void *, struct regs *); + +#ifdef __cplusplus +} +#endif + +#endif /* _GCPU_H */ diff --git a/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_main.c b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_main.c new file mode 100644 index 0000000000..68b3d8221a --- /dev/null +++ b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_main.c @@ -0,0 +1,125 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Generic x86 CPU Module + * + * This CPU module is used for generic x86 CPUs when Solaris has no other + * CPU-specific support module available. Code in this module should be the + * absolute bare-bones support and must be cognizant of both Intel and AMD etc. + */ + +#include <sys/types.h> +#include <sys/cpu_module_impl.h> +#include <sys/cpuvar.h> +#include <sys/kmem.h> +#include <sys/modctl.h> + +#include "gcpu.h" + +/*ARGSUSED*/ +static void +gcpu_nop(void *data) +{ +} + +static int +gcpu_notsup(void) +{ + return (ENOTSUP); +} + +static int +gcpu_nil(void) +{ + return (0); +} + +/*ARGSUSED*/ +static int +gcpu_init(cpu_t *cpu, void **datap) +{ + *datap = kmem_zalloc(sizeof (gcpu_data_t), KM_SLEEP); + return (0); +} + +static void +gcpu_fini(void *data) +{ + gcpu_data_t *dp = data; + + kmem_free(dp->gcpu_mca.gcpu_mca_data, + dp->gcpu_mca.gcpu_mca_nbanks * sizeof (gcpu_mca_data_t)); + + kmem_free(dp, sizeof (gcpu_data_t)); +} + +const cmi_ops_t _cmi_ops = { + gcpu_init, /* cmi_init */ + gcpu_nop, /* cmi_post_init */ + gcpu_fini, /* cmi_fini */ + gcpu_nop, /* cmi_faulted_enter */ + gcpu_nop, /* cmi_faulted_exit */ + (int (*)())gcpu_nil, /* cmi_scrubber_enable */ + gcpu_mca_init, /* cmi_mca_init */ + gcpu_mca_trap, /* cmi_mca_trap */ + (int (*)())gcpu_notsup, /* cmi_mca_inject */ + gcpu_nop, /* cmi_mca_poke */ + (void (*)())gcpu_nop, /* cmi_mc_register */ + (const cmi_mc_ops_t *(*)())gcpu_nop /* cmi_mc_getops */ +}; + +static struct modlcpu modlcpu = { + &mod_cpuops, + "Generic x86 CPU Module" +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modlcpu, + NULL +}; + +int +_init(void) +{ + return (mod_install(&modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +int +_fini(void) +{ + return (mod_remove(&modlinkage)); +} diff --git a/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_mca.c b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_mca.c new file mode 100644 index 0000000000..3793ff1087 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_mca.c @@ -0,0 +1,230 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/mca_x86.h> +#include <sys/cpu_module_impl.h> +#include <sys/cmn_err.h> +#include <sys/cpuvar.h> +#include <sys/x86_archext.h> +#include <sys/controlregs.h> +#include <sys/sysmacros.h> +#include <sys/regset.h> +#include <sys/privregs.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/log.h> +#include <sys/psw.h> + +#include "gcpu.h" + +/* + * x86 architecture standard banks for IA32 and compatible processors. These + * are effectively the lowest common denominators for the MCA architecture. + */ +static const gcpu_mca_bank_t gcpu_mca_banks_ia32[] = { +{ IA32_MSR_MC0_CTL, IA32_MSR_MC0_STATUS, IA32_MSR_MC0_ADDR, IA32_MSR_MC0_MISC }, +{ IA32_MSR_MC1_CTL, IA32_MSR_MC1_STATUS, IA32_MSR_MC1_ADDR, IA32_MSR_MC1_MISC }, +{ IA32_MSR_MC2_CTL, IA32_MSR_MC2_STATUS, IA32_MSR_MC2_ADDR, IA32_MSR_MC2_MISC }, +{ IA32_MSR_MC3_CTL, IA32_MSR_MC3_STATUS, IA32_MSR_MC3_ADDR, IA32_MSR_MC3_MISC }, +}; + +/* + * The P6-family processors have a different layout for their banks. Note that + * MC4 comes *before* MC3 by design here (Intel's design that is, not ours). + */ +static const gcpu_mca_bank_t gcpu_mca_banks_p6[] = { +{ P6_MSR_MC0_CTL, P6_MSR_MC0_STATUS, P6_MSR_MC0_ADDR, P6_MSR_MC0_MISC }, +{ P6_MSR_MC1_CTL, P6_MSR_MC1_STATUS, P6_MSR_MC1_ADDR, P6_MSR_MC1_MISC }, +{ P6_MSR_MC2_CTL, P6_MSR_MC2_STATUS, P6_MSR_MC2_ADDR, P6_MSR_MC2_MISC }, +{ P6_MSR_MC4_CTL, P6_MSR_MC4_STATUS, P6_MSR_MC4_ADDR, P6_MSR_MC4_MISC }, +{ P6_MSR_MC3_CTL, P6_MSR_MC3_STATUS, P6_MSR_MC3_ADDR, P6_MSR_MC3_MISC }, +}; + +/* + * Initialize the Machine Check Architecture (MCA) for a generic x86 CPU. + * Refer to the IA-32 Intel Architecture Software Developer's Manual, + * Volume 3: System Programming Guide, Section 14.5 for more information. + */ +void +gcpu_mca_init(void *data) +{ + gcpu_data_t *gcpu = data; + gcpu_mca_t *mca = &gcpu->gcpu_mca; + cpu_t *cp = CPU; + + uint64_t cap; + uint_t nbanks; + int i; + + /* + * We're only prepared to handle processors that have an MCG_CAP + * register. P5, K6, and earlier processors, which have their own + * more primitive way of doing machine checks, are not supported. + */ + ASSERT(x86_feature & X86_MCA); + cap = rdmsr(IA32_MSR_MCG_CAP); + + if (!(cap & MCG_CAP_CTL_P)) + return; /* do nothing if IA32_MCG_CTL register is missing */ + + if (strcmp(cpuid_getvendorstr(cp), "GenuineIntel") == 0 && + cpuid_getfamily(cp) == 6) { + mca->gcpu_mca_banks = gcpu_mca_banks_p6; + mca->gcpu_mca_nbanks = sizeof (gcpu_mca_banks_p6) / + sizeof (gcpu_mca_bank_t); + } else { + mca->gcpu_mca_banks = gcpu_mca_banks_ia32; + mca->gcpu_mca_nbanks = sizeof (gcpu_mca_banks_ia32) / + sizeof (gcpu_mca_bank_t); + } + + mca->gcpu_mca_data = kmem_alloc( + mca->gcpu_mca_nbanks * sizeof (gcpu_mca_data_t), KM_SLEEP); + + /* + * Unlike AMD's approach of assigning one MCG_CTL bit to each machine + * check register bank, Intel doesn't describe the layout of MCG_CTL or + * promise that each bit corresponds to a bank. The generic guidance + * is simply to write all ones to MCG_CTL, enabling everything that is + * present (h/w ignores writes to the undefined bit positions). The + * code right now only handles the original four banks or the P6 banks, + * so we may enable more than we know how to read on a future CPU. + * This code can be enhanced to dynamically allocate bank state based + * upon MCG_CAP.Count if RAS ever becomes important on non-AMD CPUs. + */ + nbanks = cap & MCG_CAP_COUNT_MASK; + mca->gcpu_mca_nbanks = MIN(nbanks, mca->gcpu_mca_nbanks); + wrmsr(IA32_MSR_MCG_CTL, 0ULL); /* disable features while we configure */ + + for (i = 0; i < mca->gcpu_mca_nbanks; i++) { + const gcpu_mca_bank_t *bank = &mca->gcpu_mca_banks[i]; + wrmsr(bank->bank_ctl, -1ULL); + wrmsr(bank->bank_status, 0ULL); + } + + wrmsr(IA32_MSR_MCG_CTL, -1ULL); /* enable all machine-check features */ + setcr4(getcr4() | CR4_MCE); /* enable machine-check exceptions */ +} + +/* + * Initialize the Machine Check Architecture (MCA) for a generic x86 CPU. + * Refer to the IA-32 Intel Architecture Software Developer's Manual, + * Volume 3: System Programming Guide, Section 14.7 for more information. + */ +int +gcpu_mca_trap(void *data, struct regs *rp) +{ + gcpu_data_t *gcpu = data; + gcpu_mca_t *mca = &gcpu->gcpu_mca; + uint64_t gstatus = rdmsr(IA32_MSR_MCG_STATUS); + int i, fatal = !(gstatus & MCG_STATUS_RIPV); + + if (!(gstatus & MCG_STATUS_MCIP)) + return (0); /* spurious machine check trap */ + + /* + * Read out the bank status values, and the address and misc registers + * if they are valid. Update our fatal status based on each bank. + * Clear the MCG_STATUS register when we're done reading the h/w state. + */ + for (i = 0; i < mca->gcpu_mca_nbanks; i++) { + const gcpu_mca_bank_t *bank = &mca->gcpu_mca_banks[i]; + gcpu_mca_data_t *data = &mca->gcpu_mca_data[i]; + uint64_t bstatus = rdmsr(bank->bank_status); + + data->bank_status_data = bstatus; + data->bank_addr_data = 0; + data->bank_misc_data = 0; + + if (!(bstatus & MSR_MC_STATUS_VAL)) + continue; + + if (bstatus & MSR_MC_STATUS_ADDRV) + data->bank_addr_data = rdmsr(bank->bank_addr); + if (bstatus & MSR_MC_STATUS_MISCV) + data->bank_misc_data = rdmsr(bank->bank_misc); + + if (bstatus & (MSR_MC_STATUS_PCC | MSR_MC_STATUS_O)) + fatal = 1; /* context corrupt or overflow */ + + wrmsr(bank->bank_status, 0ULL); + } + + wrmsr(IA32_MSR_MCG_STATUS, 0); + + log_enter(); + + if (gstatus & MCG_STATUS_EIPV) { + cmn_err(CE_WARN, "Machine-Check Exception at 0x%lx in %s mode", + (ulong_t)rp->r_pc, USERMODE(rp->r_cs) ? "user" : "kernel"); + } else { + cmn_err(CE_WARN, "Machine-Check Exception in %s mode", + USERMODE(rp->r_cs) ? "user" : "kernel"); + } + + /* + * Now go back through our saved state and report it using cmn_err(). + * We don't bother attempting any kind of decoding here as the actual + * values are entirely specific to the actual processor in use. We + * could break out the generic bit-fields, but you're only here if + * we didn't care enough to implement FMA support for this processor. + */ + for (i = 0; i < mca->gcpu_mca_nbanks; i++) { + gcpu_mca_data_t *bank = &mca->gcpu_mca_data[i]; + uint64_t bstatus = bank->bank_status_data; + + if (!(bstatus & MSR_MC_STATUS_VAL)) + continue; + + switch (bstatus & (MSR_MC_STATUS_ADDRV | MSR_MC_STATUS_MISCV)) { + case MSR_MC_STATUS_ADDRV | MSR_MC_STATUS_MISCV: + cmn_err(CE_WARN, "%d STAT 0x%016llx ADDR 0x%016llx " + "MISC 0x%016llx", i, (u_longlong_t)bstatus, + (u_longlong_t)bank->bank_addr_data, + (u_longlong_t)bank->bank_misc_data); + break; + case MSR_MC_STATUS_ADDRV: + cmn_err(CE_WARN, "%d STAT 0x%016llx ADDR 0x%016llx", + i, (u_longlong_t)bstatus, + (u_longlong_t)bank->bank_addr_data); + break; + case MSR_MC_STATUS_MISCV: + cmn_err(CE_WARN, "%d STAT 0x%016llx MISC 0x%016llx", + i, (u_longlong_t)bstatus, + (u_longlong_t)bank->bank_misc_data); + break; + default: + cmn_err(CE_WARN, "%d STAT 0x%016llx", + i, (u_longlong_t)bstatus); + } + } + + log_exit(); + return (fatal); +} diff --git a/usr/src/uts/i86pc/cpu/scripts/Makefile b/usr/src/uts/i86pc/cpu/scripts/Makefile new file mode 100644 index 0000000000..f3509aa90e --- /dev/null +++ b/usr/src/uts/i86pc/cpu/scripts/Makefile @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PERLFILES= \ + ao_gendisp + +include ../../../Makefile.uts + +OWNER= root +GROUP= bin + +.KEEP_STATE: + +all install setup: $(PERLFILES) + +clean: + $(RM) $(PERLFILES) + +include ../../../Makefile.targ + +%: %.pl + $(RM) $@ + cat $< > $@ + chmod +x $@ diff --git a/usr/src/uts/i86pc/cpu/scripts/ao_gendisp.pl b/usr/src/uts/i86pc/cpu/scripts/ao_gendisp.pl new file mode 100644 index 0000000000..9d8b24e539 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/scripts/ao_gendisp.pl @@ -0,0 +1,370 @@ +#!/bin/perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +use strict; +use File::Basename; + +my $PROGNAME = basename($0); + +my ($funcunit, $error); +my @funcunits = (); + +my $state = "initial"; + +sub usage() { + print STDERR "Usage: $PROGNAME inputfile\n"; + exit(2); +} + +sub bail() { + print STDERR "$PROGNAME: ", join(" ", @_), "\n"; + exit(1); +} + +sub parsebail() { + print STDERR "$PROGNAME: $::infile: $.: ", join(" ", @_), "\n"; + exit(1); +} + +sub print_header() { + print "#include \"ao_mca_disp.h\"\n\n"; +} + +sub print_footer() { + print "const ao_error_disp_t *ao_error_disp[] = {\n"; + + foreach my $name (@funcunits) { + print "\t$name,\n"; + } + + print "};\n"; +} + +sub funcunit_begin() { + my $arrnm = "ao_error_disp_" . $_[0]; + print "static const ao_error_disp_t " . $arrnm . "[] = {\n"; + + @funcunits = (@funcunits, $arrnm); +} + +sub funcunit_end() { + print "\tNULL\n};\n\n"; +} + +sub error_begin() { + my ($ereport_name) = @_; + + $ereport_name =~ tr/[a-z]./[A-Z]_/; + my $flags_name = $ereport_name; + $flags_name =~ s/EREPORT_/EREPORT_PAYLOAD_FLAGS_/; + + print "\tFM_$ereport_name,\n\tFM_$flags_name,\n"; +} + +sub error_end() { + print "\t},\n\n"; +} + +sub print_bits() { + my $name = $_[0]; + my @bits = @_[1..$#_]; + + if (@bits == 0) { + print "\t0,"; + } elsif (@bits == 1) { + print "\t$bits[0],"; + } else { + print "\t( ", join(" | ", @bits), " ),"; + } + + print " /* $name */\n"; +} + +sub field_burst() { + my ($field, $valuesref, $name, $prefix) = @_; + + if ($field eq "-") { + return (); + } + + map { + if (!defined ${$valuesref}{$_}) { + &parsebail("unknown $name value `$_'"); + } + $_ = ${$valuesref}{$_}; + tr/[a-z]/[A-Z]/; + $prefix . "_" . $_; + } split(/\//, $field); +} + +sub bin2dec() { + my $bin = $_[0]; + my $dec = 0; + + foreach my $bit (split(//, $bin)) { + $dec = $dec * 2 + ($bit eq "1" ? 1 : 0); + } + + $dec; +} + +sub state_funcunit() { + my $val = $_[0]; + + if (defined $::funcunit) { + &funcunit_end(); + } + + $::funcunit = $val; + undef $::error; + &funcunit_begin($::funcunit); +} + +sub state_desc() { + my $desc = $_[0]; + + print "\t/* $desc */\n\t{\n"; +} + +sub state_error() { + $::error = $_[0]; + &error_begin($::error); +} + +sub state_mask_on() { + @::mask_on = map { tr/[a-z]/[A-Z]/; $_; } split(/,\s*/, $_[0]); +} + +sub state_mask_off() { + my @mask_off = map { tr/[a-z]/[A-Z]/; $_; } split(/,\s*/, $_[0]); + + &print_bits("mask", @::mask_on, @mask_off); + &print_bits("mask_res", @::mask_on); +} + +sub state_code() { + my ($ext, $type, $pp, $t, $r4, $ii, $ll, $tt) = split(/\s+/, $_[0]); + + my %tt_values = ( instr => 1, data => 1, gen => 1, '-' => 1 ); + my %ll_values = ( l0 => 1, l1 => 1, l2 => 1, lg => 1 ); + + my %r4_values = ( + gen => 'gen', + rd => 'rd', + wr => 'wr', + drd => 'drd', + dwr => 'dwr', + ird => 'ird', + pf => 'prefetch', + ev => 'evict', + snp => 'snoop', + '-' => '-'); + + my %pp_values = ( + src => 'src', + rsp => 'rsp', + obs => 'obs', + gen => 'gen', + '-' => '-' ); + + my %t_values = ( 0 => 1, 1 => 1, '-' => 1 ); + + my %ii_values = ( + mem => 'mem', + io => 'io', + gen => 'gen', + '-' => '-' ); + + if (!defined $tt_values{$tt}) { + &parsebail("unknown tt value `$tt'"); + } + + if (!defined $ll_values{$ll}) { + &parsebail("unknown ll value `$ll'"); + } + + my @r4 = &field_burst($r4, \%r4_values, "r4", "AO_MCA_R4_BIT"); + + my @pp = ($pp eq '-') ? () : + &field_burst($pp, \%pp_values, "pp", "AO_MCA_PP_BIT"); + + if (!defined $t_values{$t}) { + &parsebail("unknown t value `$t'"); + } + + my @ii = ($ii eq '-') ? () : + &field_burst($ii, \%ii_values, "ii", "AO_MCA_II_BIT"); + + map { + tr/[a-z]/[A-Z]/; + } ($ii, $ll, $tt); + + if ($type eq "bus") { + if ($pp eq "-" || $t eq "-" || $r4 eq "-" || $ii eq "-" || + $ll eq "-" || + $tt ne "-") { + &parsebail("invalid members for bus code type"); + } + + print "\tAMD_ERRCODE_MKBUS(" . + "0, " . # pp + "AMD_ERRCODE_T_" . ($t ? "TIMEOUT" : "NONE") . ", " . + "0, " . # r4 + "0, " . # ii + "AMD_ERRCODE_LL_$ll),\n"; + + } elsif ($type eq "mem") { + if ($r4 eq "-" || $tt eq "-" || $ll eq "-" || + $pp ne "-" || $t ne "-" || $ii ne "-") { + &parsebail("invalid members for mem code type"); + } + + print "\tAMD_ERRCODE_MKMEM(" . + "0, " . # r4 + "AMD_ERRCODE_TT_$tt, " . + "AMD_ERRCODE_LL_$ll),\n"; + + } elsif ($type eq "tlb") { + if ($tt eq "-" || $ll eq "-" || + $r4 ne "-" || $pp ne "-" || $t ne "-" || $ii ne "-") { + &parsebail("invalid members for tlb code type"); + } + + print "\tAMD_ERRCODE_MKTLB(" . + "AMD_ERRCODE_TT_$tt, " . + "AMD_ERRCODE_LL_$ll),\n"; + } else { + &parsebail("unknown code type `$type'"); + } + + print "\t" . &bin2dec($ext) . ", /* ext code $ext */\n"; + + &print_bits("pp_bits", @pp); + &print_bits("ii_bits", @ii); + &print_bits("r4_bits", @r4); +} + +sub state_panic() { + my $val = $_[0]; + + if ($val eq "") { + print "\t0, /* panic_when */\n"; + } else { + $val =~ tr/[a-z]/[A-Z]/; + print "\tAO_AED_PANIC_$val,\n"; + } +} + +sub state_flags() { + my @flags = split(/,\s*/, $_[0]); + + @flags = map { tr/[a-z]/[A-Z]/; "AO_AED_F_" . $_; } @flags; + + &print_bits("flags", @flags); +} + +my %stateparse = ( + funcunit => [ \&state_funcunit, "desc" ], + desc => [ \&state_desc, "error" ], + error => [ \&state_error, "mask on" ], + 'mask on' => [ \&state_mask_on, "mask off" ], + 'mask off' => [ \&state_mask_off, "code" ], + code => [ \&state_code, "panic" ], + panic => [ \&state_panic, "flags" ], + flags => [ \&state_flags, "initial" ] +); + +usage unless (@ARGV == 1); + +my $infile = $ARGV[0]; +open(INFILE, "<$infile") || &bail("failed to open $infile: $!"); + +&print_header(); + +while (<INFILE>) { + chop; + + /^#/ && next; + /^$/ && next; + + if (!/^\s*(\S[^=]*\S)\s*=\s*(\S.*)?$/) { + &parsebail("failed to parse"); + } + + my ($keyword, $val) = ($1, $2); + + if ($state eq "initial") { + if ($keyword eq "funcunit") { + $state = "funcunit"; + } elsif ($keyword eq "desc") { + $state = "desc"; + } else { + &parsebail("unexpected keyword $keyword between " . + "errors"); + } + + } elsif ($state eq "desc") { + if ($keyword eq "funcunit") { + $state = "funcunit"; + } + } + + if ($keyword ne $state) { + &parsebail("keyword `$keyword' invalid here; expected `$state'"); + } + + if (!defined $stateparse{$state}) { + &parsebail("attempt to transition to invalid state `$state'"); + } + + my ($handler, $next) = @{$stateparse{$state}}; + + &{$handler}($val); + + $state = $next; + + if ($state eq "initial") { + &error_end(); + } +} + +close(INFILE); + +if ($state ne "initial" && $state ne "desc") { + &bail("input file ended prematurely"); +} + +if (defined $::funcunit) { + &funcunit_end(); +} else { + &bail("no functional units defined"); +} + +&print_footer; diff --git a/usr/src/uts/i86pc/generic_cpu/Makefile b/usr/src/uts/i86pc/generic_cpu/Makefile new file mode 100644 index 0000000000..f23d9dd369 --- /dev/null +++ b/usr/src/uts/i86pc/generic_cpu/Makefile @@ -0,0 +1,82 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = cpu.generic +# +OBJECTS = $(CPU_GCPU_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(CPU_GCPU_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_CPU_DIR)/$(MODULE) + +# +# Include common rules. +# +include ../cpu/Makefile.cpu + +# +# Our lint library has a different name from that of the module we build. +# +LINT_MODULE = generic_cpu + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(LINT_MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include ../Makefile.targ diff --git a/usr/src/uts/i86pc/io/mc/mc-amd.conf b/usr/src/uts/i86pc/io/mc/mc-amd.conf new file mode 100644 index 0000000000..066b704525 --- /dev/null +++ b/usr/src/uts/i86pc/io/mc/mc-amd.conf @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +name="mc-amd" parent="pseudo"; + +ddi-forceattach=1; diff --git a/usr/src/uts/i86pc/io/mc/mcamd.h b/usr/src/uts/i86pc/io/mc/mcamd.h new file mode 100644 index 0000000000..330ecfa5b5 --- /dev/null +++ b/usr/src/uts/i86pc/io/mc/mcamd.h @@ -0,0 +1,192 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MCAMD_H +#define _MCAMD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/chip.h> +#include <sys/ksynch.h> +#include <sys/mc_amd.h> +#include <mcamd_api.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * PCI configuration space functions for the memory controller. Note that + * the function numbers here also serve as the mc_func indices in the mc_t. + */ +#define MC_FUNC_HTCONFIG 0 /* unused */ +#define MC_FUNC_HTCONFIG_BINDNM "pci1022,1100" /* unused */ +#define MC_FUNC_ADDRMAP 1 +#define MC_FUNC_ADDRMAP_BINDNM "pci1022,1101" +#define MC_FUNC_DRAMCTL 2 +#define MC_FUNC_DRAMCTL_BINDNM "pci1022,1102" + +/* + * The memory controller driver attaches to several device nodes, but publishes + * a single minor node. We need to ensure that the minor node can be + * consistently mapped back to a single (and the same) device node, so we need + * to pick one to be used. We'll use the DRAM Controller device node, as it'll + * be the last to be attached. + */ +#define MC_FUNC_DEVIMAP MC_FUNC_DRAMCTL + +#define MC_FUNC_NUM 3 + +/* + * The following define the offsets at which various MC registers are + * accessed in PCI config space. For defines describing the register + * structure see mc_amd.h. + */ + +/* + * BKDG 3.29 section 3.4 - MC DRAM base and limit addresses, hole offset. + */ +#define MC_AM_REG_NODE_NUM 8 /* Number of DRAM nodes */ +#define MC_AM_REG_DRAMBASE_0 0x40 /* Offset for DRAM Base 0 */ +#define MC_AM_REG_DRAMLIM_0 0x44 /* Offset for DRAM Limit 0 */ +#define MC_AM_REG_DRAM_INCR 8 /* incr between base/limit pairs */ +#define MC_AM_REG_HOLEADDR 0xf0 /* DRAM Hole Address Register */ + +/* + * BKDG 3.29 section 3.5 - DRAM contoller chip-select base, mask, + * DRAM bank address mapping, DRAM configuration. + */ +#define MC_DC_REG_CS_INCR 4 /* incr for CS base and mask */ +#define MC_DC_REG_CSBASE_0 0x40 /* 0x40 - 0x5c */ +#define MC_DC_REG_CSMASK_0 0x60 /* 0x60 - 0x7c */ +#define MC_DC_REG_BANKADDRMAP 0x80 +#define MC_DC_REG_DRAMCFGLO 0x90 +#define MC_DC_REG_DRAMCFGHI 0x94 + +typedef struct mc_func { + uint_t mcf_instance; + dev_info_t *mcf_devi; +} mc_func_t; + +typedef struct mc_dimm mc_dimm_t; +typedef struct mc_cs mc_cs_t; +typedef struct mc mc_t; + +typedef uint64_t mc_prop_t; /* see mcamd_get_numprop */ + +/* + * Node types for mch_type below. These are used in array indexing. + */ +#define MC_NT_MC 0 +#define MC_NT_CS 1 +#define MC_NT_DIMM 2 +#define MC_NT_NTYPES 3 + +typedef struct mc_hdr { + uint_t mch_type; + union { + mc_t *_mch_mc; + mc_cs_t *_mch_cs; + } _mch_ptr; +} mc_hdr_t; + +#define mch_mc _mch_ptr._mch_mc + +struct mc_dimm { + mc_hdr_t mcd_hdr; /* id, pointer to parent */ + mc_dimm_t *mcd_next; /* next dimm for this MC */ + mc_cs_t *mcd_cs[MC_CHIP_DIMMRANKMAX]; /* associated chip-selects */ + mc_prop_t mcd_num; /* dimm number */ +}; + +#define mcd_mc mcd_hdr.mch_mc + +struct mc_cs { + mc_hdr_t mccs_hdr; /* id, pointer to parent */ + mc_cs_t *mccs_next; /* Next chip-select of MC */ + mc_dimm_t *mccs_dimm[MC_CHIP_DIMMPERCS]; /* dimms for this cs */ + mc_prop_t mccs_num; /* Chip-select number */ + mc_prop_t mccs_base; /* DRAM CS Base */ + mc_prop_t mccs_mask; /* DRAM CS Mask */ + mc_prop_t mccs_size; /* Chip-select bank size */ + mc_prop_t mccs_dimmnums[MC_CHIP_DIMMPERCS]; +}; + +#define mccs_mc mccs_hdr.mch_mc + +typedef struct mc_props { + mc_dimm_t *mcp_dimmlist; /* List of all logical DIMMs, */ + mc_dimm_t *mcp_dimmlast; /* linked via mcd_mcnext */ + mc_prop_t mcp_num; /* Associated *chip* number */ + mc_prop_t mcp_rev; /* Chip revision (MC_REV_*) */ + mc_prop_t mcp_base; /* base address for mc's drams */ + mc_prop_t mcp_lim; /* limit address for mc's drams */ + mc_prop_t mcp_dramcfg; /* DRAM config hi, DRAM config lo */ + mc_prop_t mcp_dramhole; /* DRAM Hole Address Register */ + mc_prop_t mcp_ilen; /* interleave enable */ + mc_prop_t mcp_ilsel; /* interleave select */ + mc_prop_t mcp_csbankmap; /* chip-select bank mapping reg */ + mc_prop_t mcp_accwidth; /* dram access width (64 or 128) */ + mc_prop_t mcp_csbank_intlv; /* cs bank interleave factor */ + mc_prop_t mcp_disabled_cs; /* # banks with CSBE clear */ +} mc_props_t; + +struct mc { + mc_hdr_t mc_hdr; /* id */ + struct mc *mc_next; /* linear, doubly-linked list */ + const char *mc_revname; /* revision name string */ + uint_t mc_ref; /* reference (attach) count */ + mc_func_t mc_funcs[MC_FUNC_NUM]; /* Instance, devinfo, ... */ + chip_t *mc_chip; /* Associated chip */ + mc_cs_t *mc_cslist; /* All active chip-selects */ + mc_cs_t *mc_cslast; /* End of chip-select list */ + mc_props_t mc_props; /* Properties */ + nvlist_t *mc_nvl; /* nvlist for export */ + char *mc_snapshot; /* packed nvlist for libmc */ + size_t mc_snapshotsz; /* packed nvlist buffer size */ + uint_t mc_snapshotgen; /* snapshot generation number */ +}; + +typedef struct mcamd_hdl { + int mcamd_errno; + int mcamd_debug; +} mcamd_hdl_t; + +extern mc_t *mc_list; +extern krwlock_t mc_lock; + +extern void mcamd_mkhdl(mcamd_hdl_t *); +extern void mcamd_mc_register(struct cpu *); + +#ifdef __cplusplus +} +#endif + +#endif /* _MCAMD_H */ diff --git a/usr/src/uts/i86pc/io/mc/mcamd_drv.c b/usr/src/uts/i86pc/io/mc/mcamd_drv.c new file mode 100644 index 0000000000..0b0c04a1d4 --- /dev/null +++ b/usr/src/uts/i86pc/io/mc/mcamd_drv.c @@ -0,0 +1,999 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/conf.h> +#include <sys/ddi.h> +#include <sys/ddifm.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/stat.h> +#include <sys/modctl.h> +#include <sys/types.h> +#include <sys/mc.h> +#include <sys/cpuvar.h> +#include <sys/cmn_err.h> +#include <sys/kmem.h> +#include <sys/cred.h> +#include <sys/ksynch.h> +#include <sys/rwlock.h> +#include <sys/chip.h> +#include <sys/open.h> +#include <sys/policy.h> +#include <sys/machsystm.h> +#include <sys/x86_archext.h> +#include <sys/cpu_module.h> +#include <sys/mc_amd.h> + +#include <mcamd.h> +#include <mcamd_api.h> + +int mc_quadranksupport = 0; /* set to 1 for a MB with quad rank support */ + +mc_t *mc_list; +krwlock_t mc_lock; +int mc_hold_attached = 1; + +static void +mc_snapshot_destroy(mc_t *mc) +{ + ASSERT(RW_LOCK_HELD(&mc_lock)); + + if (mc->mc_snapshot == NULL) + return; + + kmem_free(mc->mc_snapshot, mc->mc_snapshotsz); + mc->mc_snapshot = NULL; + mc->mc_snapshotgen++; +} + +static int +mc_snapshot_update(mc_t *mc) +{ + ASSERT(RW_LOCK_HELD(&mc_lock)); + + if (mc->mc_snapshot != NULL) + return (0); + + if (nvlist_pack(mc->mc_nvl, &mc->mc_snapshot, &mc->mc_snapshotsz, + NV_ENCODE_XDR, KM_SLEEP) != 0) + return (-1); + + return (0); +} + +static mc_t * +mc_lookup_func(dev_info_t *dip, int instance, mc_func_t **funcp) +{ + mc_t *mc; + int i; + + ASSERT(RW_LOCK_HELD(&mc_lock)); + + for (mc = mc_list; mc != NULL; mc = mc->mc_next) { + for (i = 0; i < MC_FUNC_NUM; i++) { + mc_func_t *func = &mc->mc_funcs[i]; + if ((dip != NULL && func->mcf_devi == dip) || + (dip == NULL && func->mcf_instance == instance)) { + if (funcp != NULL) + *funcp = func; + return (mc); + } + } + } + + return (NULL); +} + +static mc_t * +mc_lookup_by_devi(dev_info_t *dip, mc_func_t **funcp) +{ + return (mc_lookup_func(dip, 0, funcp)); +} + +static mc_t * +mc_lookup_by_instance(int instance, mc_func_t **funcp) +{ + return (mc_lookup_func(NULL, instance, funcp)); +} + +static mc_t * +mc_lookup_by_chipid(int chipid) +{ + mc_t *mc; + + ASSERT(RW_LOCK_HELD(&mc_lock)); + + for (mc = mc_list; mc != NULL; mc = mc->mc_next) { + if (mc->mc_chip->chip_id == chipid) + return (mc); + } + + return (NULL); +} + +typedef struct mc_rev_map { + uint_t rm_family; + uint_t rm_modello; + uint_t rm_modelhi; + uint_t rm_rev; + const char *rm_name; +} mc_rev_map_t; + +static const mc_rev_map_t mc_rev_map[] = { + { 0xf, 0x00, 0x0f, MC_REV_PRE_D, "B/C/CG" }, + { 0xf, 0x10, 0x1f, MC_REV_D_E, "D" }, + { 0xf, 0x20, 0x3f, MC_REV_D_E, "E" }, + { 0xf, 0x40, 0x5f, MC_REV_F, "F" }, + { 0, 0, 0, MC_REV_UNKNOWN, NULL } +}; + +static const mc_rev_map_t * +mc_revision(chip_t *chp) +{ + int rmn = sizeof (mc_rev_map) / sizeof (mc_rev_map[0]); + const mc_rev_map_t *rm; + uint8_t family, model; + + if (chp == NULL) + return (&mc_rev_map[rmn - 1]); + + /* + * For the moment, we assume that both cores in multi-core chips will + * be of the same revision, so we'll confine our revision check to + * the first CPU pointed to by this chip. + */ + family = cpuid_getfamily(chp->chip_cpus); + model = cpuid_getmodel(chp->chip_cpus); + + for (rm = mc_rev_map; rm->rm_rev != MC_REV_UNKNOWN; rm++) { + if (family == rm->rm_family && model >= rm->rm_modello && + model <= rm->rm_modelhi) + break; + } + + return (rm); +} + +static void +mc_prop_read_pair(ddi_acc_handle_t cfghdl, uint32_t *r1, off_t r1addr, + uint32_t *r2, off_t r2addr, int n, off_t incr) +{ + int i; + + for (i = 0; i < n; i++, r1addr += incr, r2addr += incr) { + r1[i] = pci_config_get32(cfghdl, r1addr); + r2[i] = pci_config_get32(cfghdl, r2addr); + } +} + +static void +mc_nvl_add_prop(nvlist_t *nvl, void *node, uint_t code) +{ + int valfound; + uint64_t value; + const char *name = mcamd_get_propname(code); + + valfound = mcamd_get_numprop(NULL, (mcamd_node_t *)node, code, &value); + + ASSERT(name != NULL && valfound); + if (name != NULL && valfound) + (void) nvlist_add_uint64(nvl, name, value); +} + +static nvlist_t * +mc_nvl_create(mc_t *mc) +{ + mc_cs_t *mccs = mc->mc_cslist; + nvlist_t *cslist[MC_CHIP_NCS], *dimmlist[MC_CHIP_NDIMM]; + nvlist_t *mcnvl; + mc_dimm_t *mcd; + int nelem, i; + + (void) nvlist_alloc(&mcnvl, NV_UNIQUE_NAME, KM_SLEEP); + (void) nvlist_add_string(mcnvl, "revname", mc->mc_revname); + + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_NUM); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_REV); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BASE_ADDR); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_LIM_ADDR); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAM_CONFIG); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAM_HOLE); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAM_ILEN); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAM_ILSEL); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSBANKMAP); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ACCESS_WIDTH); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSBANK_INTLV); + mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DISABLED_CS); + + for (nelem = 0; mccs != NULL; mccs = mccs->mccs_next, nelem++) { + nvlist_t **csp = &cslist[nelem]; + + (void) nvlist_alloc(csp, NV_UNIQUE_NAME, KM_SLEEP); + mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_NUM); + mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_BASE_ADDR); + mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_MASK); + mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_SIZE); + + if (mccs->mccs_dimmnums[0] != -1) + mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_LODIMM); + if (mccs->mccs_dimmnums[1] != -1) + mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_UPDIMM); + } + + (void) nvlist_add_nvlist_array(mcnvl, "cslist", cslist, nelem); + for (i = 0; i < nelem; i++) + nvlist_free(cslist[i]); + + for (nelem = 0, mcd = mc->mc_props.mcp_dimmlist; mcd != NULL; + mcd = mcd->mcd_next, nelem++) { + nvlist_t **dimmp = &dimmlist[nelem]; + int ncs = 0; + uint64_t csnums[MC_CHIP_DIMMRANKMAX]; + + (void) nvlist_alloc(dimmp, NV_UNIQUE_NAME, KM_SLEEP); + + mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_NUM); + + for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) { + if (mcd->mcd_cs[i] != NULL) + csnums[ncs++] = mcd->mcd_cs[i]->mccs_num; + } + + (void) nvlist_add_uint64_array(*dimmp, "csnums", csnums, ncs); + } + + (void) nvlist_add_nvlist_array(mcnvl, "dimmlist", dimmlist, nelem); + for (i = 0; i < nelem; i++) + nvlist_free(dimmlist[i]); + + return (mcnvl); +} + +static void +mc_dimm_csadd(mc_dimm_t *mcd, mc_cs_t *mccs) +{ + int i; + + for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) { + if (mcd->mcd_cs[i] == NULL) { + mcd->mcd_cs[i] = mccs; + break; + } + } + ASSERT(i != MC_CHIP_DIMMRANKMAX); +} + +static mc_dimm_t * +mc_dimm_create(mc_t *mc, mc_cs_t *mccs, uint_t num) +{ + mc_dimm_t *mcd = kmem_zalloc(sizeof (mc_dimm_t), KM_SLEEP); + + mcd->mcd_hdr.mch_type = MC_NT_DIMM; + mcd->mcd_mc = mc; + mcd->mcd_num = num; + mc_dimm_csadd(mcd, mccs); + + return (mcd); +} + +/* + * A chip-select is associated with up to 2 dimms, and a single dimm may + * have up to 4 associated chip-selects (in the presence of quad-rank support + * on the motherboard). How we number our dimms is determined by the MC + * config. This function may be called by multiple chip-selects for the + * same dimm(s). + */ +static void +mc_cs_dimmlist_create(mc_t *mc, mc_cs_t *mccs, uint_t *dimm_nums, int ndimm) +{ + mc_dimm_t *mcd; + mc_props_t *mcp = &mc->mc_props; + int i; + int nfound = 0; + + /* + * Has some other chip-select already created this dimm or dimms? + */ + for (mcd = mcp->mcp_dimmlist; mcd != NULL; mcd = mcd->mcd_next) { + for (i = 0; i < ndimm; i++) { + if (mcd->mcd_num == dimm_nums[i]) { + mccs->mccs_dimm[i] = mcd; + mccs->mccs_dimmnums[i] = mcd->mcd_num; + mc_dimm_csadd(mcd, mccs); + nfound++; + } + } + } + ASSERT(nfound == 0 || nfound == ndimm); + if (nfound == ndimm) + return; + + for (i = 0; i < ndimm; i++) { + mcd = mccs->mccs_dimm[i] = + mc_dimm_create(mc, mccs, dimm_nums[i]); + + mccs->mccs_dimmnums[i] = mcd->mcd_num; + + if (mcp->mcp_dimmlist == NULL) + mcp->mcp_dimmlist = mcd; + else + mcp->mcp_dimmlast->mcd_next = mcd; + mcp->mcp_dimmlast = mcd; + } + +} + +/* + * A placeholder for a future implementation that works this out from + * smbios or SPD information. For now we will return a value that + * can be tuned in /etc/system, and the default will cover current Sun systems. + */ +/*ARGSUSED*/ +static int +mc_config_quadranksupport(mc_t *mc) +{ + return (mc_quadranksupport != 0); +} + +/* + * Create the DIMM structure for this MC. There are a number of unkowns, + * such as the number of DIMM slots for this MC, the number of chip-select + * ranks supported for each DIMM, how the slots are labelled etc. + * + * SMBIOS information can help with some of this (if the bios implementation is + * complete and accurate, which is often not the case): + * + * . A record is required for each SMB_TYPE_MEMDEVICE slot, whether populated + * or not. The record should reference the associated SMB_TYPE_MEMARRAY, + * so we can figure out the number of slots for each MC. In practice some + * smbios implementations attribute all slots (from multiple chips) to + * a single memory array. + * + * . SMB_TYPE_MEMDEVICEMAP records indicate how a particular SMB_TYPE_MEMDEVICE + * has been mapped. Some smbios implementation produce rubbish here, or get + * confused when cs bank interleaving is enabled or disabled, but we can + * perform some validation of the information before using it. The record + * information is not well suited to handling cs bank interleaving since + * it really only provides for a device to have a few contiguos mappings + * and with cs interleave we have lots of little chunks interleaved across + * the devices. If we assume that the bios has followed the BKDG algorithm + * for setting up cs interleaving (which involves assinging contiguous + * and adjacent ranges to the chip selects and then swapping some + * base and mask hi and lo bits) then we can attempt to interpret the + * DEVICEMAP records as being the addresses prior to swapping address/mask + * bits to establish the interleave - that seems to cover at least some + * smbios implementations. Even if that assumption appears good it is + * also not clear which MEMDEVICE records correspond to LODIMMs and which + * to UPDIMMs in a DIMM pair (128 bit MC mode) - we have to interpret the + * Device Locator and Bank Locator labels. + * + * We also do not know how many chip-select banks reside on individual + * DIMMs. For instance we cannot distinguish a system that supports 8 + * DIMMs slots per chip (one CS line each, thereby supporting only single-rank + * DIMMs) vs a system that has just 4 slots per chip and which routes + * 2 CS lines to each pair (thereby supporting dual rank DIMMs). In each + * we would discover 8 active chip-selects. + * + * So the task of establishing the real DIMM configuration is complex, likely + * requiring some combination of good SMBIOS data and perhaps our own access + * to SPD information. Instead we opt for a canonical numbering scheme, + * derived from the 'AMD Athlon (TM) 64 FX and AMD Opteron (TM) Processors + * Motherboard Design Guide' (AMD publication #25180). + */ +static void +mc_dimmlist_create(mc_t *mc) +{ + int mcmode; + mc_cs_t *mccs; + int quadrank = mc_config_quadranksupport(mc); + uint_t dimm_nums[MC_CHIP_DIMMPERCS]; + int ldimmno; /* logical DIMM pair number, 0 .. 3 */ + + mcmode = mc->mc_props.mcp_dramcfg & MC_DC_DCFG_128 ? 128 : 64; + + for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) { + if (quadrank) { + /* + * Quad-rank support. We assume that any of cs# + * 4/5/6/6 that we have discovered active are routed + * for quad rank support as described in the MB + * design guide: + * DIMM0: CS# 0, 1, 4 and 5 + * DIMM1: CS# 2, 3, 6 and 7 + */ + ldimmno = (mccs->mccs_num % 4) /2; + } else { + /* + * DIMM0: CS# 0 and 1 + * DIMM1: CS# 2 and 3 + * DIMM2: CS# 4 and 5 + * DIMM3: CS# 6 and 7 + */ + ldimmno = mccs->mccs_num / 2; + } + + if (mcmode == 128) { + /* 128-bit data width mode - dimms present in pairs */ + dimm_nums[0] = ldimmno * 2; /* LODIMM */ + dimm_nums[1] = ldimmno * 2 + 1; /* UPDIMM */ + } else { + /* 64-bit data width mode - only even numbered dimms */ + dimm_nums[0] = ldimmno * 2; /* LODIMM */ + } + mc_cs_dimmlist_create(mc, mccs, dimm_nums, + mcmode == 128 ? 2 : 1); + } +} + +static mc_cs_t * +mc_cs_create(mc_t *mc, uint_t num, uint64_t base, uint64_t mask, size_t sz) +{ + mc_cs_t *mccs = kmem_zalloc(sizeof (mc_cs_t), KM_SLEEP); + + mccs->mccs_hdr.mch_type = MC_NT_CS; + mccs->mccs_mc = mc; + mccs->mccs_num = num; + mccs->mccs_base = base; + mccs->mccs_mask = mask; + mccs->mccs_size = sz; + + return (mccs); +} + +/* + * Function 1 Configuration - Address Map (see BKDG 3.4.4 DRAM Address Map) + * + * Read the Function 1 Address Map for each potential DRAM node. The Base + * Address for a node gives the starting system address mapped at that node, + * and the limit gives the last valid address mapped at that node. Regions for + * different nodes should not overlap, unless node-interleaving is enabled. + * The base register also indicates the node-interleaving settings (IntlvEn). + * The limit register includes IntlvSel which determines which 4K blocks will + * be routed to this node and the destination node ID for addresses that fall + * within the [base, limit] range - this must match the pair number. + */ +static void +mc_mkprops_addrmap(ddi_acc_handle_t cfghdl, mc_t *mc) +{ + uint32_t base[MC_AM_REG_NODE_NUM], lim[MC_AM_REG_NODE_NUM]; + mc_props_t *mcp = &mc->mc_props; + int i; + + mc_prop_read_pair(cfghdl, base, MC_AM_REG_DRAMBASE_0, lim, + MC_AM_REG_DRAMLIM_0, MC_AM_REG_NODE_NUM, MC_AM_REG_DRAM_INCR); + + for (i = 0; i < MC_AM_REG_NODE_NUM; i++) { + /* + * Don't create properties for empty nodes. + */ + if ((lim[i] & MC_AM_DL_DRAMLIM_MASK) == 0) + continue; + + /* + * Don't create properties for DIMM ranges that aren't local + * to this node. + */ + if ((lim[i] & MC_AM_DL_DSTNODE_MASK) != mc->mc_chip->chip_id) + continue; + + mcp->mcp_base = MC_AM_DB_DRAMBASE(base[i]); + mcp->mcp_lim = MC_AM_DL_DRAMLIM(lim[i]); + mcp->mcp_ilen = (base[i] & MC_AM_DB_INTLVEN_MASK) >> + MC_AM_DB_INTLVEN_SHIFT; + mcp->mcp_ilsel = (lim[i] & MC_AM_DL_INTLVSEL_MASK) >> + MC_AM_DL_INTLVSEL_SHIFT; + } + + /* + * The Function 1 DRAM Hole Address Register tells us which node(s) + * own the DRAM space that is hoisted above 4GB, together with the + * hole base and offset for this node. + */ + mcp->mcp_dramhole = pci_config_get32(cfghdl, MC_AM_REG_HOLEADDR); +} + +/* + * Function 2 configuration - DRAM Controller + */ +static void +mc_mkprops_dramctl(ddi_acc_handle_t cfghdl, mc_t *mc) +{ + uint32_t base[MC_CHIP_NCS], mask[MC_CHIP_NCS]; + uint64_t dramcfg; + mc_props_t *mcp = &mc->mc_props; + int wide = 0; /* 128-bit access mode? */ + int i; + mcamd_hdl_t hdl; + + mcamd_mkhdl(&hdl); /* to call into common code */ + + /* + * Read Function 2 DRAM Configuration High and Low registers and + * weld them together into a 64-bit value. The High component + * is mostly concerned with memory clocks etc and we'll not have + * any use for that. The Low component tells us if ECC is enabled, + * if we're in 64- or 128-bit MC mode, how the upper chip-selects + * are mapped, which chip-select pairs are using x4 parts, etc. + */ + dramcfg = pci_config_get32(cfghdl, MC_DC_REG_DRAMCFGLO) | + ((uint64_t)pci_config_get32(cfghdl, MC_DC_REG_DRAMCFGHI) << 32); + wide = dramcfg & MC_DC_DCFG_128; + + mcp->mcp_dramcfg = dramcfg; + mcp->mcp_accwidth = wide ? 128 : 64; + + /* + * Read Function 2 DRAM Bank Address Mapping. This tells us + * whether bank swizzle mode is enabled, and also encodes + * the type of DIMM module in use for each chip-select pair. + */ + mcp->mcp_csbankmap = pci_config_get32(cfghdl, MC_DC_REG_BANKADDRMAP); + + /* + * Read Function 2 Configuration Registers for DRAM CS Base 0 thru 7 + * and DRAM CS Mask 0 thru 7. The Base registers give us the + * BaseAddrHi and BaseAddrLo from which the base can be constructed, + * and whether this chip-select bank is enabled (CSBE). The + * Mask registers give us AddrMaskHi and AddrMaskLo from which + * a full mask can be constructed. + */ + mc_prop_read_pair(cfghdl, base, MC_DC_REG_CSBASE_0, mask, + MC_DC_REG_CSMASK_0, MC_CHIP_NCS, MC_DC_REG_CS_INCR); + + /* + * Create a cs node for each enabled chip-select + */ + for (i = 0; i < MC_CHIP_NCS; i++) { + mc_cs_t *mccs; + uint64_t csmask; + size_t sz; + + if (!(base[i] & MC_DC_CSB_CSBE)) { + mcp->mcp_disabled_cs++; + continue; + } + + if (mcamd_cs_size(&hdl, (mcamd_node_t *)mc, i, &sz) < 0) + continue; + + csmask = MC_DC_CSM_CSMASK(mask[i]); + mccs = mc_cs_create(mc, i, MC_DC_CSB_CSBASE(base[i]), csmask, + sz); + + if (mc->mc_cslist == NULL) + mc->mc_cslist = mccs; + else + mc->mc_cslast->mccs_next = mccs; + mc->mc_cslast = mccs; + + /* + * Check for cs bank interleaving - some bits clear in the + * lower mask. All banks must/will have the same lomask bits + * if cs interleaving is active. + */ + if (!mcp->mcp_csbank_intlv) { + int bitno, ibits = 0; + for (bitno = MC_DC_CSM_MASKLO_LOBIT; + bitno <= MC_DC_CSM_MASKLO_HIBIT; bitno++) { + if (!(csmask & (1 << bitno))) + ibits++; + } + if (ibits > 0) + mcp->mcp_csbank_intlv = 1 << ibits; + } + } + + /* + * Now that we have discovered all active chip-selects we attempt + * to divine the associated DIMM configuration. + */ + mc_dimmlist_create(mc); +} + +typedef struct mc_bind_map { + const char *bm_bindnm; /* attachment binding name */ + uint_t bm_func; /* PCI config space function number for bind */ + const char *bm_model; /* value for device node model property */ + void (*bm_mkprops)(ddi_acc_handle_t, mc_t *); +} mc_bind_map_t; + +static const mc_bind_map_t mc_bind_map[] = { + { MC_FUNC_HTCONFIG_BINDNM, MC_FUNC_HTCONFIG, + "AMD Memory Controller (HT Configuration)", NULL }, + { MC_FUNC_ADDRMAP_BINDNM, MC_FUNC_ADDRMAP, + "AMD Memory Controller (Address Map)", mc_mkprops_addrmap }, + { MC_FUNC_DRAMCTL_BINDNM, MC_FUNC_DRAMCTL, + "AMD Memory Controller (DRAM Controller & HT Trace)", + mc_mkprops_dramctl }, + NULL +}; + +/*ARGSUSED*/ +static int +mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) +{ + if (otyp != OTYP_CHR) + return (EINVAL); + + rw_enter(&mc_lock, RW_READER); + if (mc_lookup_by_chipid(getminor(*devp)) == NULL) { + rw_exit(&mc_lock); + return (EINVAL); + } + rw_exit(&mc_lock); + + return (0); +} + +/*ARGSUSED*/ +static int +mc_close(dev_t dev, int flag, int otyp, cred_t *credp) +{ + return (0); +} + +/*ARGSUSED*/ +static int +mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) +{ + int rc = 0; + mc_t *mc; + + if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT) + return (EINVAL); + + rw_enter(&mc_lock, RW_READER); + + if ((mc = mc_lookup_by_chipid(getminor(dev))) == NULL) { + rw_exit(&mc_lock); + return (EINVAL); + } + + if (mc_snapshot_update(mc) < 0) { + rw_exit(&mc_lock); + return (EIO); + } + + switch (cmd) { + case MC_IOC_SNAPSHOT_INFO: { + mc_snapshot_info_t mcs; + + mcs.mcs_size = mc->mc_snapshotsz; + mcs.mcs_gen = mc->mc_snapshotgen; + + if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t), + mode) < 0) + rc = EFAULT; + break; + } + + case MC_IOC_SNAPSHOT: + if (ddi_copyout(mc->mc_snapshot, (void *)arg, mc->mc_snapshotsz, + mode) < 0) + rc = EFAULT; + break; + } + + rw_exit(&mc_lock); + + return (rc); +} + +static struct cb_ops mc_cb_ops = { + mc_open, + mc_close, + nodev, /* not a block driver */ + nodev, /* no print routine */ + nodev, /* no dump routine */ + nodev, /* no read routine */ + nodev, /* no write routine */ + mc_ioctl, + nodev, /* no devmap routine */ + nodev, /* no mmap routine */ + nodev, /* no segmap routine */ + nochpoll, /* no chpoll routine */ + ddi_prop_op, + 0, /* not a STREAMS driver */ + D_NEW | D_MP, /* safe for multi-thread/multi-processor */ +}; + +/*ARGSUSED*/ +static int +mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) +{ + int rc = DDI_SUCCESS; + mc_t *mc; + + if (infocmd != DDI_INFO_DEVT2DEVINFO && + infocmd != DDI_INFO_DEVT2INSTANCE) { + *result = NULL; + return (DDI_FAILURE); + } + + rw_enter(&mc_lock, RW_READER); + + if ((mc = mc_lookup_by_chipid(getminor((dev_t)arg))) == NULL || + mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_devi == NULL) { + rc = DDI_FAILURE; + } else if (infocmd == DDI_INFO_DEVT2DEVINFO) { + *result = mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_devi; + } else { + *result = (void *)(uintptr_t) + mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_instance; + } + + rw_exit(&mc_lock); + + return (rc); +} + +/*ARGSUSED2*/ +static int +mc_fm_handle(dev_info_t *dip, ddi_fm_error_t *fmerr, const void *arg) +{ + pci_ereport_post(dip, fmerr, NULL); + return (DDI_FM_NONFATAL); +} + +static void +mc_fm_init(dev_info_t *dip) +{ + int fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE; + ddi_fm_init(dip, &fmcap, NULL); + pci_ereport_setup(dip); + ddi_fm_handler_register(dip, mc_fm_handle, NULL); +} + +static void +mc_fm_fini(dev_info_t *dip) +{ + pci_ereport_teardown(dip); + ddi_fm_fini(dip); +} + +static mc_t * +mc_create(chipid_t chipid) +{ + chip_t *chp = chip_lookup(chipid); + const mc_rev_map_t *rmp = mc_revision(chp); + mc_t *mc; + + ASSERT(RW_WRITE_HELD(&mc_lock)); + + if (chp == NULL || rmp->rm_rev == MC_REV_UNKNOWN) + return (NULL); + + mc = kmem_zalloc(sizeof (mc_t), KM_SLEEP); + mc->mc_hdr.mch_type = MC_NT_MC; + mc->mc_chip = chp; + mc->mc_props.mcp_rev = rmp->rm_rev; + mc->mc_revname = rmp->rm_name; + mc->mc_props.mcp_num = mc->mc_chip->chip_id; + + mc->mc_next = mc_list; + mc_list = mc; + + return (mc); +} + +static int +mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + ddi_acc_handle_t hdl; + const mc_bind_map_t *bm; + const char *bindnm; + char *unitstr = NULL; + long unitaddr; + int chipid, func, rc; + cpu_t *cpu; + mc_t *mc; + + if (cmd != DDI_ATTACH) + return (DDI_FAILURE); + + bindnm = ddi_binding_name(dip); + for (bm = mc_bind_map; bm->bm_bindnm != NULL; bm++) { + if (strcmp(bindnm, bm->bm_bindnm) == 0) { + func = bm->bm_func; + break; + } + } + + if (bm->bm_bindnm == NULL) + return (DDI_FAILURE); + + /* + * We need the device number, which corresponds to the processor node + * number plus 24. The node number can then be used to associate this + * memory controller device with a given processor chip. + */ + (void) ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "unit-address", &unitstr); + rc = ddi_strtol(unitstr, NULL, 16, &unitaddr); + ASSERT(rc == 0 && unitaddr >= MC_AMD_DEV_OFFSET); + if (rc != 0 || unitaddr < MC_AMD_DEV_OFFSET) { + cmn_err(CE_WARN, "failed to parse unit address %s for %s\n", + unitstr, bindnm); + return (DDI_FAILURE); + } + + chipid = unitaddr - MC_AMD_DEV_OFFSET; + + rw_enter(&mc_lock, RW_WRITER); + + for (mc = mc_list; mc != NULL; mc = mc->mc_next) { + if (mc->mc_chip->chip_id == chipid) + break; + } + + /* Integrate this memory controller device into existing set */ + if (mc == NULL) { + mc = mc_create(chipid); + + if (mc == NULL) { + /* + * We don't complain here because this is a legitimate + * path for MP systems. On those machines, we'll attach + * before all CPUs have been initialized, and thus the + * chip verification in mc_create will fail. We'll be + * reattached later for those CPUs. + */ + rw_exit(&mc_lock); + return (DDI_FAILURE); + } + } else { + mc_snapshot_destroy(mc); + } + + /* Beyond this point, we're committed to creating this node */ + + mc_fm_init(dip); + + ASSERT(mc->mc_funcs[func].mcf_devi == NULL); + mc->mc_funcs[func].mcf_devi = dip; + mc->mc_funcs[func].mcf_instance = ddi_get_instance(dip); + + mc->mc_ref++; + + rw_downgrade(&mc_lock); + + /* + * Add the common properties to this node, and then add any properties + * that are specific to this node based upon its configuration space. + */ + (void) ddi_prop_update_string(DDI_DEV_T_NONE, + dip, "model", (char *)bm->bm_model); + + (void) ddi_prop_update_int(DDI_DEV_T_NONE, + dip, "chip-id", mc->mc_chip->chip_id); + + if (bm->bm_mkprops != NULL && + pci_config_setup(dip, &hdl) == DDI_SUCCESS) { + bm->bm_mkprops(hdl, mc); + pci_config_teardown(&hdl); + } + + /* + * If this is the last node to be attached for this memory controller, + * so create the minor node and set up the properties. + */ + if (func == MC_FUNC_DEVIMAP) { + mc_props_t *mcp = &mc->mc_props; + + if (ddi_create_minor_node(dip, "mc-amd", S_IFCHR, + mc->mc_chip->chip_id, "ddi_mem_ctrl", 0) != DDI_SUCCESS) { + cmn_err(CE_WARN, "failed to create minor node for chip " + "%u memory controller\n", mc->mc_chip->chip_id); + } + + /* + * Register the memory controller for every CPU of this chip. + * Then attempt to enable h/w memory scrubbers for this node. + * If we are successful, disable the software memory scrubber. + */ + mutex_enter(&cpu_lock); + + cpu = mc->mc_chip->chip_cpus; + rc = cmi_scrubber_enable(cpu, mcp->mcp_base, mcp->mcp_ilen); + + do { + mcamd_mc_register(cpu); + cpu = cpu->cpu_next_chip; + } while (cpu != mc->mc_chip->chip_cpus); + + mutex_exit(&cpu_lock); + + if (rc) + memscrub_disable(); + } + + nvlist_free(mc->mc_nvl); + mc->mc_nvl = mc_nvl_create(mc); + + rw_exit(&mc_lock); + return (DDI_SUCCESS); +} + +/*ARGSUSED*/ +static int +mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + return (DDI_FAILURE); +} + +static struct dev_ops mc_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + mc_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + mc_attach, /* devo_attach */ + mc_detach, /* devo_detach */ + nodev, /* devo_reset */ + &mc_cb_ops, /* devo_cb_ops */ + NULL, /* devo_bus_ops */ + NULL /* devo_power */ +}; + +static struct modldrv modldrv = { + &mod_driverops, + "Memory Controller for AMD processors", + &mc_ops +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modldrv, + NULL +}; + +int +_init(void) +{ + rw_init(&mc_lock, NULL, RW_DRIVER, NULL); + return (mod_install(&modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +int +_fini(void) +{ + int rc; + + if ((rc = mod_remove(&modlinkage)) != 0) + return (rc); + + rw_destroy(&mc_lock); + return (0); +} diff --git a/usr/src/uts/i86pc/io/mc/mcamd_off.in b/usr/src/uts/i86pc/io/mc/mcamd_off.in new file mode 100644 index 0000000000..8e6a8dbc5e --- /dev/null +++ b/usr/src/uts/i86pc/io/mc/mcamd_off.in @@ -0,0 +1,55 @@ +\ +\ CDDL HEADER START +\ +\ The contents of this file are subject to the terms of the +\ Common Development and Distribution License, Version 1.0 only +\ (the "License"). You may not use this file except in compliance +\ with the License. +\ +\ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +\ or http://www.opensolaris.org/os/licensing. +\ See the License for the specific language governing permissions +\ and limitations under the License. +\ +\ When distributing Covered Code, include this CDDL HEADER in each +\ file and include the License file at usr/src/OPENSOLARIS.LICENSE. +\ If applicable, add the following below this CDDL HEADER, with the +\ fields enclosed by brackets "[]" replaced with your own identifying +\ information: Portions Copyright [yyyy] [name of copyright owner] +\ +\ CDDL HEADER END +\ + +#pragma ident "%Z%%M% %I% %E% SMI" + +\ +\ Copyright 2006 Sun Microsystems, Inc. All rights reserved. +\ Use is subject to license terms. +\ + +#include <mcamd.h> + +mc_t + mc_props.mcp_num MCAMD_MC_OFF_NUM + mc_props.mcp_rev MCAMD_MC_OFF_REV + mc_props.mcp_base MCAMD_MC_OFF_BASE_ADDR + mc_props.mcp_lim MCAMD_MC_OFF_LIM_ADDR + mc_props.mcp_dramcfg MCAMD_MC_OFF_DRAMCFG + mc_props.mcp_dramhole MCAMD_MC_OFF_DRAMHOLE + mc_props.mcp_ilen MCAMD_MC_OFF_DRAM_ILEN + mc_props.mcp_ilsel MCAMD_MC_OFF_DRAM_ILSEL + mc_props.mcp_csbankmap MCAMD_MC_OFF_CSBANKMAP + mc_props.mcp_accwidth MCAMD_MC_OFF_ACCWIDTH + mc_props.mcp_csbank_intlv MCAMD_MC_OFF_CSBANK_INTLV + mc_props.mcp_disabled_cs MCAMD_MC_OFF_DISABLED_CS + +mc_cs_t + mccs_num MCAMD_CS_OFF_NUM + mccs_base MCAMD_CS_OFF_BASE_ADDR + mccs_mask MCAMD_CS_OFF_MASK + mccs_size MCAMD_CS_OFF_SIZE + mccs_dimmnums MCAMD_CS_OFF_DIMMNUMS + +mc_dimm_t + mcd_num MCAMD_DIMM_OFF_NUM + diff --git a/usr/src/uts/i86pc/io/mc/mcamd_subr.c b/usr/src/uts/i86pc/io/mc/mcamd_subr.c new file mode 100644 index 0000000000..4537de771b --- /dev/null +++ b/usr/src/uts/i86pc/io/mc/mcamd_subr.c @@ -0,0 +1,442 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Stub routines used to link in files from $SRC/common/mc + */ + +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/varargs.h> +#include <sys/cpu_module_impl.h> +#include <sys/fm/protocol.h> +#include <sys/mc.h> + +#include <mcamd.h> +#include <mcamd_off.h> + +int mcamd_debug = 0; /* see mcamd_api.h for MCAMD_DBG_* values */ + +struct mc_propmap { + uint_t mcpm_code; + uint_t mcpm_offset; +}; + +static uint_t +nodetype(mcamd_node_t *node) +{ + mc_hdr_t *mch = (mc_hdr_t *)node; + return (mch->mch_type); +} + +static void * +node2type(mcamd_node_t *node, int type) +{ + mc_hdr_t *mch = (mc_hdr_t *)node; + ASSERT(mch->mch_type == type); + return (mch); +} + +/* + * Iterate over all memory controllers. + */ +/*ARGSUSED*/ +mcamd_node_t * +mcamd_mc_next(mcamd_hdl_t *hdl, mcamd_node_t *root, mcamd_node_t *last) +{ + mc_t *mc; + + ASSERT(RW_LOCK_HELD(&mc_lock)); + + if (last == NULL) + return ((mcamd_node_t *)mc_list); + + mc = node2type(last, MC_NT_MC); + + return ((mcamd_node_t *)mc->mc_next); +} + +/* + * Iterate over all chip-selects of a MC or all chip-selects of a DIMM + * depending on the node type of 'node'. In the DIMM case we do not + * have a linked list of associated chip-selects but an array of pointer + * to them. + */ +/*ARGSUSED*/ +mcamd_node_t * +mcamd_cs_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last) +{ + uint_t nt = nodetype(node); + mc_t *mc; + mc_cs_t *mccs; + mc_dimm_t *mcd; + int i; + void *retval; + + ASSERT(nt == MC_NT_MC || nt == MC_NT_DIMM); + + if (last == NULL) { + switch (nt) { + case MC_NT_MC: + mc = node2type(node, MC_NT_MC); + retval = mc->mc_cslist; + break; + case MC_NT_DIMM: + mcd = node2type(node, MC_NT_DIMM); + retval = mcd->mcd_cs[0]; + break; + } + } else { + mccs = node2type(last, MC_NT_CS); + + switch (nt) { + case MC_NT_MC: + retval = mccs->mccs_next; + break; + case MC_NT_DIMM: + mcd = node2type(node, MC_NT_DIMM); + for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) { + if (mcd->mcd_cs[i] == mccs) + break; + } + if (i == MC_CHIP_DIMMRANKMAX) + cmn_err(CE_PANIC, "Bad last value for " + "mcamd_cs_next"); + + if (i == MC_CHIP_DIMMRANKMAX - 1) + retval = NULL; + else + retval = mcd->mcd_cs[i + 1]; + break; + } + } + + return ((mcamd_node_t *)retval); +} + +/* + * Iterate over all DIMMs of an MC or all DIMMs of a chip-select depending + * on the node type of 'node'. In the chip-select case we don not have + * a linked list of associated DIMMs but an array of pointers to them. + */ +/*ARGSUSED*/ +mcamd_node_t * +mcamd_dimm_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last) +{ + uint_t nt = nodetype(node); + mc_t *mc; + mc_cs_t *mccs; + mc_dimm_t *mcd; + int i; + void *retval; + + ASSERT(nt == MC_NT_MC || nt == MC_NT_CS); + + if (last == NULL) { + switch (nt) { + case MC_NT_MC: + mc = node2type(node, MC_NT_MC); + retval = mc->mc_props.mcp_dimmlist; + break; + case MC_NT_CS: + mccs = node2type(node, MC_NT_CS); + retval = mccs->mccs_dimm[0]; + break; + } + } else { + mcd = node2type(last, MC_NT_DIMM); + + switch (nt) { + case MC_NT_MC: + retval = mcd->mcd_next; + break; + case MC_NT_CS: + mccs = node2type(node, MC_NT_CS); + for (i = 0; i < MC_CHIP_DIMMPERCS; i++) { + if (mccs->mccs_dimm[i] == mcd) + break; + } + if (i == MC_CHIP_DIMMPERCS) + cmn_err(CE_PANIC, "Bad last value for " + "mcamd_dimm_next"); + + if (i == MC_CHIP_DIMMPERCS - 1) + retval = NULL; + else + retval = mccs->mccs_dimm[i + 1]; + break; + } + } + + return ((mcamd_node_t *)retval); +} + +/*ARGSUSED*/ +mcamd_node_t * +mcamd_cs_mc(mcamd_hdl_t *hdl, mcamd_node_t *csnode) +{ + mc_cs_t *mccs = node2type(csnode, MC_NT_CS); + return ((mcamd_node_t *)mccs->mccs_mc); +} + +/*ARGSUSED*/ +mcamd_node_t * +mcamd_dimm_mc(mcamd_hdl_t *hdl, mcamd_node_t *dnode) +{ + mc_dimm_t *mcd = node2type(dnode, MC_NT_DIMM); + return ((mcamd_node_t *)mcd->mcd_mc); +} + +/* + * Node properties. A property is accessed through a property number code; + * we search these tables for a match (choosing table from node type) and + * return the uint64_t property at the indicated offset into the node + * structure. All properties must be of type uint64_t. It is assumed that + * property lookup does not have to be super-fast - we search linearly + * down the (small) lists. + */ +static const struct mc_propmap mcamd_mc_propmap[] = { + { MCAMD_PROP_NUM, MCAMD_MC_OFF_NUM }, + { MCAMD_PROP_REV, MCAMD_MC_OFF_REV }, + { MCAMD_PROP_BASE_ADDR, MCAMD_MC_OFF_BASE_ADDR }, + { MCAMD_PROP_LIM_ADDR, MCAMD_MC_OFF_LIM_ADDR }, + { MCAMD_PROP_DRAM_CONFIG, MCAMD_MC_OFF_DRAMCFG }, + { MCAMD_PROP_DRAM_HOLE, MCAMD_MC_OFF_DRAMHOLE }, + { MCAMD_PROP_DRAM_ILEN, MCAMD_MC_OFF_DRAM_ILEN }, + { MCAMD_PROP_DRAM_ILSEL, MCAMD_MC_OFF_DRAM_ILSEL }, + { MCAMD_PROP_CSBANKMAP, MCAMD_MC_OFF_CSBANKMAP }, + { MCAMD_PROP_ACCESS_WIDTH, MCAMD_MC_OFF_ACCWIDTH }, + { MCAMD_PROP_CSBANK_INTLV, MCAMD_MC_OFF_CSBANK_INTLV }, + { MCAMD_PROP_DISABLED_CS, MCAMD_MC_OFF_DISABLED_CS } +}; + +static const struct mc_propmap mcamd_cs_propmap[] = { + { MCAMD_PROP_NUM, MCAMD_CS_OFF_NUM }, + { MCAMD_PROP_BASE_ADDR, MCAMD_CS_OFF_BASE_ADDR }, + { MCAMD_PROP_MASK, MCAMD_CS_OFF_MASK }, + { MCAMD_PROP_SIZE, MCAMD_CS_OFF_SIZE }, + { MCAMD_PROP_LODIMM, MCAMD_CS_OFF_DIMMNUMS }, + { MCAMD_PROP_UPDIMM, MCAMD_CS_OFF_DIMMNUMS + + MCAMD_CS_OFF_DIMMNUMS_INCR } +}; + +static const struct mc_propmap mcamd_dimm_propmap[] = { + { MCAMD_PROP_NUM, MCAMD_DIMM_OFF_NUM }, +}; + +/*ARGSUSED*/ +int +mcamd_get_numprop(mcamd_hdl_t *hdl, mcamd_node_t *node, uint_t code, + uint64_t *valp) +{ + int i; + mc_hdr_t *mch = (mc_hdr_t *)node; + int nt = mch->mch_type; + int found = 0; + const struct mc_propmap *pmp; + struct mcamd_nt_props { + const struct mc_propmap *props; + int numprops; + } props[] = { + { mcamd_mc_propmap, /* MC_NT_MC */ + sizeof (mcamd_mc_propmap) / sizeof (struct mc_propmap) }, + { mcamd_cs_propmap, /* MC_NT_CS */ + sizeof (mcamd_cs_propmap) / sizeof (struct mc_propmap) }, + { mcamd_dimm_propmap, /* MC_NT_DIMM */ + sizeof (mcamd_dimm_propmap) / sizeof (struct mc_propmap) }, + }; + + if (mch->mch_type < MC_NT_NTYPES) { + for (i = 0, pmp = props[nt].props; i < props[nt].numprops; + i++, pmp++) { + if (pmp->mcpm_code == code) { + found = 1; + break; + } + } + } + + ASSERT(found); + if (found) { + *valp = *(uint64_t *)((uintptr_t)node + pmp->mcpm_offset); + } + + return (found == 1); +} + +int +mcamd_errno(mcamd_hdl_t *mcamd) +{ + return (mcamd->mcamd_errno); +} + +int +mcamd_set_errno(mcamd_hdl_t *mcamd, int err) +{ + mcamd->mcamd_errno = err; + return (-1); +} + +void +mcamd_dprintf(mcamd_hdl_t *mcamd, int mask, const char *fmt, ...) +{ + va_list ap; + + if (!(mcamd->mcamd_debug & mask)) + return; + + va_start(ap, fmt); + vcmn_err(mask & MCAMD_DBG_ERR ? CE_WARN : CE_NOTE, fmt, ap); + va_end(ap); +} + +void +mcamd_mkhdl(mcamd_hdl_t *hdl) +{ + hdl->mcamd_errno = 0; + hdl->mcamd_debug = mcamd_debug; +} + +/*ARGSUSED*/ +static int +mcamd_patounum_wrap(void *arg, uint64_t pa, uint32_t synd, int syndtype, + mc_unum_t *unump) +{ + mcamd_hdl_t mcamd; + int rc; + + mcamd_mkhdl(&mcamd); + + rw_enter(&mc_lock, RW_READER); + + rc = mcamd_patounum(&mcamd, + (mcamd_node_t *)mc_list, pa, synd, syndtype, unump); + +#ifdef DEBUG + /* + * Apply the reverse operation to verify the result. If there is + * a problem complain but continue. + */ + if (rc == 0 && MCAMD_RC_OFFSET_VALID(unump->unum_offset)) { + uint64_t rpa; + if (mcamd_unumtopa(&mcamd, (mcamd_node_t *)mc_list, unump, + &rpa) != 0 || rpa != pa) { + mcamd_dprintf(&mcamd, MCAMD_DBG_ERR, + "mcamd_patounum_wrap: offset calculation " + "verification for PA 0x%llx failed\n", pa); + } + } +#endif + rw_exit(&mc_lock); + + return (rc == 0); +} + +static int +fmri2unum(nvlist_t *nvl, mc_unum_t *unump) +{ + int i; + uint64_t offset; + nvlist_t *fu, **hcl; + uint_t npr; + + if (nvlist_lookup_nvlist(nvl, FM_FMRI_MEM_UNUM "-fmri", &fu) != 0 || + nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &offset) != 0|| + nvlist_lookup_nvlist_array(fu, FM_FMRI_HC_LIST, &hcl, &npr) != 0) + return (0); + + + bzero(unump, sizeof (mc_unum_t)); + for (i = 0; i < MC_UNUM_NDIMM; i++) + unump->unum_dimms[i] = -1; + + for (i = 0; i < npr; i++) { + char *hcnm, *hcid; + long v; + + if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &hcnm) != 0 || + nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &hcid) != 0 || + ddi_strtol(hcid, NULL, 0, &v) != 0) + return (0); + + if (strcmp(hcnm, "motherboard") == 0) + unump->unum_board = (int)v; + else if (strcmp(hcnm, "chip") == 0) + unump->unum_chip = (int)v; + else if (strcmp(hcnm, "memory-controller") == 0) + unump->unum_mc = (int)v; + else if (strcmp(hcnm, "chip-select") == 0) + unump->unum_cs = (int)v; + else if (strcmp(hcnm, "dimm") == 0) + unump->unum_dimms[0] = (int)v; + } + + unump->unum_offset = offset; + + return (1); +} + +/*ARGSUSED*/ +static int +mcamd_unumtopa_wrap(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap) +{ + mcamd_hdl_t mcamd; + int rc; + mc_unum_t unum; + + if (unump != NULL && nvl != NULL) + return (0); + + if (unump == NULL) { + if (!fmri2unum(nvl, &unum)) + return (0); + unump = &unum; + } + + mcamd_mkhdl(&mcamd); + + rw_enter(&mc_lock, RW_READER); + rc = mcamd_unumtopa(&mcamd, (mcamd_node_t *)mc_list, unump, pap); + rw_exit(&mc_lock); + + return (rc == 0); +} + +static const cmi_mc_ops_t mcamd_mc_ops = { + mcamd_patounum_wrap, + mcamd_unumtopa_wrap +}; + +void +mcamd_mc_register(cpu_t *cp) +{ + cmi_mc_register(cp, &mcamd_mc_ops, NULL); +} diff --git a/usr/src/uts/i86pc/io/rootnex.c b/usr/src/uts/i86pc/io/rootnex.c index 69789f49a7..741b405669 100644 --- a/usr/src/uts/i86pc/io/rootnex.c +++ b/usr/src/uts/i86pc/io/rootnex.c @@ -63,7 +63,6 @@ #include <sys/rootnex.h> #include <vm/hat_i86.h> - /* * enable/disable extra checking of function parameters. Useful for debugging * drivers. @@ -82,6 +81,9 @@ int rootnex_unbind_verify_buffer = 0; int rootnex_sync_check_parms = 0; #endif +/* Master Abort and Target Abort panic flag */ +int rootnex_fm_ma_ta_panic_flag = 0; + /* Semi-temporary patchables to phase in bug fixes, test drivers, etc. */ int rootnex_bind_fail = 1; int rootnex_bind_warn = 1; @@ -321,6 +323,8 @@ static int rootnex_maxxfer_window_boundary(ddi_dma_impl_t *hp, static int rootnex_valid_sync_parms(ddi_dma_impl_t *hp, rootnex_window_t *win, off_t offset, size_t size, uint_t cache_flags); static int rootnex_verify_buffer(rootnex_dma_t *dma); +static int rootnex_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, + const void *no_used); /* @@ -365,6 +369,7 @@ _fini(void) static int rootnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { + int fmcap; int e; @@ -385,12 +390,24 @@ rootnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) rootnex_state = kmem_zalloc(sizeof (rootnex_state_t), KM_SLEEP); rootnex_state->r_dip = dip; + rootnex_state->r_err_ibc = (ddi_iblock_cookie_t)ipltospl(15); rootnex_state->r_reserved_msg_printed = B_FALSE; rootnex_cnt = &rootnex_state->r_counters[0]; mutex_init(&rootnex_state->r_peekpoke_mutex, NULL, MUTEX_SPIN, (void *)ipltospl(15)); + /* + * Set minimum fm capability level for i86pc platforms and then + * initialize error handling. Since we're the rootnex, we don't + * care what's returned in the fmcap field. + */ + ddi_system_fmcap = DDI_FM_ERRCB_CAPABLE; + fmcap = ddi_system_fmcap; + ddi_fm_init(dip, &fmcap, &rootnex_state->r_err_ibc); + if (fmcap & DDI_FM_ERRCB_CAPABLE) + ddi_fm_handler_register(dip, rootnex_fm_callback, NULL); + /* initialize DMA related state */ e = rootnex_dma_init(); if (e != DDI_SUCCESS) { @@ -4407,3 +4424,10 @@ rootnex_dma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, return (DDI_FAILURE); #endif /* defined(__amd64) */ } + +/*ARGSUSED*/ +static int +rootnex_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used) +{ + return (rootnex_fm_ma_ta_panic_flag ? DDI_FM_FATAL : DDI_FM_NONFATAL); +} diff --git a/usr/src/uts/i86pc/mc-amd/Makefile b/usr/src/uts/i86pc/mc-amd/Makefile new file mode 100644 index 0000000000..d7c876fa41 --- /dev/null +++ b/usr/src/uts/i86pc/mc-amd/Makefile @@ -0,0 +1,97 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = mc-amd +# +OBJECTS = $(MCAMD_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(MCAMD_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE) +SRCDIR = $(UTSBASE)/i86pc/io/mc +CONF_SRCDIR = $(UTSBASE)/i86pc/io/mc + +MCAMD_OFF_H = $(OBJS_DIR)/mcamd_off.h +MCAMD_OFF_SRC = $(SRCDIR)/mcamd_off.in + +# +# Include common rules. +# +include ../cpu/Makefile.cpu + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +# +# Overrides and additions +# +CPPFLAGS += -I$(SRCDIR) -I$(OBJS_DIR) -I$(SRC)/common/mc/mc-amd +CLEANFILES += $(MCAMD_OFF_H) +CLOBBERFILES += $(MCAMD_OFF_H) + +$(OBJECTS): $(MCAMD_OFF_H) + +# +# Create mcamd_off.h +# +$(MCAMD_OFF_H): $(MCAMD_OFF_SRC) + $(OFFSETS_CREATE) <$(MCAMD_OFF_SRC) >$@ + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include ../Makefile.targ diff --git a/usr/src/uts/i86pc/os/cmi.c b/usr/src/uts/i86pc/os/cmi.c new file mode 100644 index 0000000000..236b3c44a8 --- /dev/null +++ b/usr/src/uts/i86pc/os/cmi.c @@ -0,0 +1,299 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Public interface to routines implemented by CPU modules + */ + +#include <sys/x86_archext.h> +#include <sys/cpu_module_impl.h> +#include <sys/fm/util.h> +#include <sys/reboot.h> +#include <sys/modctl.h> +#include <sys/param.h> +#include <sys/cmn_err.h> +#include <sys/systm.h> +#include <sys/types.h> + +#define CPUMOD_SUBDIR "cpu" +#define CPUMOD_PREFIX "cpu" + +#define CMI_OPS(cpu) \ + (cpu)->cpu_m.mcpu_cmi->cmi_ops +#define CMI_DATA(cpu) \ + (cpu)->cpu_m.mcpu_cmidata + +/* + * If cleared for debugging, we will suppress panicking on fatal hardware + * errors. This should *only* be used for debugging; it use can and will + * cause data corruption if actual hardware errors are detected by the system. + */ +int cmi_panic_on_uncorrectable_error = 1; + +static cmi_t *cmi_list; +static kmutex_t cmi_load_lock; + +static int +cmi_cpu_match(cpu_t *c1, cpu_t *c2) +{ + return (cpuid_getfamily(c1) == cpuid_getfamily(c2) && + cpuid_getmodel(c1) == cpuid_getmodel(c2) && + cpuid_getstep(c1) == cpuid_getstep(c2) && + strcmp(cpuid_getvendorstr(c1), cpuid_getvendorstr(c2)) == 0); +} + +static cmi_t * +cmi_load_modctl(modctl_t *modp) +{ + uintptr_t ops; + cmi_t *cmi; + + ASSERT(MUTEX_HELD(&cmi_load_lock)); + + for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) { + if (cmi->cmi_modp == modp) + return (cmi); + } + + if ((ops = modlookup_by_modctl(modp, "_cmi_ops")) == NULL) { + cmn_err(CE_WARN, "CPU module %s is invalid: no _cmi_ops " + "found\n", modp->mod_modname); + return (NULL); + } + + /* + * Hold the module in memory. We call to CPU modules without using the + * stubs mechanism, so these modules must be manually held in memory. + * The mod_ref acts as if another loaded module has a dependency on us. + */ + mutex_enter(&mod_lock); + modp->mod_ref++; + mutex_exit(&mod_lock); + + cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP); + cmi->cmi_ops = (const cmi_ops_t *)ops; + cmi->cmi_modp = modp; + + cmi->cmi_next = cmi_list; + cmi_list = cmi; + + return (cmi); +} + +static cmi_t * +cmi_load_module(cpu_t *cp) +{ + modctl_t *modp; + cmi_t *cmi; + int i, modid; + uint_t s[3]; + + /* + * Look to see if we've already got a module loaded for a CPU just + * like this one. If we do, then we'll re-use it. + */ + ASSERT(MUTEX_HELD(&cmi_load_lock)); + mutex_enter(&cpu_lock); + + for (i = 0; i < NCPU; i++) { + cpu_t *cp2 = cpu[i]; + + if (cp2 != NULL && cp2 != cp && + cp2->cpu_m.mcpu_cmi != NULL && cmi_cpu_match(cp, cp2)) { + mutex_exit(&cpu_lock); + return (cp2->cpu_m.mcpu_cmi); + } + } + + mutex_exit(&cpu_lock); + + /* + * If we can't find a match, attempt to load the appropriate module. + * If that also fails, try to load the generic CPU module. + */ + s[0] = cpuid_getfamily(cp); + s[1] = cpuid_getmodel(cp); + s[2] = cpuid_getstep(cp); + + modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX, + cpuid_getvendorstr(cp), ".", s, sizeof (s) / sizeof (s[0])); + + if (modid == -1) + modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic"); + + if (modid == -1) + return (NULL); + + modp = mod_hold_by_id(modid); + cmi = cmi_load_modctl(modp); + mod_release_mod(modp); + + return (cmi); +} + +static cmi_t * +cmi_load_generic(void) +{ + modctl_t *modp; + cmi_t *cmi; + int modid; + + if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1) + return (NULL); + + modp = mod_hold_by_id(modid); + cmi = cmi_load_modctl(modp); + mod_release_mod(modp); + + return (cmi); +} + +/* + * Load a CPU module for the specified CPU, and then call its cmi_init routine. + * If the module returns ENOTSUP, try using the generic CPU module instead. + * If all else fails, we return -1 and the caller will panic or halt. + */ +int +cmi_load(cpu_t *cp) +{ + int err = ENOENT; + cmi_t *cmi; + void *data; + + mutex_enter(&cmi_load_lock); + + if ((cmi = cmi_load_module(cp)) == NULL || ( + (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 && err != ENOTSUP)) { + cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d", + cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err); + mutex_exit(&cmi_load_lock); + return (-1); + } + + if (err != 0 && ((cmi = cmi_load_generic()) == NULL || + (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) { + cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d", + cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err); + mutex_exit(&cmi_load_lock); + return (-1); + } + + ASSERT(cp->cpu_m.mcpu_cmi == NULL); + cp->cpu_m.mcpu_cmi = cmi; + cp->cpu_m.mcpu_cmidata = data; + + cmi->cmi_refcnt++; + mutex_exit(&cmi_load_lock); + + if (boothowto & RB_VERBOSE) { + printf("cpuid %d: initialized cpumod: %s\n", + cp->cpu_id, cmi->cmi_modp->mod_modname); + } + + return (0); +} + +void +cmi_init(void) +{ + if (cmi_load(CPU) < 0) + panic("failed to load module for CPU %u", CPU->cpu_id); +} + +void +cmi_post_init(void) +{ + CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU)); +} + +void +cmi_faulted_enter(cpu_t *cp) +{ + CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp)); +} + +void +cmi_faulted_exit(cpu_t *cp) +{ + CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp)); +} + +int +cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen) +{ + return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen)); +} + +void +cmi_mca_init(void) +{ + CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU)); +} + +void +cmi_mca_trap(struct regs *rp) +{ + if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) { + if (cmi_panic_on_uncorrectable_error) + fm_panic("Unrecoverable Machine-Check Exception"); + else + cmn_err(CE_WARN, "suppressing panic from fatal #mc"); + } +} + +int +cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs) +{ + int err; + + kpreempt_disable(); + err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs); + kpreempt_enable(); + + return (err); +} + +void +cmi_mca_poke(void) +{ + CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU)); +} + +void +cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata) +{ + CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata); +} + +int +cmi_mc_patounum(uint64_t pa, uint32_t synd, int syndtype, mc_unum_t *up) +{ + const struct cmi_mc_ops *mcops; + cpu_t *cp = CPU; + + if (CMI_OPS(cp) == NULL || + (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) + return (-1); /* not registered yet */ + + return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, synd, syndtype, up)); +} + +int +cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap) +{ + const struct cmi_mc_ops *mcops; + cpu_t *cp = CPU; + + if (up != NULL && nvl != NULL) + return (-1); /* only convert from one or the other form */ + + if (CMI_OPS(cp) == NULL || + (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) + return (-1); /* not registered yet */ + + return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap)); +} diff --git a/usr/src/uts/i86pc/os/cpuid.c b/usr/src/uts/i86pc/os/cpuid.c index a16c5a6298..739700125a 100644 --- a/usr/src/uts/i86pc/os/cpuid.c +++ b/usr/src/uts/i86pc/os/cpuid.c @@ -2628,9 +2628,12 @@ add_cpunode2devtree(processorid_t cpu_id, struct cpuid_info *cpi) "chunks", CPI_CHUNKS(cpi)); (void) ndi_prop_update_int(DDI_DEV_T_NONE, cpu_devi, "apic-id", CPI_APIC_ID(cpi)); - if (cpi->cpi_chipid >= 0) + if (cpi->cpi_chipid >= 0) { (void) ndi_prop_update_int(DDI_DEV_T_NONE, cpu_devi, "chip#", cpi->cpi_chipid); + (void) ndi_prop_update_int(DDI_DEV_T_NONE, cpu_devi, + "clog#", cpi->cpi_clogid); + } } /* cpuid-features */ diff --git a/usr/src/uts/i86pc/os/memscrub.c b/usr/src/uts/i86pc/os/memscrub.c index 6dd21e302f..2e73a2a694 100644 --- a/usr/src/uts/i86pc/os/memscrub.c +++ b/usr/src/uts/i86pc/os/memscrub.c @@ -19,8 +19,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -129,6 +130,7 @@ static void *memscrub_pte; * we can patch these defaults in /etc/system if necessary */ uint_t disable_memscrub = 0; +static uint_t disable_memscrub_quietly = 0; pgcnt_t memscrub_min_pages = MEMSCRUB_MIN_PAGES; pgcnt_t memscrub_span_pages = MEMSCRUB_DFL_SPAN_PAGES; time_t memscrub_period_sec = MEMSCRUB_DFL_PERIOD_SEC; @@ -150,6 +152,7 @@ static uint_t memscrub_phys_pages; static kcondvar_t memscrub_cv; static kmutex_t memscrub_lock; + /* * memscrub_lock protects memscrub_memlist */ @@ -199,6 +202,16 @@ memscrub_init() TS_RUN, memscrub_thread_pri); } +/* + * Function to cause the software memscrubber to exit quietly if the + * platform support has located a hardware scrubber and enabled it. + */ +void +memscrub_disable(void) +{ + disable_memscrub_quietly = 1; +} + #ifdef MEMSCRUB_DEBUG void memscrub_printmemlist(char *title, struct memlist *listp) @@ -267,7 +280,7 @@ memscrubber() deadline = gethrestime_sec() + memscrub_delay_start_sec; for (;;) { - if (disable_memscrub) + if (disable_memscrub || disable_memscrub_quietly) break; mutex_enter(&memscrub_lock); @@ -317,7 +330,7 @@ memscrubber() pgcnt_t pages = memscrub_span_pages; uint64_t address = mlp_next_addr; - if (disable_memscrub) + if (disable_memscrub || disable_memscrub_quietly) break; mutex_enter(&memscrub_lock); @@ -379,7 +392,8 @@ memscrubber() memscrub_exit: - cmn_err(CE_NOTE, "memory scrubber exiting."); + if (!disable_memscrub_quietly) + cmn_err(CE_NOTE, "memory scrubber exiting."); cv_destroy(&memscrub_cv); diff --git a/usr/src/uts/i86pc/os/mp_startup.c b/usr/src/uts/i86pc/os/mp_startup.c index f98a787266..a0809d99fe 100644 --- a/usr/src/uts/i86pc/os/mp_startup.c +++ b/usr/src/uts/i86pc/os/mp_startup.c @@ -64,6 +64,7 @@ #include <vm/hat_i86.h> #include <sys/memnode.h> #include <sys/pci_cfgspace.h> +#include <sys/cpu_module.h> struct cpu cpus[1]; /* CPU data */ struct cpu *cpu[NCPU] = {&cpus[0]}; /* pointers to all CPUs */ @@ -1024,11 +1025,6 @@ mp_startup(void) */ if (x86_feature & X86_MTRR) mtrr_sync(); - /* - * Enable machine check architecture - */ - if (x86_feature & X86_MCA) - setup_mca(); /* * Initialize this CPU's syscall handlers @@ -1102,6 +1098,18 @@ mp_startup(void) (void) spl0(); /* enable interrupts */ + /* + * Set up the CPU module for this CPU. This can't be done before + * this CPU is made CPU_READY, because we may (in heterogeneous systems) + * need to go load another CPU module. The act of attempting to load + * a module may trigger a cross-call, which will ASSERT unless this + * cpu is CPU_READY. + */ + cmi_init(); + + if (x86_feature & X86_MCA) + cmi_mca_init(); + if (boothowto & RB_DEBUG) kdi_dvec_cpu_init(cp); @@ -1238,15 +1246,17 @@ mp_unmap_warm_reset_vector(ushort_t *warm_reset_vector) psm_unmap_phys((caddr_t)warm_reset_vector, sizeof (ushort_t *)); } -/*ARGSUSED*/ void mp_cpu_faulted_enter(struct cpu *cp) -{} +{ + cmi_faulted_enter(cp); +} -/*ARGSUSED*/ void mp_cpu_faulted_exit(struct cpu *cp) -{} +{ + cmi_faulted_exit(cp); +} /* * The following two routines are used as context operators on threads belonging diff --git a/usr/src/uts/i86pc/os/startup.c b/usr/src/uts/i86pc/os/startup.c index 091fb331e3..1766aa0d81 100644 --- a/usr/src/uts/i86pc/os/startup.c +++ b/usr/src/uts/i86pc/os/startup.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -123,6 +123,7 @@ #include <sys/cpc_impl.h> #include <sys/chip.h> #include <sys/x86_archext.h> +#include <sys/cpu_module.h> #include <sys/smbios.h> extern void progressbar_init(void); @@ -1289,11 +1290,6 @@ startup_modules(void) mod_setup(); /* - * Setup machine check architecture on P6 - */ - setup_mca(); - - /* * Initialize system parameters. */ param_init(); @@ -1337,6 +1333,19 @@ startup_modules(void) * then invoke bus specific code to probe devices. */ setup_ddi(); + + /* + * Set up the CPU module subsystem. Modifies the device tree, so it + * must be done after setup_ddi(). + */ + cmi_init(); + + /* + * Initialize the MCA handlers + */ + if (x86_feature & X86_MCA) + cmi_mca_init(); + /* * Fake a prom tree such that /dev/openprom continues to work */ @@ -1879,6 +1888,11 @@ post_startup(void) memscrub_init(); /* + * Complete CPU module initialization + */ + cmi_post_init(); + + /* * Perform forceloading tasks for /etc/system. */ (void) mod_sysctl(SYS_FORCELOAD, NULL); @@ -2379,86 +2393,6 @@ uint64_t mtrrdef, pat_attr_reg; */ int enable_relaxed_mtrr = 0; -/* - * These must serve for Pentium, Pentium Pro (P6/Pentium II/Pentium III) - * and Pentium 4, and yes, they are named 0, 1, 2, 4, 3 in ascending - * address order (starting from 0x400). The Pentium 4 only implements - * 4 sets, and while they are named 0-3 in the doc, the corresponding - * names for P6 are 0,1,2,4. So define these arrays in address order - * so that they work for both pre-Pentium4 and Pentium 4 processors. - */ - -static uint_t mci_ctl[] = {REG_MC0_CTL, REG_MC1_CTL, REG_MC2_CTL, - REG_MC4_CTL, REG_MC3_CTL}; -static uint_t mci_status[] = {REG_MC0_STATUS, REG_MC1_STATUS, REG_MC2_STATUS, - REG_MC4_STATUS, REG_MC3_STATUS}; -static uint_t mci_addr[] = {REG_MC0_ADDR, REG_MC1_ADDR, REG_MC2_ADDR, - REG_MC4_ADDR, REG_MC3_ADDR}; -static int mca_cnt; - - -void -setup_mca() -{ - int i; - uint64_t mca_cap; - - if (!(x86_feature & X86_MCA)) - return; - mca_cap = rdmsr(REG_MCG_CAP); - if (mca_cap & MCG_CAP_CTL_P) - wrmsr(REG_MCG_CTL, -1ULL); /* all ones */ - mca_cnt = mca_cap & MCG_CAP_COUNT_MASK; - if (mca_cnt > P6_MCG_CAP_COUNT) - mca_cnt = P6_MCG_CAP_COUNT; - for (i = 1; i < mca_cnt; i++) - wrmsr(mci_ctl[i], -1ULL); /* all ones */ - for (i = 0; i < mca_cnt; i++) - wrmsr(mci_status[i], 0ULL); - setcr4(getcr4() | CR4_MCE); - -} - -int -mca_exception(struct regs *rp) -{ - uint64_t status, addr; - int i, ret = 1, errcode, mserrcode; - - status = rdmsr(REG_MCG_STATUS); - if (status & MCG_STATUS_RIPV) - ret = 0; - if (status & MCG_STATUS_EIPV) - cmn_err(CE_WARN, "MCE at 0x%lx", rp->r_pc); - wrmsr(REG_MCG_STATUS, 0ULL); - for (i = 0; i < mca_cnt; i++) { - status = rdmsr(mci_status[i]); - /* - * If status register not valid skip this bank - */ - if (!(status & MCI_STATUS_VAL)) - continue; - errcode = status & MCI_STATUS_ERRCODE; - mserrcode = (status >> MSERRCODE_SHFT) & MCI_STATUS_ERRCODE; - if (status & MCI_STATUS_ADDRV) { - /* - * If mci_addr contains the address where - * error occurred, display the address - */ - addr = rdmsr(mci_addr[i]); - cmn_err(CE_WARN, "MCE: Bank %d: error code 0x%x:"\ - "addr = 0x%" PRIx64 ", model errcode = 0x%x", i, - errcode, addr, mserrcode); - } else { - cmn_err(CE_WARN, - "MCE: Bank %d: error code 0x%x, mserrcode = 0x%x", - i, errcode, mserrcode); - } - wrmsr(mci_status[i], 0ULL); - } - return (ret); -} - void setup_mtrr() { diff --git a/usr/src/uts/i86pc/os/trap.c b/usr/src/uts/i86pc/os/trap.c index da416edeea..37a38b9a80 100644 --- a/usr/src/uts/i86pc/os/trap.c +++ b/usr/src/uts/i86pc/os/trap.c @@ -497,15 +497,6 @@ trap(struct regs *rp, caddr_t addr, processorid_t cpuid) } switch (type) { - - case T_MCE: /* Machine check exception */ - case T_MCE + USER: - if (x86_feature & X86_MCA) { - if (mca_exception(rp)) - (void) die(type, rp, addr, cpuid); - type &= ~USER; - goto cleanup; - } default: if (type & USER) { if (tudebug) diff --git a/usr/src/uts/i86pc/sys/cpu_module.h b/usr/src/uts/i86pc/sys/cpu_module.h new file mode 100644 index 0000000000..8bd9c488a7 --- /dev/null +++ b/usr/src/uts/i86pc/sys/cpu_module.h @@ -0,0 +1,69 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_CPU_MODULE_H +#define _SYS_CPU_MODULE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/cpuvar.h> +#include <sys/nvpair.h> +#include <sys/mc.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct regs; +struct cmi_mc_ops; + +typedef struct cmi_mca_regs { + uint_t cmr_msrnum; + uint64_t cmr_msrval; +} cmi_mca_regs_t; + +extern void cmi_init(void); +extern void cmi_post_init(void); + +extern void cmi_faulted_enter(struct cpu *); +extern void cmi_faulted_exit(struct cpu *); +extern int cmi_scrubber_enable(struct cpu *, uint64_t, uint64_t); + +extern void cmi_mca_init(void); +extern int cmi_mca_inject(cmi_mca_regs_t *, uint_t); +extern void cmi_mca_poke(void); + +extern void cmi_mc_register(struct cpu *, const struct cmi_mc_ops *, void *); +extern int cmi_mc_patounum(uint64_t, uint32_t, int, mc_unum_t *); +extern int cmi_mc_unumtopa(mc_unum_t *, nvlist_t *, uint64_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_CPU_MODULE_H */ diff --git a/usr/src/uts/i86pc/sys/cpu_module_impl.h b/usr/src/uts/i86pc/sys/cpu_module_impl.h new file mode 100644 index 0000000000..7bdd6de7ce --- /dev/null +++ b/usr/src/uts/i86pc/sys/cpu_module_impl.h @@ -0,0 +1,74 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_CPU_MODULE_IMPL_H +#define _SYS_CPU_MODULE_IMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/cpu_module.h> +#include <sys/cpuvar.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct cmi_mc_ops { + int (*cmi_mc_patounum)(void *, uint64_t, uint32_t, int, mc_unum_t *); + int (*cmi_mc_unumtopa)(void *, mc_unum_t *, nvlist_t *, uint64_t *); +} cmi_mc_ops_t; + +typedef struct cmi_ops { + int (*cmi_init)(cpu_t *, void **); + void (*cmi_post_init)(void *); + void (*cmi_fini)(void *); + void (*cmi_faulted_enter)(void *); + void (*cmi_faulted_exit)(void *); + int (*cmi_scrubber_enable)(void *, uint64_t, uint64_t); + void (*cmi_mca_init)(void *); + int (*cmi_mca_trap)(void *, struct regs *); + int (*cmi_mca_inject)(void *, cmi_mca_regs_t *, uint_t); + void (*cmi_mca_poke)(void *); + void (*cmi_mc_register)(void *, const cmi_mc_ops_t *, void *); + const struct cmi_mc_ops *(*cmi_mc_getops)(void *); +} cmi_ops_t; + +typedef struct cmi { + struct cmi *cmi_next; + const cmi_ops_t *cmi_ops; + struct modctl *cmi_modp; + uint_t cmi_refcnt; +} cmi_t; + +extern int cmi_panic_on_uncorrectable_error; + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_CPU_MODULE_IMPL_H */ diff --git a/usr/src/uts/i86pc/sys/machcpuvar.h b/usr/src/uts/i86pc/sys/machcpuvar.h index 9e355d4b49..f339b97951 100644 --- a/usr/src/uts/i86pc/sys/machcpuvar.h +++ b/usr/src/uts/i86pc/sys/machcpuvar.h @@ -52,7 +52,8 @@ extern "C" { */ typedef void *cpu_pri_lev_t; -struct cpuid_info; /* (deliberately not visible here) */ +struct cpuid_info; +struct cmi; struct machcpu { /* define all the x_call stuff */ @@ -91,6 +92,8 @@ struct machcpu { uint64_t pil_high_start[HIGH_LEVELS]; uint64_t intrstat[PIL_MAX + 1][2]; struct cpuid_info *mcpu_cpi; + struct cmi *mcpu_cmi; /* CPU module state */ + void *mcpu_cmidata; #if defined(__amd64) greg_t mcpu_rtmp_rsp; /* syscall: temporary %rsp stash */ greg_t mcpu_rtmp_r15; /* syscall: temporary %r15 stash */ diff --git a/usr/src/uts/i86pc/sys/machsystm.h b/usr/src/uts/i86pc/sys/machsystm.h index 0732c4fde2..8dc5c5414b 100644 --- a/usr/src/uts/i86pc/sys/machsystm.h +++ b/usr/src/uts/i86pc/sys/machsystm.h @@ -86,7 +86,9 @@ extern void get_system_configuration(void); extern void mmu_init(void); extern int cpuid2nodeid(int); extern void map_kaddr(caddr_t, pfn_t, int, int); + extern void memscrub_init(void); +extern void memscrub_disable(void); extern unsigned int microdata; extern int use_mp; diff --git a/usr/src/uts/i86pc/sys/rootnex.h b/usr/src/uts/i86pc/sys/rootnex.h index 4236730776..96600248eb 100644 --- a/usr/src/uts/i86pc/sys/rootnex.h +++ b/usr/src/uts/i86pc/sys/rootnex.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -325,14 +325,15 @@ typedef enum { * r_counters - profile/performance counters */ typedef struct rootnex_state_s { - uint_t r_prealloc_cookies; - uint_t r_prealloc_size; - kmem_cache_t *r_dmahdl_cache; - uintptr_t r_dvma_call_list_id; - kmutex_t r_peekpoke_mutex; - dev_info_t *r_dip; - boolean_t r_reserved_msg_printed; - uint64_t r_counters[ROOTNEX_CNT_LAST]; + uint_t r_prealloc_cookies; + uint_t r_prealloc_size; + kmem_cache_t *r_dmahdl_cache; + uintptr_t r_dvma_call_list_id; + kmutex_t r_peekpoke_mutex; + dev_info_t *r_dip; + ddi_iblock_cookie_t r_err_ibc; + boolean_t r_reserved_msg_printed; + uint64_t r_counters[ROOTNEX_CNT_LAST]; } rootnex_state_t; diff --git a/usr/src/uts/i86pc/vm/hat_kdi.c b/usr/src/uts/i86pc/vm/hat_kdi.c index 61d26bb28f..acdc2ddbb9 100644 --- a/usr/src/uts/i86pc/vm/hat_kdi.c +++ b/usr/src/uts/i86pc/vm/hat_kdi.c @@ -19,8 +19,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -32,6 +33,7 @@ * operations may be performed. */ +#include <sys/cpuvar.h> #include <sys/kdi_impl.h> #include <sys/errno.h> #include <sys/systm.h> diff --git a/usr/src/uts/intel/ia32/ml/exception.s b/usr/src/uts/intel/ia32/ml/exception.s index fce42f797b..5886ab532c 100644 --- a/usr/src/uts/intel/ia32/ml/exception.s +++ b/usr/src/uts/intel/ia32/ml/exception.s @@ -1,5 +1,5 @@ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -943,14 +943,47 @@ check_for_user_address: /* * #MC */ + .globl cmi_mca_trap /* see uts/i86pc/os/cmi.c */ + +#if defined(__amd64) + ENTRY_NP(mcetrap) TRAP_NOERR(T_MCE) /* $18 */ -#if defined(__amd64) SET_CPU_GSBASE -#endif - jmp cmninttrap + INTR_PUSH + + TRACE_PTR(%rdi, %rbx, %ebx, %rcx, $TT_TRAP) + TRACE_REGS(%rdi, %rsp, %rbx, %rcx) + TRACE_STAMP(%rdi) + + DISABLE_INTR_FLAGS + movq %rsp, %rbp + + movq %rsp, %rdi /* arg0 = struct regs *rp */ + call cmi_mca_trap /* cmi_mca_trap(rp); */ + + jmp _sys_rtt SET_SIZE(mcetrap) +#else + + ENTRY_NP(mcetrap) + TRAP_NOERR(T_MCE) /* $18 */ + INTR_PUSH + + DISABLE_INTR_FLAGS + movl %esp, %ebp + + movl %esp, %ecx + pushl %ecx /* arg0 = struct regs *rp */ + call cmi_mca_trap /* cmi_mca_trap(rp) */ + addl $4, %esp /* pop arg0 */ + + jmp _sys_rtt + SET_SIZE(mcetrap) + +#endif + /* * #XF */ diff --git a/usr/src/uts/intel/ia32/ml/i86_subr.s b/usr/src/uts/intel/ia32/ml/i86_subr.s index 4c75164e50..5a164b1088 100644 --- a/usr/src/uts/intel/ia32/ml/i86_subr.s +++ b/usr/src/uts/intel/ia32/ml/i86_subr.s @@ -1999,23 +1999,43 @@ repoutsd(int port, uint32_t *addr, int count) #endif /* __lint */ /* + * void int3(void) + * void int18(void) * void int20(void) */ #if defined(__lint) void +int3(void) +{} + +void +int18(void) +{} + +void int20(void) {} #else /* __lint */ + ENTRY(int3) + int $T_BPTFLT + ret + SET_SIZE(int3) + + ENTRY(int18) + int $T_MCE + ret + SET_SIZE(int18) + ENTRY(int20) movl boothowto, %eax andl $RB_DEBUG, %eax jz 1f - int $20 + int $T_DBGENTR 1: rep; ret /* use 2 byte return instruction when branch target */ /* AMD Software Optimization Guide - Section 6.2 */ diff --git a/usr/src/uts/intel/ia32/sys/trap.h b/usr/src/uts/intel/ia32/sys/trap.h index 44a22bcd51..9eafa6b197 100644 --- a/usr/src/uts/intel/ia32/sys/trap.h +++ b/usr/src/uts/intel/ia32/sys/trap.h @@ -24,7 +24,7 @@ /* All Rights Reserved */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -58,7 +58,7 @@ extern "C" { #define T_PGFLT 0xe /* #pf page fault */ #define T_EXTERRFLT 0x10 /* #mf x87 FPU error fault */ #define T_ALIGNMENT 0x11 /* #ac alignment check error */ -#define T_MCE 0x12 /* #mc machine check exception (P6 only) */ +#define T_MCE 0x12 /* #mc machine check exception */ #define T_SIMDFPE 0x13 /* #xm SSE/SSE exception */ #define T_DBGENTR 0x14 /* debugger entry */ #define T_ENDPERR 0x21 /* emulated extension error flt */ diff --git a/usr/src/uts/intel/mm/Makefile b/usr/src/uts/intel/mm/Makefile index 729ed50b4d..8efea6da29 100644 --- a/usr/src/uts/intel/mm/Makefile +++ b/usr/src/uts/intel/mm/Makefile @@ -22,7 +22,7 @@ # # uts/intel/mm/Makefile # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #pragma ident "%Z%%M% %I% %E% SMI" @@ -51,6 +51,8 @@ CONF_SRCDIR = $(UTSBASE)/common/io # include $(UTSBASE)/intel/Makefile.intel +CPPFLAGS += -I$(UTSBASE)/i86pc + # # Define targets # diff --git a/usr/src/uts/intel/os/driver_aliases b/usr/src/uts/intel/os/driver_aliases index d5b27e5095..0e95977131 100644 --- a/usr/src/uts/intel/os/driver_aliases +++ b/usr/src/uts/intel/os/driver_aliases @@ -26,3 +26,5 @@ mpt "pci1000,50" mpt "pci1000,56" mpt "pci1000,58" ibd "ib.ipib" +mc-amd "pci1022,1101" +mc-amd "pci1022,1102" diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major index 72b3a3ab6f..6ee2d75b26 100644 --- a/usr/src/uts/intel/os/name_to_major +++ b/usr/src/uts/intel/os/name_to_major @@ -119,4 +119,5 @@ zfs 182 npe 183 pcie_pci 184 kssl 185 +mc-amd 186 did 239 diff --git a/usr/src/uts/intel/sys/Makefile b/usr/src/uts/intel/sys/Makefile index 31cb0ddb03..de2867be84 100644 --- a/usr/src/uts/intel/sys/Makefile +++ b/usr/src/uts/intel/sys/Makefile @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -55,6 +55,11 @@ HDRS = \ machlock.h \ machsig.h \ machtypes.h \ + mc.h \ + mc_amd.h \ + mca_amd.h \ + mca_x86.h \ + memtest.h \ mii.h \ miipriv.h \ mmu.h \ diff --git a/usr/src/uts/intel/sys/archsystm.h b/usr/src/uts/intel/sys/archsystm.h index 76c33e8c05..5ed70f0cb2 100644 --- a/usr/src/uts/intel/sys/archsystm.h +++ b/usr/src/uts/intel/sys/archsystm.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -60,6 +60,8 @@ extern void tenmicrosec(void); extern void restore_int_flag(int); extern int clear_int_flag(void); +extern void int3(void); +extern void int18(void); extern void int20(void); #if defined(__amd64) diff --git a/usr/src/uts/intel/sys/controlregs.h b/usr/src/uts/intel/sys/controlregs.h index b0001c62be..5863b0c201 100644 --- a/usr/src/uts/intel/sys/controlregs.h +++ b/usr/src/uts/intel/sys/controlregs.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -145,7 +145,8 @@ extern "C" { #define MSR_AMD_HWCR 0xc0010015 -#define AMD_HWCR_FFDIS 0x40 /* set to disable TLB Flush Filter */ +#define AMD_HWCR_FFDIS 0x00040 /* disable TLB Flush Filter */ +#define AMD_HWCR_MCI_STATUS_WREN 0x40000 /* enable write of MCi_STATUS */ /* AMD's NorthBridge Config MSR, SHOULD ONLY BE WRITTEN TO BY BIOS */ diff --git a/usr/src/uts/intel/sys/fm/cpu/AMD.h b/usr/src/uts/intel/sys/fm/cpu/AMD.h new file mode 100644 index 0000000000..bb7aa427e5 --- /dev/null +++ b/usr/src/uts/intel/sys/fm/cpu/AMD.h @@ -0,0 +1,223 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_FM_CPU_AMD_H +#define _SYS_FM_CPU_AMD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ereport class subcategory for AMD processors */ +#define FM_EREPORT_CPU_AMD "amd" + +/* + * Ereport payload definitions + */ +#define FM_EREPORT_PAYLOAD_NAME_BANK_STAT "bank-status" +#define FM_EREPORT_PAYLOAD_NAME_BANK_NUM "bank-number" +#define FM_EREPORT_PAYLOAD_NAME_ADDR "addr" +#define FM_EREPORT_PAYLOAD_NAME_ADDR_VALID "addr-valid" +#define FM_EREPORT_PAYLOAD_NAME_SYND "syndrome" +#define FM_EREPORT_PAYLOAD_NAME_SYND_TYPE "syndrome-type" +#define FM_EREPORT_PAYLOAD_NAME_IP "ip" +#define FM_EREPORT_PAYLOAD_NAME_PRIV "privileged" +#define FM_EREPORT_PAYLOAD_NAME_RESOURCE "resource" + +#define FM_EREPORT_PAYLOAD_FLAG_BANK_STAT 0x0000000000000001 +#define FM_EREPORT_PAYLOAD_FLAG_BANK_NUM 0x0000000000000002 +#define FM_EREPORT_PAYLOAD_FLAG_ADDR 0x0000000000000004 +#define FM_EREPORT_PAYLOAD_FLAG_ADDR_VALID 0x0000000000000008 +#define FM_EREPORT_PAYLOAD_FLAG_SYND 0x0000000000000010 +#define FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE 0x0000000000000020 +#define FM_EREPORT_PAYLOAD_FLAG_IP 0x0000000000000040 +#define FM_EREPORT_PAYLOAD_FLAG_PRIV 0x0000000000000080 +#define FM_EREPORT_PAYLOAD_FLAG_RESOURCE 0x0000000000000100 +#define FM_EREPORT_PAYLOAD_FLAG_STACK 0x0000000000000200 + +#define FM_EREPORT_PAYLOAD_FLAGS_BANK \ + (FM_EREPORT_PAYLOAD_FLAG_BANK_STAT | FM_EREPORT_PAYLOAD_FLAG_BANK_NUM) +#define FM_EREPORT_PAYLOAD_FLAGS_ADDR \ + (FM_EREPORT_PAYLOAD_FLAG_ADDR | FM_EREPORT_PAYLOAD_FLAG_ADDR_VALID) +#define FM_EREPORT_PAYLOAD_FLAGS_SYND \ + (FM_EREPORT_PAYLOAD_FLAG_SYND | FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE) +#define FM_EREPORT_PAYLOAD_FLAGS_RESOURCE \ + (FM_EREPORT_PAYLOAD_FLAG_RESOURCE) +#define FM_EREPORT_PAYLOAD_FLAGS_COMMON \ + (FM_EREPORT_PAYLOAD_FLAGS_BANK | FM_EREPORT_PAYLOAD_FLAG_IP | \ + FM_EREPORT_PAYLOAD_FLAG_PRIV) +#define FM_EREPORT_PAYLOAD_FLAGS_NB \ + (FM_EREPORT_PAYLOAD_FLAG_STACK) + +#define FM_EREPORT_PAYLOAD_FLAGS_1(f1) \ + (FM_EREPORT_PAYLOAD_FLAGS_COMMON | FM_EREPORT_PAYLOAD_FLAGS_##f1) +#define FM_EREPORT_PAYLOAD_FLAGS_2(f1, f2) \ + (FM_EREPORT_PAYLOAD_FLAGS_COMMON | FM_EREPORT_PAYLOAD_FLAGS_##f1 | \ + FM_EREPORT_PAYLOAD_FLAGS_##f2) +#define FM_EREPORT_PAYLOAD_FLAGS_3(f1, f2, f3) \ + (FM_EREPORT_PAYLOAD_FLAGS_COMMON | FM_EREPORT_PAYLOAD_FLAGS_##f1 | \ + FM_EREPORT_PAYLOAD_FLAGS_##f2 | FM_EREPORT_PAYLOAD_FLAGS_##f3) + +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_SYS_ECC1 \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_L2_ECC1 \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_SYS_ECCM \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_L2_ECCM \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_DATA_ECC1 \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_DATA_ECC1_UC \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_DATA_ECCM \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_TAG_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_STAG_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_L1TLB_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_L2TLB_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_INF_SYS_ECC1 \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_INF_L2_ECC1 \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_INF_SYS_ECCM \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_INF_L2_ECCM \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_DATA_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_TAG_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_STAG_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_L1TLB_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_L2TLB_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_RDDE \ + FM_EREPORT_PAYLOAD_FLAGS_COMMON + +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_L2D_ECC1 \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_L2D_ECCM \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_L2T_PAR \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_L2T_ECC1 \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_L2T_ECCM \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_S_RDE \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_S_ECC1 \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_S_ECCM \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) + +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_LS_S_RDE \ + FM_EREPORT_PAYLOAD_FLAGS_COMMON + +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_MEM_CE \ + FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_MEM_UE \ + FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_HT_CRC \ + FM_EREPORT_PAYLOAD_FLAGS_COMMON +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_HT_SYNC \ + FM_EREPORT_PAYLOAD_FLAGS_COMMON +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_MA \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, NB) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_TA \ + FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, NB) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_GART_WALK \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_RMW \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_WDOG \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + +#define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_UNKNOWN \ + FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + +#define FM_EREPORT_CPU_AMD_DC_INF_SYS_ECC1 "dc.inf_sys_ecc1" +#define FM_EREPORT_CPU_AMD_DC_INF_SYS_ECCM "dc.inf_sys_eccm" +#define FM_EREPORT_CPU_AMD_DC_INF_L2_ECC1 "dc.inf_l2_ecc1" +#define FM_EREPORT_CPU_AMD_DC_INF_L2_ECCM "dc.inf_l2_eccm" +#define FM_EREPORT_CPU_AMD_DC_DATA_ECC1 "dc.data_ecc1" +#define FM_EREPORT_CPU_AMD_DC_DATA_ECC1_UC "dc.data_ecc1_uc" +#define FM_EREPORT_CPU_AMD_DC_DATA_ECCM "dc.data_eccm" +#define FM_EREPORT_CPU_AMD_DC_TAG_PAR "dc.tag_par" +#define FM_EREPORT_CPU_AMD_DC_STAG_PAR "dc.stag_par" +#define FM_EREPORT_CPU_AMD_DC_L1TLB_PAR "dc.l1tlb_par" +#define FM_EREPORT_CPU_AMD_DC_L2TLB_PAR "dc.l2tlb_par" + +#define FM_EREPORT_CPU_AMD_IC_INF_SYS_ECC1 "ic.inf_sys_ecc1" +#define FM_EREPORT_CPU_AMD_IC_INF_SYS_ECCM "ic.inf_sys_eccm" +#define FM_EREPORT_CPU_AMD_IC_INF_L2_ECC1 "ic.inf_l2_ecc1" +#define FM_EREPORT_CPU_AMD_IC_INF_L2_ECCM "ic.inf_l2_eccm" +#define FM_EREPORT_CPU_AMD_IC_DATA_PAR "ic.data_par" +#define FM_EREPORT_CPU_AMD_IC_TAG_PAR "ic.tag_par" +#define FM_EREPORT_CPU_AMD_IC_STAG_PAR "ic.stag_par" +#define FM_EREPORT_CPU_AMD_IC_L1TLB_PAR "ic.l1tlb_par" +#define FM_EREPORT_CPU_AMD_IC_L2TLB_PAR "ic.l2tlb_par" +#define FM_EREPORT_CPU_AMD_IC_RDDE "ic.rdde" + +#define FM_EREPORT_CPU_AMD_BU_L2D_ECC1 "bu.l2d_ecc1" +#define FM_EREPORT_CPU_AMD_BU_L2D_ECCM "bu.l2d_eccm" +#define FM_EREPORT_CPU_AMD_BU_L2T_PAR "bu.l2t_par" +#define FM_EREPORT_CPU_AMD_BU_L2T_ECC1 "bu.l2t_ecc1" +#define FM_EREPORT_CPU_AMD_BU_L2T_ECCM "bu.l2t_eccm" +#define FM_EREPORT_CPU_AMD_BU_S_RDE "bu.s_rde" +#define FM_EREPORT_CPU_AMD_BU_S_ECC1 "bu.s_ecc1" +#define FM_EREPORT_CPU_AMD_BU_S_ECCM "bu.s_eccm" + +#define FM_EREPORT_CPU_AMD_LS_S_RDE "ls.s_rde" + +#define FM_EREPORT_CPU_AMD_NB_MEM_CE "nb.mem_ce" +#define FM_EREPORT_CPU_AMD_NB_MEM_UE "nb.mem_ue" +#define FM_EREPORT_CPU_AMD_NB_HT_CRC "nb.ht_crc" +#define FM_EREPORT_CPU_AMD_NB_HT_SYNC "nb.ht_sync" +#define FM_EREPORT_CPU_AMD_NB_MA "nb.ma" +#define FM_EREPORT_CPU_AMD_NB_TA "nb.ta" +#define FM_EREPORT_CPU_AMD_NB_GART_WALK "nb.gart_walk" +#define FM_EREPORT_CPU_AMD_NB_RMW "nb.rmw" +#define FM_EREPORT_CPU_AMD_NB_WDOG "nb.wdog" + +#define FM_EREPORT_CPU_AMD_UNKNOWN "unknown" + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FM_CPU_AMD_H */ diff --git a/usr/src/uts/intel/sys/mc.h b/usr/src/uts/intel/sys/mc.h new file mode 100644 index 0000000000..416f323c86 --- /dev/null +++ b/usr/src/uts/intel/sys/mc.h @@ -0,0 +1,80 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_MC_H +#define _SYS_MC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Public interfaces exposed by the memory controller driver + */ + +#include <sys/cpuvar.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define MC_UNUM_NAMLEN 192 +#define MC_UNUM_NDIMM 8 + +typedef struct mc_unum { + int unum_board; + int unum_chip; + int unum_mc; + int unum_cs; + uint64_t unum_offset; + int unum_dimms[MC_UNUM_NDIMM]; +} mc_unum_t; + +#define MC_AMD_DEV_OFFSET 24 /* node ID + offset == PCI dev num */ + +#define MC_IOC (0x4d43 << 16) +#define MC_IOC_SNAPSHOT_INFO (MC_IOC | 1) +#define MC_IOC_SNAPSHOT (MC_IOC | 2) + +/* + * Prior to requesting a copy of the snapshot, consumers are advised to request + * information regarding the snapshot. An mc_snapshot_info_t will be returned, + * containing the snapshot size as well as the snapshot generation number. Note + * that, due to the potentially dynamic nature of the system, the snapshot may + * change at any time. As such, the information in the mc_snapshot_info_t may + * be out of date by the time it is used. The generation number is used to + * track snapshot changes. That is, the generation number will be updated each + * time the source data for the snapshot is updated. The consumer should not + * attach any meaning to the magnitude of a generation number change, and pay + * attention only to the fact that the number has changed. + */ +typedef struct mc_snapshot_info { + uint32_t mcs_size; /* snapshot size */ + uint_t mcs_gen; /* snapshot generation number */ +} mc_snapshot_info_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_MC_H */ diff --git a/usr/src/uts/intel/sys/mc_amd.h b/usr/src/uts/intel/sys/mc_amd.h new file mode 100644 index 0000000000..fba266b14f --- /dev/null +++ b/usr/src/uts/intel/sys/mc_amd.h @@ -0,0 +1,195 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MC_AMD_H +#define _MC_AMD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Definitions describing various memory controller constant properties and + * the structure of configuration registers. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Configuration constants + */ +#define MC_CHIP_NDIMM 8 /* max dimms per MC */ +#define MC_CHIP_NCS 8 /* number of chip-selects per MC */ +#define MC_CHIP_DIMMRANKMAX 4 /* largest number of ranks per dimm */ +#define MC_CHIP_DIMMPERCS 2 /* max number of dimms per cs */ +#define MC_CHIP_DIMMPAIR(csnum) (csnum / MC_CHIP_DIMMPERCS) + +/* + * Encoding of chip version variations that we need to distinguish + */ +#define MC_REV_UNKNOWN -1u /* unknown AMD revision */ +#define MC_REV_PRE_D 0 /* B/C/CG */ +#define MC_REV_D_E 1 /* D or E */ +#define MC_REV_F 2 /* F */ + +/* + * BKDG 3.29 section 3.4.4.1 - DRAM base i registers + */ +#define MC_AM_DB_DRAMBASE_MASK 0xffff0000 +#define MC_AM_DB_DRAMBASE_LSHFT 8 +#define MC_AM_DB_DRAMBASE(regval) \ + (((uint64_t)(regval) & MC_AM_DB_DRAMBASE_MASK) << \ + MC_AM_DB_DRAMBASE_LSHFT) +#define MC_AM_DB_INTLVEN_MASK 0x00000700 +#define MC_AM_DB_INTLVEN_SHIFT 8 +#define MC_AM_DB_WE 0x00000002 +#define MC_AM_DB_RE 0x00000001 + +/* + * BKDG 3.29 section 3.4.4.2 - DRAM limit i registers + */ +#define MC_AM_DL_DRAMLIM_MASK 0xffff0000 +#define MC_AM_DL_DRAMLIM_SHIFT 16 +#define MC_AM_DL_DRAMLIM_LSHFT 8 +#define MC_AM_DL_DRAMLIM(regval) \ + ((((uint64_t)(regval) & MC_AM_DL_DRAMLIM_MASK) << \ + MC_AM_DL_DRAMLIM_LSHFT) | ((regval) ? \ + ((1 << (MC_AM_DL_DRAMLIM_SHIFT + MC_AM_DL_DRAMLIM_LSHFT)) - 1) : 0)) +#define MC_AM_DL_INTLVSEL_MASK 0x00000700 +#define MC_AM_DL_INTLVSEL_SHIFT 8 +#define MC_AM_DL_DSTNODE_MASK 0x00000007 + +/* + * BKDG 3.29 section 3.5.4 - DRAM CS Base Address Registers. + * + * MC_DC_CSB_CSBASE combines the BaseAddrHi and BaseAddrLo into a single + * uint64_t, shifting them into the dram address bits they describe. + */ +#define MC_DC_CSB_BASEHI_MASK 0xffe00000 +#define MC_DC_CSB_BASEHI_LSHFT 4 + +#define MC_DC_CSB_BASELO_MASK 0x0000fe00 +#define MC_DC_CSB_BASELO_LSHFT 4 + +#define MC_DC_CSB_CSBASE(regval) \ + ((((uint64_t)(regval) & MC_DC_CSB_BASEHI_MASK) << \ + MC_DC_CSB_BASEHI_LSHFT) | (((uint64_t)(regval) & \ + MC_DC_CSB_BASELO_MASK) << MC_DC_CSB_BASELO_LSHFT)) + +#define MC_DC_CSB_CSBE 0x00000001 + +/* + * BKDG 3.29 section 3.5.5 - DRAM CS Mask Registers. + * + * MC_DC_CSM_CSMASK combines the AddrMaskHi and AddrMaskLo into a single + * uint64_t, shifting them into the dram address bit positions they mask. + * It also fills the gaps between high and low mask and below the low mask. + * MC_DC_CSM_UNMASKED_BITS indicates the number of high dram address bits + * above MC_DC_CSM_MASKHI_HIBIT that cannot be masked. + */ +#define MC_DC_CSM_MASKHI_MASK 0x3fe00000 +#define MC_DC_CSM_MASKHI_LSHFT 4 +#define MC_DC_CSM_MASKHI_LOBIT 25 +#define MC_DC_CSM_MASKHI_HIBIT 33 + +#define MC_DC_CSM_MASKLO_MASK 0x0000fe00 +#define MC_DC_CSM_MASKLO_LOBIT 13 +#define MC_DC_CSM_MASKLO_HIBIT 19 +#define MC_DC_CSM_MASKLO_LSHFT 4 + +#define MC_DC_CSM_MASKFILL 0x1f01fff /* [24:20] and [12:0] */ + +#define MC_DC_CSM_UNMASKED_BITS 2 + +#define MC_DC_CSM_CSMASK(regval) \ + ((((uint64_t)(regval) & MC_DC_CSM_MASKHI_MASK) << \ + MC_DC_CSM_MASKHI_LSHFT) | (((uint64_t)(regval) & \ + MC_DC_CSM_MASKLO_MASK) << MC_DC_CSM_MASKLO_LSHFT) | \ + MC_DC_CSM_MASKFILL) + +/* + * BKDG 3.29 section 3.5.6 - DRAM Bank Address Mapping Register + */ +#define MC_DC_BAM_CSBANK_MASK 0x0000000f +#define MC_DC_BAM_CSBANK_SHIFT 4 +#define MC_DC_BAM_CSBANK_SWIZZLE 0x40000000 + +/* + * BKDG 3.29 section 3.4.8 - DRAM Hole register, revs E and later + */ +#define MC_DC_HOLE_VALID 0x00000001 +#define MC_DC_HOLE_OFFSET_MASK 0x0000ff00 +#define MC_DC_HOLE_OFFSET_LSHIFT 16 + +/* + * BKDG 3.29 section 3.5.11 - DRAM configuration high and low registers. + * The following defines may be applied to a uint64_t made by + * concatenating those two 32-bit registers. + */ +#define MC_DC_DCFG_DLL_DIS 0x0000000000000001 +#define MC_DC_DCFG_D_DRV 0x0000000000000002 +#define MC_DC_DCFG_QFC_EN 0x0000000000000004 +#define MC_DC_DCFG_DISDQSYS 0x0000000000000008 +#define MC_DC_DCFG_BURST2OPT 0x0000000000000020 +#define MC_DC_DCFG_MOD64BITMUX 0x0000000000000040 +#define MC_DC_DCFG_PWRDWNTRIEN 0x0000000000000080 /* >= rev E */ +#define MC_DC_DCFG_SCRATCHBIT 0x0000000000000080 /* <= rev D */ +#define MC_DC_DCFG_DRAMINIT 0x0000000000000100 +#define MC_DC_DCFG_DUALDIMMEN 0x0000000000000200 +#define MC_DC_DCFG_DRAMENABLE 0x0000000000000400 +#define MC_DC_DCFG_MEMCLRSTATUS 0x0000000000000800 +#define MC_DC_DCFG_ESR 0x0000000000001000 +#define MC_DC_DCFG_SR_S 0x0000000000002000 +#define MC_DC_DCFG_RDWRQBYP_MASK 0x000000000000c000 +#define MC_DC_DCFG_128 0x0000000000010000 +#define MC_DC_DCFG_DIMMECEN 0x0000000000020000 +#define MC_DC_DCFG_UNBUFFDIMM 0x0000000000040000 +#define MC_DC_DCFG_32BYTEEN 0x0000000000080000 +#define MC_DC_DCFG_X4DIMMS_MASK 0x0000000000f00000 +#define MC_DC_DCFG_X4DIMMS_SHIFT 20 +#define MC_DC_DCFG_DISINRCVRS 0x0000000001000000 +#define MC_DC_DCFG_BYPMAX_MASK 0x000000000e000000 +#define MC_DC_DCFG_EN2T 0x0000000010000000 +#define MC_DC_DCFG_UPPERCSMAP 0x0000000020000000 +#define MC_DC_DCFG_PWRDOWNCTL_MASK 0x00000000c0000000 +#define MC_DC_DCFG_ASYNCLAT_MASK 0x0000000f00000000 +#define MC_DC_DCFG_RDPREAMBLE_MASK 0x00000f0000000000 +#define MC_DC_DCFG_MEMDQDRVSTREN_MASK 0x0000600000000000 +#define MC_DC_DCFG_DISABLEJITTER 0x0000800000000000 +#define MC_DC_DCFG_ILD_LMT_MASK 0x0007000000000000 +#define MC_DC_DCFG_ECC_EN 0x0008000000000000 +#define MC_DC_DCFG_MEMCLK_MASK 0x0070000000000000 +#define MC_DC_DCFG_MCR 0x0200000000000000 +#define MC_DC_DCFG_MC0_EN 0x0400000000000000 +#define MC_DC_DCFG_MC1_EN 0x0800000000000000 +#define MC_DC_DCFG_MC2_EN 0x1000000000000000 +#define MC_DC_DCFG_MC3_EN 0x2000000000000000 +#define MC_DC_DCFG_ODDDIVISORCORRECT 0x8000000000000000 + +#ifdef __cplusplus +} +#endif + +#endif /* _MC_AMD_H */ diff --git a/usr/src/uts/intel/sys/mca_amd.h b/usr/src/uts/intel/sys/mca_amd.h new file mode 100644 index 0000000000..21524f8713 --- /dev/null +++ b/usr/src/uts/intel/sys/mca_amd.h @@ -0,0 +1,418 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_MCA_AMD_H +#define _SYS_MCA_AMD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Constants the Memory Check Architecture as implemented on AMD CPUs. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define AMD_MSR_MCG_CAP 0x179 +#define AMD_MSR_MCG_STATUS 0x17a +#define AMD_MSR_MCG_CTL 0x17b + +#define AMD_MCA_BANK_DC 0 /* Data Cache */ +#define AMD_MCA_BANK_IC 1 /* Instruction Cache */ +#define AMD_MCA_BANK_BU 2 /* Bus Unit */ +#define AMD_MCA_BANK_LS 3 /* Load/Store Unit */ +#define AMD_MCA_BANK_NB 4 /* Northbridge */ +#define AMD_MCA_BANK_COUNT 5 + +#define AMD_MSR_DC_CTL 0x400 +#define AMD_MSR_DC_MASK 0xc0010044 +#define AMD_MSR_DC_STATUS 0x401 +#define AMD_MSR_DC_ADDR 0x402 + +#define AMD_MSR_IC_CTL 0x404 +#define AMD_MSR_IC_MASK 0xc0010045 +#define AMD_MSR_IC_STATUS 0x405 +#define AMD_MSR_IC_ADDR 0x406 + +#define AMD_MSR_BU_CTL 0x408 +#define AMD_MSR_BU_MASK 0xc0010046 +#define AMD_MSR_BU_STATUS 0x409 +#define AMD_MSR_BU_ADDR 0x40a + +#define AMD_MSR_LS_CTL 0x40c +#define AMD_MSR_LS_MASK 0xc0010047 +#define AMD_MSR_LS_STATUS 0x40d +#define AMD_MSR_LS_ADDR 0x40e + +#define AMD_MSR_NB_CTL 0x410 +#define AMD_MSR_NB_MASK 0xc0010048 +#define AMD_MSR_NB_STATUS 0x411 +#define AMD_MSR_NB_ADDR 0x412 + +#define AMD_MCG_EN_DC 0x01 +#define AMD_MCG_EN_IC 0x02 +#define AMD_MCG_EN_BU 0x04 +#define AMD_MCG_EN_LS 0x08 +#define AMD_MCG_EN_NB 0x10 +#define AMD_MCG_EN_ALL \ + (AMD_MCG_EN_DC | AMD_MCG_EN_IC | AMD_MCG_EN_BU | AMD_MCG_EN_LS | \ + AMD_MCG_EN_NB) + +/* + * Data Cache (DC) bank error-detection enabling bits and CTL register + * initializer value. + */ + +#define AMD_DC_EN_ECCI 0x00000001ULL +#define AMD_DC_EN_ECCM 0x00000002ULL +#define AMD_DC_EN_DECC 0x00000004ULL +#define AMD_DC_EN_DMTP 0x00000008ULL +#define AMD_DC_EN_DSTP 0x00000010ULL +#define AMD_DC_EN_L1TP 0x00000020ULL +#define AMD_DC_EN_L2TP 0x00000040ULL + +#define AMD_DC_CTL_INIT \ + (AMD_DC_EN_ECCI | AMD_DC_EN_ECCM | AMD_DC_EN_DECC | AMD_DC_EN_DMTP | \ + AMD_DC_EN_DSTP | AMD_DC_EN_L1TP | AMD_DC_EN_L2TP) + +/* + * Instruction Cache (IC) bank error-detection enabling bits and CTL register + * initializer value. + * + * The Northbridge will handle Read Data errors. Our initializer will enable + * all but the RDDE detector. + */ + +#define AMD_IC_EN_ECCI 0x00000001ULL +#define AMD_IC_EN_ECCM 0x00000002ULL +#define AMD_IC_EN_IDP 0x00000004ULL +#define AMD_IC_EN_IMTP 0x00000008ULL +#define AMD_IC_EN_ISTP 0x00000010ULL +#define AMD_IC_EN_L1TP 0x00000020ULL +#define AMD_IC_EN_L2TP 0x00000040ULL +#define AMD_IC_EN_RDDE 0x00000200ULL + +#define AMD_IC_CTL_INIT \ + (AMD_IC_EN_ECCI | AMD_IC_EN_ECCM | AMD_IC_EN_IDP | AMD_IC_EN_IMTP | \ + AMD_IC_EN_ISTP | AMD_IC_EN_L1TP | AMD_IC_EN_L2TP) + +/* + * Bus Unit (BU) bank error-detection enabling bits and CTL register + * initializer value. + * + * The Northbridge will handle Read Data errors. Our initializer will enable + * all but the S_RDE_* detectors. + */ + +#define AMD_BU_EN_S_RDE_HP 0x00000001ULL +#define AMD_BU_EN_S_RDE_TLB 0x00000002ULL +#define AMD_BU_EN_S_RDE_ALL 0x00000004ULL +#define AMD_BU_EN_S_ECC1_TLB 0x00000008ULL +#define AMD_BU_EN_S_ECC1_HP 0x00000010ULL +#define AMD_BU_EN_S_ECCM_TLB 0x00000020ULL +#define AMD_BU_EN_S_ECCM_HP 0x00000040ULL +#define AMD_BU_EN_L2T_PAR_ICDC 0x00000080ULL +#define AMD_BU_EN_L2T_PAR_TLB 0x00000100ULL +#define AMD_BU_EN_L2T_PAR_SNP 0x00000200ULL +#define AMD_BU_EN_L2T_PAR_CPB 0x00000400ULL +#define AMD_BU_EN_L2T_PAR_SCR 0x00000800ULL +#define AMD_BU_EN_L2D_ECC1_TLB 0x00001000ULL +#define AMD_BU_EN_L2D_ECC1_SNP 0x00002000ULL +#define AMD_BU_EN_L2D_ECC1_CPB 0x00004000ULL +#define AMD_BU_EN_L2D_ECCM_TLB 0x00008000ULL +#define AMD_BU_EN_L2D_ECCM_SNP 0x00010000ULL +#define AMD_BU_EN_L2D_ECCM_CPB 0x00020000ULL +#define AMD_BU_EN_L2T_ECC1_SCR 0x00040000ULL +#define AMD_BU_EN_L2T_ECCM_SCR 0x00080000ULL + +#define AMD_BU_CTL_INIT \ + (AMD_BU_EN_S_ECC1_TLB | AMD_BU_EN_S_ECC1_HP | \ + AMD_BU_EN_S_ECCM_TLB | AMD_BU_EN_S_ECCM_HP | \ + AMD_BU_EN_L2T_PAR_ICDC | AMD_BU_EN_L2T_PAR_TLB | \ + AMD_BU_EN_L2T_PAR_SNP | AMD_BU_EN_L2T_PAR_CPB | \ + AMD_BU_EN_L2T_PAR_SCR | AMD_BU_EN_L2D_ECC1_TLB | \ + AMD_BU_EN_L2D_ECC1_SNP | AMD_BU_EN_L2D_ECC1_CPB | \ + AMD_BU_EN_L2D_ECCM_TLB | AMD_BU_EN_L2D_ECCM_SNP | \ + AMD_BU_EN_L2D_ECCM_CPB | AMD_BU_EN_L2T_ECC1_SCR | \ + AMD_BU_EN_L2T_ECCM_SCR) + +/* + * Load/Store (LS) bank error-detection enabling bits and CTL register + * initializer value. + * + * The Northbridge will handle Read Data errors. That's the only type of + * error the LS unit can detect at present, so we won't be enabling any + * LS detectors. + */ + +#define AMD_LS_EN_S_RDE_S 0x00000001ULL +#define AMD_LS_EN_S_RDE_L 0x00000002ULL + +#define AMD_LS_CTL_INIT 0ULL + +/* + * The Northbridge (NB) is configured using both the standard MCA CTL register + * and a NB-specific configuration register (NB CFG). The AMD_NB_EN_* macros + * are the detector enabling bits for the NB MCA CTL register. The + * AMD_NB_CFG_* bits are for the NB CFG register. + * + * The CTL register can be initialized statically, but portions of the NB CFG + * register must be initialized based on the current machine's configuration. + * + * The MCA NB Control Register maps to MC4_CTL[31:0]. + * + */ +#define AMD_NB_EN_CORRECC 0x00000001 +#define AMD_NB_EN_UNCORRECC 0x00000002 +#define AMD_NB_EN_CRCERR0 0x00000004 +#define AMD_NB_EN_CRCERR1 0x00000008 +#define AMD_NB_EN_CRCERR2 0x00000010 +#define AMD_NB_EN_SYNCPKT0 0x00000020 +#define AMD_NB_EN_SYNCPKT1 0x00000040 +#define AMD_NB_EN_SYNCPKT2 0x00000080 +#define AMD_NB_EN_MSTRABRT 0x00000100 +#define AMD_NB_EN_TGTABRT 0x00000200 +#define AMD_NB_EN_GARTTBLWK 0x00000400 +#define AMD_NB_EN_ATOMICRMW 0x00000800 +#define AMD_NB_EN_WCHDOGTMR 0x00001000 + +#define AMD_NB_CTL_INIT /* All but GARTTBLWK */ \ + (AMD_NB_EN_CORRECC | AMD_NB_EN_UNCORRECC | \ + AMD_NB_EN_CRCERR0 | AMD_NB_EN_CRCERR1 | AMD_NB_EN_CRCERR2 | \ + AMD_NB_EN_SYNCPKT0 | AMD_NB_EN_SYNCPKT1 | AMD_NB_EN_SYNCPKT2 | \ + AMD_NB_EN_MSTRABRT | AMD_NB_EN_TGTABRT | \ + AMD_NB_EN_ATOMICRMW | AMD_NB_EN_WCHDOGTMR) + +#define AMD_NB_CFG_CPUECCERREN 0x00000001 +#define AMD_NB_CFG_CPURDDATERREN 0x00000002 +#define AMD_NB_CFG_SYNCONUCECCEN 0x00000004 +#define AMD_NB_CFG_SYNCPKTGENDIS 0x00000008 +#define AMD_NB_CFG_SYNCPKTPROPDIS 0x00000010 +#define AMD_NB_CFG_IOMSTABORTDIS 0x00000020 +#define AMD_NB_CFG_CPUERRDIS 0x00000040 +#define AMD_NB_CFG_IOERRDIS 0x00000080 +#define AMD_NB_CFG_WDOGTMRDIS 0x00000100 +#define AMD_NB_CFG_SYNCONWDOGEN 0x00100000 +#define AMD_NB_CFG_SYNCONANYERREN 0x00200000 +#define AMD_NB_CFG_ECCEN 0x00400000 +#define AMD_NB_CFG_CHIPKILLECCEN 0x00800000 +#define AMD_NB_CFG_IORDDATERREN 0x01000000 +#define AMD_NB_CFG_DISPCICFGCPUERRRSP 0x02000000 +#define AMD_NB_CFG_NBMCATOMSTCPUEN 0x08000000 + +#define AMD_NB_CFG_WDOGTMRCNTSEL_4095 0x00000000 +#define AMD_NB_CFG_WDOGTMRCNTSEL_2047 0x00000200 +#define AMD_NB_CFG_WDOGTMRCNTSEL_1023 0x00000400 +#define AMD_NB_CFG_WDOGTMRCNTSEL_511 0x00000600 +#define AMD_NB_CFG_WDOGTMRCNTSEL_255 0x00000800 +#define AMD_NB_CFG_WDOGTMRCNTSEL_127 0x00000a00 +#define AMD_NB_CFG_WDOGTMRCNTSEL_63 0x00000c00 +#define AMD_NB_CFG_WDOGTMRCNTSEL_31 0x00000e00 +#define AMD_NB_CFG_WDOGTMRCNTSEL_MASK 0x00000e00 +#define AMD_NB_CFG_WDOGTMRCNTSEL_SHIFT 9 + +#define AMD_NB_CFG_WDOGTMRBASESEL_1MS 0x00000000 +#define AMD_NB_CFG_WDOGTMRBASESEL_1US 0x00001000 +#define AMD_NB_CFG_WDOGTMRBASESEL_5NS 0x00002000 +#define AMD_NB_CFG_WDOGTMRBASESEL_MASK 0x00003000 +#define AMD_NB_CFG_WDOGTMRBASESEL_SHIFT 12 + +#define AMD_NB_CFG_LDTLINKSEL_MASK 0x0000c000 +#define AMD_NB_CFG_LDTLINKSEL_SHIFT 14 + +#define AMD_NB_CFG_GENCRCERRBYTE0 0x00010000 +#define AMD_NB_CFG_GENCRCERRBYTE1 0x00020000 + +/* Generic bank status register bits */ +#define AMD_BANK_STAT_VALID 0x8000000000000000 +#define AMD_BANK_STAT_OVER 0x4000000000000000 +#define AMD_BANK_STAT_UC 0x2000000000000000 +#define AMD_BANK_STAT_EN 0x1000000000000000 +#define AMD_BANK_STAT_MISCV 0x0800000000000000 +#define AMD_BANK_STAT_ADDRV 0x0400000000000000 +#define AMD_BANK_STAT_PCC 0x0200000000000000 + +#define AMD_BANK_STAT_CECC 0x0000400000000000 +#define AMD_BANK_STAT_UECC 0x0000200000000000 +#define AMD_BANK_STAT_SCRUB 0x0000010000000000 + +#define AMD_BANK_STAT_SYND_MASK 0x007f800000000000 /* syndrome[7:0] */ +#define AMD_BANK_STAT_SYND_SHIFT 47 + +#define AMD_BANK_SYND(stat) \ + (((stat) & AMD_BANK_STAT_SYND_MASK) >> AMD_BANK_STAT_SYND_SHIFT) +#define AMD_BANK_MKSYND(synd) \ + (((uint64_t)(synd) << AMD_BANK_STAT_SYND_SHIFT) & \ + AMD_BANK_STAT_SYND_MASK) + +/* northbridge (NB) status registers */ + +#define AMD_NB_FUNC 3 +#define AMD_NB_REG_CFG 0x44 +#define AMD_NB_REG_STLO 0x48 /* alias: NB_STATUS[0:31] */ +#define AMD_NB_REG_STHI 0x4c /* alias: NB_STATUS[32:63] */ +#define AMD_NB_REG_ADDRLO 0x50 /* alias: NB_ADDR[0:31] */ +#define AMD_NB_REG_ADDRHI 0x54 /* alias: NB_ADDR[32:63] */ + +#define AMD_NB_REG_SCRUBCTL 0x58 +#define AMD_NB_REG_SCRUBADDR_LO 0x5c +#define AMD_NB_REG_SCRUBADDR_HI 0x60 + +#define AMD_NB_STAT_LDTLINK_MASK 0x0000007000000000 +#define AMD_NB_STAT_LDTLINK_SHIFT 4 +#define AMD_NB_STAT_ERRCPU1 0x0000000200000000 +#define AMD_NB_STAT_ERRCPU0 0x0000000100000000 +#define AMD_NB_STAT_CKSYND_MASK 0x00000000ff000000 /* syndrome[15:8] */ +#define AMD_NB_STAT_CKSYND_SHIFT (24 - 8) /* shift [31:24] to [15:8] */ + +#define AMD_NB_STAT_CKSYND(stat) \ + ((((stat) & AMD_NB_STAT_CKSYND_MASK) >> AMD_NB_STAT_CKSYND_SHIFT) | \ + AMD_BANK_SYND((stat))) + +#define AMD_NB_STAT_MKCKSYND(synd) \ + ((((uint64_t)(synd) << AMD_NB_STAT_CKSYND_SHIFT) & \ + AMD_NB_STAT_CKSYND_MASK) | AMD_BANK_MKSYND(synd)) + +#define AMD_ERRCODE_MASK 0x000000000000ffff +#define AMD_ERREXT_MASK 0x00000000000f0000 +#define AMD_ERREXT_SHIFT 16 + +#define AMD_ERRCODE_TT_MASK 0x000c +#define AMD_ERRCODE_TT_SHIFT 2 +#define AMD_ERRCODE_TT_INSTR 0x0 +#define AMD_ERRCODE_TT_DATA 0x1 +#define AMD_ERRCODE_TT_GEN 0x2 + +#define AMD_ERRCODE_LL_MASK 0x0003 +#define AMD_ERRCODE_LL_L0 0x0 +#define AMD_ERRCODE_LL_L1 0x1 +#define AMD_ERRCODE_LL_L2 0x2 +#define AMD_ERRCODE_LL_LG 0x3 + +#define AMD_ERRCODE_R4_MASK 0x00f0 +#define AMD_ERRCODE_R4_SHIFT 4 +#define AMD_ERRCODE_R4_GEN 0x0 +#define AMD_ERRCODE_R4_RD 0x1 +#define AMD_ERRCODE_R4_WR 0x2 +#define AMD_ERRCODE_R4_DRD 0x3 +#define AMD_ERRCODE_R4_DWR 0x4 +#define AMD_ERRCODE_R4_IRD 0x5 +#define AMD_ERRCODE_R4_PREFETCH 0x6 +#define AMD_ERRCODE_R4_EVICT 0x7 +#define AMD_ERRCODE_R4_SNOOP 0x8 + +#define AMD_ERRCODE_PP_MASK 0x0600 +#define AMD_ERRCODE_PP_SHIFT 9 +#define AMD_ERRCODE_PP_SRC 0x0 +#define AMD_ERRCODE_PP_RSP 0x1 +#define AMD_ERRCODE_PP_OBS 0x2 +#define AMD_ERRCODE_PP_GEN 0x3 + +#define AMD_ERRCODE_T_MASK 0x0100 +#define AMD_ERRCODE_T_SHIFT 8 +#define AMD_ERRCODE_T_NONE 0x0 +#define AMD_ERRCODE_T_TIMEOUT 0x1 + +#define AMD_ERRCODE_II_MASK 0x000c +#define AMD_ERRCODE_II_SHIFT 2 +#define AMD_ERRCODE_II_MEM 0x0 +#define AMD_ERRCODE_II_IO 0x2 +#define AMD_ERRCODE_II_GEN 0x3 + +#define AMD_ERRCODE_TLB_BIT 4 +#define AMD_ERRCODE_MEM_BIT 8 +#define AMD_ERRCODE_BUS_BIT 11 + +#define AMD_ERRCODE_TLB_MASK 0xfff0 +#define AMD_ERRCODE_TLB_VAL 0x0010 +#define AMD_ERRCODE_MEM_MASK 0xff00 +#define AMD_ERRCODE_MEM_VAL 0x0100 +#define AMD_ERRCODE_BUS_MASK 0xf800 +#define AMD_ERRCODE_BUS_VAL 0x0800 + +#define AMD_ERRCODE_MKTLB(tt, ll) \ + (AMD_ERRCODE_TLB_VAL | \ + (((tt) << AMD_ERRCODE_TT_SHIFT) & AMD_ERRCODE_TT_MASK) | \ + ((ll) & AMD_ERRCODE_LL_MASK)) +#define AMD_ERRCODE_ISTLB(code) \ + (((code) & AMD_ERRCODE_TLB_MASK) == AMD_ERRCODE_TLB_VAL) + +#define AMD_ERRCODE_MKMEM(r4, tt, ll) \ + (AMD_ERRCODE_MEM_VAL | \ + (((r4) << AMD_ERRCODE_R4_SHIFT) & AMD_ERRCODE_R4_MASK) | \ + (((tt) << AMD_ERRCODE_TT_SHIFT) & AMD_ERRCODE_TT_MASK) | \ + ((ll) & AMD_ERRCODE_LL_MASK)) +#define AMD_ERRCODE_ISMEM(code) \ + (((code) & AMD_ERRCODE_MEM_MASK) == AMD_ERRCODE_MEM_VAL) + +#define AMD_ERRCODE_MKBUS(pp, t, r4, ii, ll) \ + (AMD_ERRCODE_BUS_VAL | \ + (((pp) << AMD_ERRCODE_PP_SHIFT) & AMD_ERRCODE_PP_MASK) | \ + (((t) << AMD_ERRCODE_T_SHIFT) & AMD_ERRCODE_T_MASK) | \ + (((r4) << AMD_ERRCODE_R4_SHIFT) & AMD_ERRCODE_R4_MASK) | \ + (((ii) << AMD_ERRCODE_II_SHIFT) & AMD_ERRCODE_II_MASK) | \ + ((ll) & AMD_ERRCODE_LL_MASK)) +#define AMD_ERRCODE_ISBUS(code) \ + (((code) & AMD_ERRCODE_BUS_MASK) == AMD_ERRCODE_BUS_VAL) + +#define AMD_NB_ADDRLO_MASK 0xfffffff8 +#define AMD_NB_ADDRHI_MASK 0x000000ff + +#define AMD_SYNDTYPE_ECC 0 +#define AMD_SYNDTYPE_CHIPKILL 1 + +#define AMD_NB_SCRUBCTL_DRAM_MASK 0x0000001f +#define AMD_NB_SCRUBCTL_DRAM_SHIFT 0 +#define AMD_NB_SCRUBCTL_L2_MASK 0x00001f00 +#define AMD_NB_SCRUBCTL_L2_SHIFT 8 +#define AMD_NB_SCRUBCTL_DC_MASK 0x001f0000 +#define AMD_NB_SCRUBCTL_DC_SHIFT 16 + +#define AMD_NB_SCRUBCTL_RATE_NONE 0 +#define AMD_NB_SCRUBCTL_RATE_MAX 0x16 + +#define AMD_NB_SCRUBADDR_LO_MASK 0xffffffc0 +#define AMD_NB_SCRUBADDR_LO_SCRUBREDIREN 0x1 +#define AMD_NB_SCRUBADDR_HI_MASK 0x000000ff + +#define AMD_NB_SCRUBADDR_MKLO(addr) \ + ((addr) & AMD_NB_SCRUBADDR_LO_MASK) + +#define AMD_NB_SCRUBADDR_MKHI(addr) \ + (((addr) >> 32) & AMD_NB_SCRUBADDR_HI_MASK) + +#define AMD_NB_MKSCRUBCTL(dc, l2, dr) ( \ + (((dc) << AMD_NB_SCRUBCTL_DC_SHIFT) & AMD_NB_SCRUBCTL_DC_MASK) | \ + (((l2) << AMD_NB_SCRUBCTL_L2_SHIFT) & AMD_NB_SCRUBCTL_L2_MASK) | \ + (((dr) << AMD_NB_SCRUBCTL_DRAM_SHIFT) & AMD_NB_SCRUBCTL_DRAM_MASK)) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_MCA_AMD_H */ diff --git a/usr/src/uts/intel/sys/mca_x86.h b/usr/src/uts/intel/sys/mca_x86.h new file mode 100644 index 0000000000..78066bdc63 --- /dev/null +++ b/usr/src/uts/intel/sys/mca_x86.h @@ -0,0 +1,126 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_MCA_X86_H +#define _SYS_MCA_X86_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Constants for the Memory Check Architecture as implemented on generic x86 + * CPUs. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Intel has defined a number of MSRs as part of the IA32 architecture. The + * MCG registers are part of that set, as are the first four banks (0-3) as + * implemented by the P4 processor. Bank MSRs were laid out slightly + * differently on the P6 family of processors, and thus have their own #defines + * following the architecture-generic ones. + */ +#define IA32_MSR_MCG_CAP 0x179 +#define IA32_MSR_MCG_STATUS 0x17a +#define IA32_MSR_MCG_CTL 0x17b + +#define MCG_CAP_COUNT_MASK 0x000000ffULL +#define MCG_CAP_CTL_P 0x00000100ULL +#define MCG_CAP_EXT_P 0x00000200ULL +#define MCG_CAP_EXT_CNT_MASK 0x00ff0000ULL +#define MCG_CAP_EXT_CNT_SHIFT 16 + +#define MCG_STATUS_RIPV 0x01 +#define MCG_STATUS_EIPV 0x02 +#define MCG_STATUS_MCIP 0x04 + +#define IA32_MSR_MC0_CTL 0x400 +#define IA32_MSR_MC0_STATUS 0x401 +#define IA32_MSR_MC0_ADDR 0x402 +#define IA32_MSR_MC0_MISC 0x403 + +#define IA32_MSR_MC1_CTL 0x404 +#define IA32_MSR_MC1_STATUS 0x405 +#define IA32_MSR_MC1_ADDR 0x406 +#define IA32_MSR_MC1_MISC 0x407 + +#define IA32_MSR_MC2_CTL 0x408 +#define IA32_MSR_MC2_STATUS 0x409 +#define IA32_MSR_MC2_ADDR 0x40a +#define IA32_MSR_MC2_MISC 0x40b + +#define IA32_MSR_MC3_CTL 0x40c +#define IA32_MSR_MC3_STATUS 0x40d +#define IA32_MSR_MC3_ADDR 0x40e +#define IA32_MSR_MC3_MISC 0x40f + +#define MSR_MC_STATUS_VAL 0x8000000000000000ULL +#define MSR_MC_STATUS_O 0x4000000000000000ULL +#define MSR_MC_STATUS_UC 0x2000000000000000ULL +#define MSR_MC_STATUS_EN 0x1000000000000000ULL +#define MSR_MC_STATUS_MISCV 0x0800000000000000ULL +#define MSR_MC_STATUS_ADDRV 0x0400000000000000ULL +#define MSR_MC_STATUS_PCC 0x0200000000000000ULL +#define MSR_MC_STATUS_OTHER_MASK 0x01ffffff00000000ULL +#define MSR_MC_STATUS_OTHER_SHIFT 32 +#define MSR_MC_STATUS_MSERR_MASK 0x00000000ffff0000ULL +#define MSR_MC_STATUS_MSERR_SHIFT 16 +#define MSR_MC_STATUS_MCAERR_MASK 0x000000000000ffffULL + +/* + * P6 MCA bank MSRs. Note that the ordering is 0, 1, 2, *4*, 3. Yes, really. + */ +#define P6_MSR_MC0_CTL 0x400 +#define P6_MSR_MC0_STATUS 0x401 +#define P6_MSR_MC0_ADDR 0x402 +#define P6_MSR_MC0_MISC 0x403 + +#define P6_MSR_MC1_CTL 0x404 +#define P6_MSR_MC1_STATUS 0x405 +#define P6_MSR_MC1_ADDR 0x406 +#define P6_MSR_MC1_MISC 0x407 + +#define P6_MSR_MC2_CTL 0x408 +#define P6_MSR_MC2_STATUS 0x409 +#define P6_MSR_MC2_ADDR 0x40a +#define P6_MSR_MC2_MISC 0x40b + +#define P6_MSR_MC4_CTL 0x40c +#define P6_MSR_MC4_STATUS 0x40d +#define P6_MSR_MC4_ADDR 0x40e +#define P6_MSR_MC4_MISC 0x40f + +#define P6_MSR_MC3_CTL 0x410 +#define P6_MSR_MC3_STATUS 0x411 +#define P6_MSR_MC3_ADDR 0x412 +#define P6_MSR_MC3_MISC 0x413 + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_MCA_X86_H */ diff --git a/usr/src/uts/intel/sys/memtest.h b/usr/src/uts/intel/sys/memtest.h new file mode 100644 index 0000000000..8e9d4fc8ef --- /dev/null +++ b/usr/src/uts/intel/sys/memtest.h @@ -0,0 +1,126 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MEMTEST_H +#define _MEMTEST_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Interfaces for the memory error injection driver (memtest). This driver is + * intended for use only by mtst. + */ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define MEMTEST_DEVICE "/devices/pseudo/memtest@0:memtest" + +#define MEMTEST_VERSION 1 + +#define MEMTESTIOC ('M' << 8) +#define MEMTESTIOC_INQUIRE (MEMTESTIOC | 0) +#define MEMTESTIOC_CONFIG (MEMTESTIOC | 1) +#define MEMTESTIOC_INJECT (MEMTESTIOC | 2) +#define MEMTESTIOC_MEMREQ (MEMTESTIOC | 3) +#define MEMTESTIOC_MEMREL (MEMTESTIOC | 4) + +#define MEMTEST_F_DEBUG 0x1 + +typedef struct memtest_inq { + uint_t minq_version; /* [out] driver version */ +} memtest_inq_t; + +/* + * Used by the userland injector to request a memory region from the driver. + * This region (or a portion thereof) will be used for the error. The caller + * is expected to fill in the restrictions, if any, that are to be applied to + * the region. If the driver cannot allocate a region that meets the supplied + * restrictions, the ioctl will fail. Upon success, all members will be filled + * in with values that reflect the allocated area. + */ + +#define MEMTEST_MEMREQ_MAXNUM 5 /* maximum number of open allocations */ +#define MEMTEST_MEMREQ_MAXSIZE 8192 /* maximum size of each allocation */ + +#define MEMTEST_MEMREQ_UNSPEC ((uint64_t)-1) + +typedef struct memtest_memreq { + int mreq_cpuid; /* cpu restriction (opt, -1 if unset) */ + uint32_t mreq_size; /* size of allocation */ + uint64_t mreq_vaddr; /* [out] VA of allocation */ + uint64_t mreq_paddr; /* [out] PA of allocation */ +} memtest_memreq_t; + +/* + * Arrays of statements are passed to the memtest driver for error injection. + */ +#define MEMTEST_INJECT_MAXNUM 20 /* Max # of stmts per INJECT ioctl */ + +#define MEMTEST_INJ_STMT_MSR 0x1 /* an MSR to be written */ +#define MEMTEST_INJ_STMT_PCICFG 0x2 /* address in PCI config space */ +#define MEMTEST_INJ_STMT_INT 0x3 /* a specific interrupt to be raised */ +#define MEMTEST_INJ_STMT_POLL 0x4 /* tell CPU module to poll for CEs */ + +/* Must be kept in sync with mtst_inj_statement in mtst_cpumod_api.h */ +typedef struct memtest_inj_stmt { + int mis_cpuid; /* target CPU for statement */ + uint_t mis_type; /* MEMTEST_INJ_STMT_* */ + union { + struct { /* MEMTEST_INJ_STMT_MSR */ + uint32_t _mis_msrnum; /* MSR number */ + uint32_t _mis_pad; /* reserved */ + uint64_t _mis_msrval; /* value for MSR */ + } _mis_msr; + struct { /* MEMTEST_INJ_STMT_PCICFG */ + uint32_t _mis_pciaddr; /* address in config space */ + uint32_t _mis_pcival; /* value for PCI config reg */ + } _mis_pci; + uint8_t _mis_int; /* MEMTEST_INJ_STMT_INT; int num */ + } _mis_data; +} memtest_inj_stmt_t; + +#define mis_msrnum _mis_data._mis_msr._mis_msrnum +#define mis_msrval _mis_data._mis_msr._mis_msrval +#define mis_pciaddr _mis_data._mis_pci._mis_pciaddr +#define mis_pcival _mis_data._mis_pci._mis_pcival +#define mis_int _mis_data._mis_int + +typedef struct memtest_inject { + int mi_nstmts; + uint32_t mi_pad; + memtest_inj_stmt_t mi_stmts[1]; +} memtest_inject_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _MEMTEST_H */ diff --git a/usr/src/uts/intel/sys/x86_archext.h b/usr/src/uts/intel/sys/x86_archext.h index 4b93663dbd..2f52d6ee4b 100644 --- a/usr/src/uts/intel/sys/x86_archext.h +++ b/usr/src/uts/intel/sys/x86_archext.h @@ -262,54 +262,7 @@ extern "C" { #define MSR_PRP4_LBSTK_TO_14 0x6ce #define MSR_PRP4_LBSTK_TO_15 0x6cf -#define REG_MCG_CAP 0x179 -#define REG_MCG_STATUS 0x17a -#define REG_MCG_CTL 0x17b - -#define REG_MC0_CTL 0x400 -#define REG_MC0_STATUS 0x401 -#define REG_MC0_ADDR 0x402 -#define REG_MC0_MISC 0x403 -#define REG_MC1_CTL 0x404 -#define REG_MC1_STATUS 0x405 -#define REG_MC1_ADDR 0x406 -#define REG_MC1_MISC 0x407 -#define REG_MC2_CTL 0x408 -#define REG_MC2_STATUS 0x409 -#define REG_MC2_ADDR 0x40a -#define REG_MC2_MISC 0x40b -#define REG_MC4_CTL 0x40c -#define REG_MC4_STATUS 0x40d -#define REG_MC4_ADDR 0x40e -#define REG_MC4_MISC 0x40f -#define REG_MC3_CTL 0x410 -#define REG_MC3_STATUS 0x411 -#define REG_MC3_ADDR 0x412 -#define REG_MC3_MISC 0x413 - -#define P6_MCG_CAP_COUNT 5 -#define MCG_CAP_COUNT_MASK 0xff -#define MCG_CAP_CTL_P 0x100 - -#define MCG_STATUS_RIPV 0x01 -#define MCG_STATUS_EIPV 0x02 -#define MCG_STATUS_MCIP 0x04 - -#define MCG_CTL_VALUE 0xffffffff - #define MCI_CTL_VALUE 0xffffffff -#define MCI_STATUS_ERRCODE 0xffff -#define MCI_STATUS_MSERRCODE 0xffff0000 -#define MCI_STATUS_PCC ((long long)0x200000000000000) -#define MCI_STATUS_ADDRV ((long long)0x400000000000000) -#define MCI_STATUS_MISCV ((long long)0x800000000000000) -#define MCI_STATUS_EN ((long long)0x1000000000000000) -#define MCI_STATUS_UC ((long long)0x2000000000000000) -#define MCI_STATUS_O ((long long)0x4000000000000000) -#define MCI_STATUS_VAL ((long long)0x8000000000000000) - -#define MSERRCODE_SHFT 16 - #define MTRRTYPE_MASK 0xff @@ -437,6 +390,8 @@ typedef struct mtrrvar { #define X86_VENDOR_TM 9 /* GenuineTMx86 */ #define X86_VENDOR_NSC 10 /* Geode by NSC */ +#define X86_VENDOR_STRLEN 13 /* vendor string max len + \0 */ + #if !defined(_ASM) #if defined(_KERNEL) || defined(_KMEMUSER) @@ -471,8 +426,6 @@ struct cpuid_regs { extern uint64_t rdmsr(uint_t); extern void wrmsr(uint_t, const uint64_t); extern void invalidate_cache(void); -struct regs; -extern int mca_exception(struct regs *); extern ulong_t getcr4(void); extern void setcr4(ulong_t); extern void mtrr_sync(void); diff --git a/usr/src/uts/sparc/sys/Makefile b/usr/src/uts/sparc/sys/Makefile index 88221c9101..72c122f969 100644 --- a/usr/src/uts/sparc/sys/Makefile +++ b/usr/src/uts/sparc/sys/Makefile @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -59,19 +59,37 @@ HDRS= \ vmparam.h \ sysconfig_impl.h -FPUHDRS= fpu_simulator.h fpusystm.h globals.h ieee.h +FPUHDRS= \ + fpu_simulator.h \ + fpusystm.h \ + globals.h \ + ieee.h + +FMCPUHDRS= \ + UltraSPARC-II.h \ + UltraSPARC-III.h \ + UltraSPARC-T1.h ROOTDIR= $(ROOT)/usr/include/sys -ROOTDIRS= $(ROOTDIR) $(ROOTDIR)/fpu +ROOTDIRS= \ + $(ROOTDIR) \ + $(ROOTDIR)/fm/cpu \ + $(ROOTDIR)/fpu ROOTHDRS= $(HDRS:%=$(ROOTDIR)/%) ROOTFPUHDRS= $(FPUHDRS:%=$(ROOTDIR)/fpu/%) +ROOTFMCPUHDRS= $(FMCPUHDRS:%=$(ROOTDIR)/fm/cpu/%) fpu/%.check: fpu/%.h $(DOT_H_CHECK) -CHECKHDRS= $(HDRS:%.h=%.check) \ - $(FPUHDRS:%.h=fpu/%.check) +fm/cpu/%.check: fm/cpu/%.h + $(DOT_H_CHECK) + +CHECKHDRS= \ + $(HDRS:%.h=%.check) \ + $(FPUHDRS:%.h=fpu/%.check) \ + $(FMCPUHDRS:%.h=fm/cpu/%.check) # install rules $(ROOTDIR)/%: % @@ -80,11 +98,14 @@ $(ROOTDIR)/%: % $(ROOTDIR)/fpu/%: fpu/% $(INS.file) +$(ROOTDIR)/fm/cpu/%: fm/cpu/% + $(INS.file) + .KEEP_STATE: -.PARALLEL: $(CHECKHDRS) $(ROOTHDRS) $(ROOTFPUHDRS) +.PARALLEL: $(CHECKHDRS) $(ROOTHDRS) $(ROOTFPUHDRS) $(ROOTFMCPUHDRS) -install_h: $(ROOTDIRS) .WAIT $(ROOTHDRS) $(ROOTFPUHDRS) +install_h: $(ROOTDIRS) .WAIT $(ROOTHDRS) $(ROOTFPUHDRS) $(ROOTFMCPUHDRS) $(ROOTDIRS): $(INS.dir) diff --git a/usr/src/uts/common/sys/fm/cpu/UltraSPARC-II.h b/usr/src/uts/sparc/sys/fm/cpu/UltraSPARC-II.h index c6013c9515..c6013c9515 100644 --- a/usr/src/uts/common/sys/fm/cpu/UltraSPARC-II.h +++ b/usr/src/uts/sparc/sys/fm/cpu/UltraSPARC-II.h diff --git a/usr/src/uts/common/sys/fm/cpu/UltraSPARC-III.h b/usr/src/uts/sparc/sys/fm/cpu/UltraSPARC-III.h index 6422868396..6422868396 100644 --- a/usr/src/uts/common/sys/fm/cpu/UltraSPARC-III.h +++ b/usr/src/uts/sparc/sys/fm/cpu/UltraSPARC-III.h diff --git a/usr/src/uts/common/sys/fm/cpu/UltraSPARC-T1.h b/usr/src/uts/sparc/sys/fm/cpu/UltraSPARC-T1.h index e6ffdfe922..e6ffdfe922 100644 --- a/usr/src/uts/common/sys/fm/cpu/UltraSPARC-T1.h +++ b/usr/src/uts/sparc/sys/fm/cpu/UltraSPARC-T1.h diff --git a/usr/src/uts/sun4u/cpu/us3_common.c b/usr/src/uts/sun4u/cpu/us3_common.c index f503c84984..6d1a99fa7b 100644 --- a/usr/src/uts/sun4u/cpu/us3_common.c +++ b/usr/src/uts/sun4u/cpu/us3_common.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -6400,13 +6400,14 @@ void cpu_ereport_post(struct async_flt *aflt) { char *cpu_type, buf[FM_MAX_CLASS]; + char sbuf[21]; /* sizeof (UINT64_MAX) + '\0' */ nv_alloc_t *nva = NULL; nvlist_t *ereport, *detector, *resource; errorq_elem_t *eqep; ch_async_flt_t *ch_flt = (ch_async_flt_t *)aflt; char unum[UNUM_NAMLEN]; int len = 0; - uint8_t msg_type; + uint8_t msg_type, mask; plat_ecc_ch_async_flt_t plat_ecc_ch_flt; if (aflt->flt_panic || panicstr) { @@ -6447,9 +6448,11 @@ cpu_ereport_post(struct async_flt *aflt) cpu_type = FM_EREPORT_CPU_UNSUPPORTED; break; } + mask = cpunodes[aflt->flt_inst].version; + (void) snprintf(sbuf, sizeof (sbuf), "%llX", + (u_longlong_t)cpunodes[aflt->flt_inst].device_id); (void) fm_fmri_cpu_set(detector, FM_CPU_SCHEME_VERSION, NULL, - aflt->flt_inst, (uint8_t)cpunodes[aflt->flt_inst].version, - cpunodes[aflt->flt_inst].device_id); + aflt->flt_inst, &mask, (const char *)sbuf); /* * Encode all the common data into the ereport. |